MyBatis是一个半ORM框架,作用在持久(DAO)层,是对JDBC的一种封装。它支持动态SQL、多级缓存以及高级映射,免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作,通过简单的 XML 或注解来配置和映射原始类型、接口和Pojo类,将数据库中的一条记录对应着一个Pojo类。
MyBatis的前身是iBATIS,iBATIS于2002年由ClintonBegin创建。MyBatis3是iBATIS的全新设计,支持注解和Mapper(映射)。
MyBatis的结构主要包括:
- 配置文件(mybatis-config.xml):配置MyBatis的数据库环境、映射文件路径等。
- 数据库环境:可以配置多个环境。有一个默认的调用环境。在调用的时候也可以选择调用具体哪个环境。开发时主要包括5种环境:DEV(开发)、TEST、QA(质量评估)、UAT(用户验收)、PRODUCTION(生产)。每个环境可以配置一个数据源(dataSource),其type属性包括UNPOOLED(非池化)、POOLED(池化)、JNDI(从应用服务器向配置好的JNDI数据源dataSource获取数据库连接,生产环境常用);还可以配置事务管理器(transactionManager),JDBC为开发者管理,MANAGED为配置了管理器。
- 映射文件(XXXMapping.xml):配置SQL语句映射到哪一个(映射)接口,以及接口的哪一个方法。
- 映射接口(XXXMapping.java):通过调用映射接口的方法,执行方法对应的SQL语句。
MyBatis的主要步骤:
解析XML配置文件成一个对象,确定调用的数据源,获取数据库链接,通过映射执行SQL语句。这里的映射主要是MyBatis根据接口和映射文件的关系进行反向代理,自动生成接口的实现类。映射关系主要包括以下几个:
- 接口(全限定名)——mapper元素namespace属性
- 接口方法名——update、insert、delete、select等元素的id属性
- 方法参数类型(如pojo类)(全限定名)——update、insert、delete、select等元素parameterType属性
- 参数对象(pojo类)的值或属性——SQL语句中的占位符(#{})
MyBatis的几个问题:
- 当参数对象(pojo类)的属性也是一个pojo类时,例如Teacher表中拥有一个Telephone对象,MyBatis并不知道该怎样来处理这个类型的对象。MyBatis提供了抽象类BaseTypeHandler
,可以继承此类创建自定义类型处理器。 - 参数注入的两种方式#{}和${}:#{}会为传入的参数添加引号,能避免SQL注入,但只能用于传入记录的值;${}则不会添加引号,因此可以用于传入表名、字段名等,不能避免SQL注入。
DML操作
1、mybatis.properties
2、mybatis-config.xml ```xml <?xml version=”1.0” encoding=”utf-8”?> <!DOCTYPE configuration PUBLIC “-//mybatis.org//DTD Config3.0//EN” “http://mybatis.org/dtd/mybatis-3-config.dtd">driver=com.mysql.jdbc.Driver
url=jdbc:mysql://192.168.110.128:3306/library
username=root
password=root
<typeAliases><!-- 为该包下的所有pojo类起别名(为类名) -->
<package name="com.briup.mybatis.bean"/>
<environments default="develop">
<environment id="develop">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/briup/mybatis/test/StudentMapper.xml"/>
</mappers>
3、StudentMapper.xml
```xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.briup.mybatis.test.StudentMapper">
<update id="createTable">
create table student (
id bigint primary key,
score double,
name varchar(20),
birthday datetime
)
</update>
<insert id="insertData" parameterType="com.briup.mybatis.bean.Student">
insert into student values(#{id}, #{score}, #{name}, #{birthday})
</insert>
<insert id="insertDataAutoId" parameterType="com.briup.mybatis.bean.Student">
insert into student(score, name, birthday) values(#{score}, #{name}, #{birthday})
<selectKey order="AFTER" keyColumn="id" keyProperty="id" resultType="java.lang.Long">
SELECT LAST_INSERT_ID()
</selectKey>
</insert>
<update id="updateData" parameterType="com.briup.mybatis.bean.Student">
update student set score = #{score}, name = #{name}, birthday = #{birthday} where id = #{id}
</update>
<delete id="deleteData" parameterType="java.lang.Long">
<!-- 基本数据类型直接写类型名 -->
delete from student where id = #{id}
</delete>
</mapper>
4、StudentMapper.java
package com.briup.mybatis.test;
import com.briup.mybatis.bean.Student;
public interface StudentMapper {
void insertData(Student stu);
void insertDataAutoId(Student stu);
void updateData(Student stu);
void deleteData(Long id);
}
5、执行代码
public class MybatisTest {
@Test
public void testCreateTable() {
try {
InputStream stream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(stream);
SqlSession sqlSession = sqlSessionFactory.openSession();
sqlSession.update("linja.createTable");
sqlSession.close();
System.out.println("执行终了");
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void testInsert() {
try {
InputStream stream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(stream);
SqlSession sqlSession = sqlSessionFactory.openSession();
Student student = new Student(2L, 25.5, "3344", new Date(123456666));
sqlSession.insert("linja.insertData", student);
sqlSession.commit();
sqlSession.close();
System.out.println("执行终了");
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void testInsertByInterface() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
Student student = new Student(null, 25.5, "3344", new Date(723456666));
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
mapper.insertDataAutoId(student);
sqlSession.commit();
sqlSession.close();
System.out.println(student);
System.out.println("执行终了");
}
}
DQL操作
DQL按查询方式可分为单表查询、多表查询;多表查询又分一对一、一对多和多对多查询。而查询的方法又分为不传参数和传参数的、返回pojo对象或集合的。此外还有查询的列名和pojo对象属性名不一致的问题;动态SQL的问题等。针对以上问题,MyBatis的映射文件提供了一系列的元素供使用:(为了方便,以下注释全部使用Java注释)
<select id="findStudentById" parameterType="int" resultType="Student">
SELECT STUD_ID, NAME, EMAIL, PHONE
FROM STUDENTS
WHERE STUD_ID=#{id}
</select> // 最简单的一种查询
// 如果方法返回类型为List、Set、SortedSet,就会封装成这些集合后返回,resultType是这些集合的泛型
// 如果方法返回为Map,resultType就要填写Map,会将key值设置为列名,value值设置为数据
// 如果列名和pojo对象属性名不一致,就要写一个resultMap,将它们对应起来
<resultMap id="StudentResult" type="com.briup.pojo.Student">
<id property="studId" column="stud_id" /> // 主键使用id标签
<result property="name" column="name" /> // 非主键使用result标签
<result property="email" column="email" />
<result property="phone" column="phone" />
</resultMap>
<select id="findStudentById" parameterType="int" resultMap="StudentResult">
SELECT * FROM STUDENTS WHERE STUD_ID=#{id}
</select>
// resultMap可以拓展
<resultMap type="Student" id="StudentResult">
<id property="stud Id" column="stud_id" />
<result property="name" column="name" />
<result property="email" column="email" />
<result property="phone" column="phone" />
</resultMap>
<resultMap type="Student" id="StudentWithAddressResult" extends="StudentResult">
<result property="address.addrId" column="addr_id" />
<result property="address.street" column="street" />
<result property="address.city" column="city" />
<result property="address.state" column="state" />
<result property="address.zip" column="zip" />
<result property="address.country" column="country" />
</resultMap>
// 可以将需要反复查的语句提取到sql标签中
<sql id="querycolumns">
id,name,age,address
</sql>
<select id="findStudentById"parameterType="int" resultMap="Student">
Select <include refid="querycolumns"/> from students where stud_id = #{studId}
</select>
// 下面是一个地址的普通查询
<resultMap type="Address" id="AddressResult">
<id property="addrId" column="addr_id" />
<result property="street" column="street" />
<result property="city" column="city" />
<result property="state" column="state" />
<result property="zip" column="zip" />
<result property="country" column="country" />
</resultMap>
<select id="findAddressById" parameterType="int" resultMap="AddressResult">
select * from addresses
where addr_id=#{id}
</select>
// 下面是一个查询学生的一对一查询
<resultMap type="Student" id="StudentWithAddressResult">
<id property="studId" column="stud_id" />
<result property="name" column="name" />
<result property="email" column="email" />
<result property="dob" column="dob" />
<result property="phone" column="phone" />
// 在一对一查询中,查Student会查出addr_id,于是在查到的时候也要把addr_id对应的Address查出来并封装
// 使用association标签调用一个嵌套的多表查询
<association property="address" column="addr_id" select="findAddressById" />
</resultMap>
<select id="selectStudentWithAddress" parameterType="int" resultMap="StudentWithAddressResult">
select * from students
where stud_id=#{id}
</select>
// 一对多查询
<!-- 独立的Address封装映射 -->
<resultMap type="Address" id="AddressResult">
<id property="addrId" column="addr_id" />
<result property="street" column="street" />
<result property="city" column="city" />
<result property="state" column="state" />
<result property="zip" column="zip" />
<result property="country" column="country" />
</resultMap>
<!-- 独立的Course封装映射 -->
<resultMap type="Course" id="CourseResult">
<id column="course_id" property="courseId" />
<result column="name" property="name" />
<result column="description" property="description" />
<result column="start_date" property="startDate" />
<result column="end_date" property="endDate" />
</resultMap>
<resultMap type="Tutor" id="TutorResult">
<id column="tutor_id" property="tutorId" />
<result column="tutor_name" property="name" />
<result column="email" property="email" />
<result column="phone" property="phone" />
<!-- 把addr_id列的值当做参数传给findAddressById进行查询 -->
<association property="address" column="addr_id"
select="findAddressById"></association>
<!-- 把tutor_id列的值当做参数传给findCoursesByTutor进行查询 -->
<collection property="courses" column="tutor_id"
select="findCoursesByTutor" />
</resultMap>
<!-- 单独的Tutor查询语句 -->
<select id="findTutorById" parameterType="int"
resultMap="TutorResult">
select *
from tutors
where tutor_id=#{tutor_id}
</select>
<!-- 单独的Address查询语句 -->
<select id="findAddressById" parameterType="int"
resultMap="AddressResult">
select *
from addresses
where addr_id = #{addr_id}
</select>
<!-- 单独的Course查询语句 -->
<select id="findCoursesByTutor" parameterType="int"
resultMap="CourseResult">
select *
from courses
where tutor_id=#{tutor_id}
</select>
几个重要属性:
- parameterType:用于select、insert等标签,表示输入参数的类型。
- resultType:用于select、selectKey标签,表示产生的对象的类型,或产生集合的范型。如果多表查询产生多个对象,则不用resultType而用resultMap。
- type:用于resultMap标签,表示列名和属性名对应的pojo类。
- javaType:用于resultMap.association标签(即一对一查询),表示property变量的类型。
- ofType:用于resultMap.collection标签(即一对多查询和多对多查询),表示property变量集合的泛型。
- jdbcType:association标签和collection标签都有,表示数据在数据库中的类型,一般不写。
MyBatis的多表查询无外乎两种方式:不是嵌套result,就是嵌套resultMap。