MyBatis

MyBatis本是Apache软件基金会的一个开源项目iBatis,2010年这个项目由apache software foundation迁移到了Google Code,并且改名为MyBatis。2013年11月迁移到Github。
MyBatis是一个优秀的基于Java的持久层框架,支持自定义SQL,存储过程和高级映射。
MyBatis对原有JDBC操作进行了封装,几乎消除了所有JDBC代码,使开发者只需关注SQL本身。
MyBatis可以使用简单的XML或Annotation来配置执行SQL,并自动完成ORM操作,将执行结果返回。

一、引言

1.1 什么是框架

软件的半成品,解决了软件开发过程当中的普适性问题,从而简化了开发步骤提供了开发的效率

1.2 什么是ORM框架?

  • ORM(Object Relational Mapping)对象关系映射,将程序中的一个对象与表中的一行数据一一对应
  • ORM框架提供了持久化类与表的映射关系,在运行时参照映射文件的信息,把对象持久化到数据库中

1.3 使用JDBC完成ORM操作的缺点

  • 存在大量的穴余代码。
  • 手工创建Connection、Statement等。
  • 手工将结果集封装成实体对象。
  • 查询效率低,没有对数据访问进行过优化(Not Cache)。

二、MyBatis框架

2.1 概念

  • MyBatis本是Apache软件基金会的一个开源项目iBatis,2010年这个项目由apache software foundation迁移到了Google Code,并且改名为MyBatis。2013年11月迁移到Github。
  • MyBatis是一个优秀的基于Java的持久层框架,支持自定义SQL,存储过程和高级映射。
  • MyBatis对原有JDBC操作进行了封装,几乎消除了所有JDBC代码,使开发者只需关注SQL本身。
  • MyBatis可以使用简单的XML或Annotation来配置执行SQL,并自动完成ORM操作,将执行结果返回。

2.2 官网与API

官网:https://mybatis.org/mybatis-3/zh/index.html

三、MyBatis环境搭建【重点】

3.1 导入依赖

导入依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!--MyBatis核心依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>

<!-- 连接mysql数据库 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>

3.2 创建MyBatis配置文件

创建并配置mybatis-config.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
<!--(properties?, settings?, typeAliases?,
typeHandlers?, objectFactory?, objectWrapperFactory?,
reflectorFactory?, plugins?, environments?,
databaseIdProvider?, mappers?)-->
<!-- 导入外部的参数 -->
<properties resource="jdbc.properties"/>
<!-- 注意书写位置 -->
<settings>
<setting name="cacheEnabled" value="true"/> <!-- mybaits-config.xml中开启全局缓存(默认开启) -->
</settings>
<!-- 实体类别名 -->
<typeAliases>
<!--<typeAlias type="com.techoc.entity.User" alias="user_shine"/>-->
<!-- 定义实体类所在的package,每个实体类都会自动注册一个别名=类名 -->
<package name="com.techoc.entity"/>
</typeAliases>

<!-- 核心配置信息 -->
<environments default="shine_config">
<!-- 数据库相关配置 -->
<environment id="shine_config">
<!-- 事务控制类型 -->
<transactionManager type="jdbc"></transactionManager>
<!-- 数据连接参数 -->
<dataSource type="com.techoc.datasource.MyDruidDataSourceFactory">
<property name="driverClass" value="${jdbc.driver}"/>
<!-- &转义&amp; -->
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>

<!-- 注册mapper文件 -->
<mappers>
<mapper resource="com/techoc/dao/UserDAOMapper.xml"/>
</mappers>
</configuration>

注意:mapper.xml默认建议存放在resources中,路径不能以/开头

四、MyBatis开发步骤【重点】

4.1 建表

1
2
3
4
5
6
7
8
create table t_users(
id int primary key auto_increment,
name varchar(50),
password varchar(50),
sex varchar(1),
birthday datetime,
registTime datetime
)default charset = utf8;

4.2 定义实体类

定义所需CURD操作的实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class User {
private Integer id;
private String username;
private String password;
private String sex;
private Date birthday;
private Date registTime;

// 无参构造(必备构造二选一)
public User() {
}

// 全参构造(必备构造二选一)
public User(Integer id, String username, String password, String sex, Date birthday, Date registTime) {
this.id = id;
this.username = username;
this.password = password;
this.sex = sex;
this.birthday = birthday;
this.registTime = registTime;
}
//get and set
}

4.3 定义DAO接口

根据所需DAO定义接口、以及方法

1
2
3
public interface UserDAO {
User selectUserById(Integer id);
}

4.4 编写Mapper.xml

在resouces目录中创建UserDaoMapper.xml文件

1
2
3
4
5
6
7
8
9
10
11
12
<?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">

<!-- namespace=所需实现的接口全限定名 -->
<mapper namespace="com.techoc.dao.UserDAO">
<!-- id=所需重写的接口抽象方法,resultType=查询后所需返回的对象类型 -->
<select id="selectUserById" resultType="User">
<!--#{arg0}=方法的第一个形参-->
SELECT * FROM t_users WHERE id = #{arg0}
</select>
</mapper>

4.5 注册Mapper

Mapper.xml注册到mybatis-config.xml

1
2
3
4
<!-- 注册mapper文件 -->
<mappers>
<mapper resource="com/techoc/dao/UserDAOMapper.xml"/>
</mappers>

4.6 测试 MyBatis API

MyBatis的API操作方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void test1() throws IOException {

//1.获得读取MyBatis配置文件的流对象
Inputstream is = Resources.getResourceAsStream ("mybatis-config.xml");

//2.构建Sq1Session连接对象的工厂
Sq1SessionFactory factory = new SqlSessionFactoryBuilder().build(is);

//3.通过工厂获得连接对象
Sq1Session sq1Session = factory.openSession();

//4.通过连接对象获得接口实现类对象
UserDao userDao = sqlSession.getMapper(UserDao.class) ;

//5.调用接口中的方法
System.out.println(userDao.selectUserById(1));
}

五、细节补充

5.1 解决mapper.xml存放在resources以外路径中的读取问题

pom.xml文件最后追加<build>标签,以便可以将xml文件复制到classes中,并在程序运行时正确读取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<build>
<!-- 更改maven编译规则 -->
<resources>
<resource>
<!-- 资源目录 -->
<directory>src/main/java</directory>
<includes>
<include>*.xml</include><!-- 默认(新添加自定义则失效) -->
<include>**/*.xml</include><!-- 新添加 */代表1级目录 **/代表多级目录 -->
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>

5.2 类型别名

1
2
3
4
5
6
7
8
<configuration>
<!-- 实体类别名 -->
<typeAliases>
<!--<typeAlias type="com.techoc.entity.User" alias="user_shine"/>-->
<!-- 定义实体类所在的package,每个实体类都会自动注册一个别名=类名 -->
<package name="com.techoc.entity"/>
</typeAliases>
</configuration>

5.3 创建log4j配置文件

导入依赖

1
2
3
4
5
6
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>

创建并配置log4j.properties

1
2
3
4
5
6
7
8
# Global logging configuration
log4j.rootLogger=DEBUG,stdout
# MyBatis logging configuration...
log4j.logger.org.mybatis.example.BlogMapper=TRACE
# Console output...
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = %5p[%t] - %m%n
级别 描述
ALLLEVEL 打开所有日志记录开关;是最低等级的,用于打开所有日志记录。
DEBUG 输出调试信息;指出细粒度信息事件对调试应用程序是非常有帮助的。
INFO 输出提示信息;消息在粗粒度级别上突出强调应用程序的运行过程。
WARN 输出警告信息;表明会出现潜在错误的情形。
ERROR 输出错误信息;指出虽然发生错误事件,但仍然不影响系统的继续运行。
FATAL 输出致命错误;指出每个严重的错误事件将会导致应用程序的退出。
OFFLEVEL 关闭所有日志记录开关;是最高等级的,用于关闭所有日志记录。

六、MyBatis的CRUD操作【重点】

6.1 查询

标签:<select id="" resultType="">

6.1.1 序号参数绑定

1
2
3
4
public interface UserDao {
//使用原生参数绑定
public User selectUserByIdAndPwd(Integer id,String pwd);
}
1
2
3
4
5
6
7
8
<select id="selectUserByIdAndPwd"resultType="user">
SELECT*FROMt_users
WHERE id = #{arg0} AND password = #{arg1} <!--arg0 arg1 arg2 ...-->
</select>
<select id="selectUserByIdAndPwd" resultType="user">
SELECT*FROMt_users
WHERE id = #{param1} AND password = #{param2} <!--param1 param2 param3 ...-->
</select>

6.1.2 注解参数绑定【推荐】

1
2
3
4
public interface UserDao {
//使用MyBatis提供的@Param进行参数绑定
public User selectUserByIdAndPwd(@Param("id") Integer id, @Param("pwd") String pwd);
}
1
2
3
4
<select id="selectUserByIdAndPwd" resultType="user">
SELECT*FROM t_users
WHERE id = #{id} AND password =#{pwd} <!--使用注解值 @Param("pwd") -->
</select>

6.1.3 Map参数绑定

1
2
3
4
public interface UserDao {
//添加Map进行参数绑定
public User selectUserByIdAndPwd_map(Map values);
}
1
2
3
4
Map values = new HashMap();//测试类创建Map
values.put("myId",1);//自定义key,绑定参数
values.put("myPwd","123456");
ser user = userDao. selectUserByIdAndPwd_map(values);
1
2
3
4
<select id="selectUserByIdAndPwd_map" resultType="user">
SELECT*FROM t_users
WHERE id = #{myId} AND password = #{myPwd} <!--通过key获得value -->
</select>

6.1.4 对象参数绑定

1
2
3
4
public interface UserDao {
//使用对象属性进行参数绑定
public User selectUserByUserInfo(User user);
}
1
2
3
4
5
<select id="selectUserByUserInfo" resultType="user">
SELECT*FROM t_users
WHERE id = #{id} AND password = #{password}
<!--#{id}取User对象的id属性值、#{password)同理-->
</select>

6.1.5 模糊查询

1
2
3
public interface UserDao {
public List<User> selectUsersByKeyword(eParam("keyword") String keyword);
}
1
2
3
4
5
6
<mapper namespace="com.techoc.mybatis.part1.different.UserDao">
<select id="selectUsersByKeyword" resultType="user">
SELECT*FROM t_users
WHERE name LIKE concat('%',#{keyword},"%')<!--拼接'%'-->
</select>
</mapper>

6.2 删除

标签:<delete id=""parameterType="">

1
2
3
4
<delete id="deleteUser" parameterType="int">
DELETE FROM t_users
WHERE id=#{id}
</delete>

6.3 修改

标签:<update id=""parameterType="">

1
2
3
4
5
<update id="updateUser" parameterType="user">
UPDATE t_users SET name=#{name},password=#{password},sex=#{sex},birthday=#{birthday}
WHERE id=#{id}
<!--方法参数为对象时,可直接使用#{属性名}进行获取-->
</update>

6.4 添加

标签:<insert id=""parameterType="">

1
2
3
4
5
6
7
8
9
10
<!--手动主键-->
<insert id="insertUser" parameterType="user">
INSERT INTO t_users VALUES(#{id},#{name},#{password},#{sex},#(birthday},NULL);
</insert>
<!--自动主键-->
<insert id="insertUser" parameterType="user">
<!--自动增长主键,以下两种方案均可-->
INSERT INTO t_users VALUES(#{id},#{name},#{password},#{sex},#{birthday},NULL);
INSERT INTO t_users VALUES(NULL,#{name},#{password},#{sex},#{birthday},NULL);
</insert>

6.5 主键回填

标签:<selectKey id=""parameterType=""order="AFTER|BEFORE">

6.5.1 通过last_insert_id()查询主键

1
2
3
4
create table t_product(
id int primary key auto_increment,
name varchar(50)
)default charset = utf8;
1
2
3
4
5
class Product{
private Integer id;
private String name;
//set+get...
}
1
2
3
4
5
6
7
8
<mapper namespace="com.techoc.mybatis.part1.basic.ProductDao">
<insert id="insertProduct" parameterType="product">
<selectKey keyProperty="id" resultType="int" order="AFTER"> <!--插入之后-->
SELECT LAST_INSERT_ID()<!--适用于整数类型自增主键-->
</selectKey>
INSERT INTO t_product(id,name)VALUES(#{id},#{name})
</insert>
</mapper >

6.5.2通过uuid()查询主键

1
2
3
4
5
6
7
8
<mapper namespace="com.techoc.mybatis.part1.basic.OrderDao">
<insert id="insertOrder" parameterType="order">
<selectKey keyProperty="id" resultType="String" order="BEFORE"><!--插入之前-->
SELECT REPLACE(UUID(),'-','')<!--适用于字符类型主键-->
</selectKey>
INSERT INTO t_order(id,name)VALUES(#{id},#{name})
</insert>
</mapper>

七、ORM映射【重点】

7.1 MyBatis自动ORM失效

MyBatis只能自动维护库表”列名“与”属性名“相同时的一一对应关系,二者不同时,无法自动ORM。

自动ORM失效

7.2 方案一:列的别名

在SQL中使用as为查询字段添加列别名,以匹配属性名。

1
2
3
4
5
6
7
<mapper namespace="com.techoc.mybatis.part2.orm.ManagerDao">
<select id="selectManagerByIdAndPwd" resultType="com.techoc.mybatis.part2.orm.Manager">
SELECT mgr_id AS id,mgr_name AS username,mgr_pwd AS password
FROM t_managers
WHERE mgr_id=#{id} AND mgr_pwd=#{pwd}
</select>
</mapper>

7.3方案二:结果映射(ResultMap-查询结果的封装规则)

通过<resultMap id=""type="">映射,匹配列名与属性名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<mapper namespace="com.techoc.mybatis.part2.orm.ManagerDao">
<!--定义resultMap标签-->
<resultMap id="managerResultMap" type="com.techoc.mybatis.part2.orm.Manager">
<!--关联主键与列名-->
<id property="id" column="mgr_id" />

<!--关联属性与列名-->
<result property="username" column="mgr_name" />
<result property="password" column="mgr_pwd" />
</resultMap>

<!--使用resultMap作为ORM映射依据-->
<select id="selectA11Managers" resultMap="managerResultMap">
SELECT mgr_id,mgr_name,mgr_pwd
FROM t_managers
</select>
</mapper>

八、MyBatis处理关联关系-多表连接【重点】

实体间的关系:关联关系(拥有has、属于belong)

  • OneToOne:一对一关系(Passenger—Passport)
  • OneToMany:一对多关系(Employee-Department)
  • ManyToMany:多对多关系(Student—Subject)

Table建立外键关系

Entity添加关系属性

Mapper中将属性与列名对应

8.1 一对一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<mapper namespace="com.techoc.mybatis.part2.one2one.PassengerDao">
<!--结果映射(查询结果的封装规则)-->
<resultMap id="passengerResultMap" type="com.techoc.mybatis.part2.one2one.Passenger">
<id property="id" column="id"/>
<result property="name" column="name" />
<result property="sex" column="sex" />
<result property="birthday" column="birthday" />

<!--关系表中数据的封装规则------指定关系表的实体类型-->
<association property="passport"
javaType="com.techoc.mybatis.part2.one2one.Passport">
<id property="id" column="passport_id" />
<result property="nationality" column="nationality" />
<result property="expire" column="expire" />
<result property="passenger_id" column="passenger_id" />
</association>
</resultMap>

<!--多表连接查询-->
<!--结果映射(查询结果的封装规则)-->
<select id="selectPassengerById" resultMap="passengerResultMap">
<!--别名(避免与p1.id冲突)-->
SELECT p1.id,p1.name,p1.sex,p1.birthday,p2.id as passport_id,p2.nationality,p2.expire,
p2.passenger_id
FROM t_passengers p1 LEFT JOIN t_passports p2
ON p1.id=p2.passenger_id
WHERE p1.id = #{id}
</select>
</mapper>

注意:指定“一方”关系时(对象),使用<association javaType="">

8.2 一对多

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<mapper namespace="com.techoc.mybatis.part2.one2many.DepartmentDao">
<!--封装规则-->
<resultMap id="departmentResultMap"type="com.techoc.mybatis.part2.one2many.Department">
<id property="id" column="id" />
<result property="name"column="name"/>
<result property="location" column="location" />
<!--关系表中数据的封装规则------指定关系表的实体类型-->
<collection property="emps" ofType="com.techoc.mybatis.part2.one2many.Employee">
<id property="id" column="emp_id" />
<result property="name"column="emp_name"/>
<result property="salary" column="salary" />
<result property="dept_id" column="dept_id" />
</collection>
</resultMap>
<!--多表连接查询-->
<!--封装规则-->
<select id="selectDepartmentById" resultMap="departmentResultMap">
<!--别名(避免与d.id、d.name冲突)-->
SELECT d.id , d.name , d.location , e.id AS emp_id , e.name emp_name , e.salary, e.dept_id
FROM t_departments d LEFT JOIN t_employees e
ON d.id=e.dept_id
WHERE d.id=#{id}
</select>
</mapper>

注意:指定“多方”关系时(集合),使用<collection ofType="">

8.3 多对多

建立三张关系表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<mapper namespace="com.techoc.mybatis.part2.many2many.StudentDao">

<!--映射查询只封装两表中的信息,可忽略关系表内容-->
<resultMap id="a11Map" type="com.techoc.mybatis.part2.many2many.Student">
<id property="id" column="id" />
<result property="name" column="name" />
<result property="sex" column="sex" />
<collection property="subjects" ofType="com.techoc.mybatis.part2.many2many.Subject">
<id property="id" column="sid" />
<result property="name"column="sname"/>
<result property="grade" column="grade"/>
</collection>
</resultMap>

<!--三表连接查询-->
<select id="selectA11Students" resultMap="a11Map">
SELECT s1.*,ss.*,s2.id as sid,s2.name as sname,s2.grade
FROM t_students s1 LEFT JOIN t_stu_sub ss
ON s1.id=ss.student_id<!--通过t_stu_sub表建立二者之间的关系-->
LEFT JOIN t_subjects s2
ON ss.subject_id=s2.id
</select>
</mapper>

注意:指定“多方”关系时(集合),使用<collection ofType="">

8.4 关系总结

一方,添加集合;多方,添加对象。

双方均可建立关系属性,建立关系属性后,对应的Mapper文件中需使用<ResultMap>完成多表映射。

持有对象关系属性,使用<association property="dept" javaType="department">

持有集合关系属性,使用<collection property="emps" ofType="employee">

九、动态SQL【重点】

MyBatis的映射文件中支持在基础SQL上添加一些逻辑操作,并动态拼接成完整的SQL之后再执行,以达到SQL复用、简化编程的效果。

9.1 < sql >

1
2
3
4
5
6
7
8
9
<mapper namespace="com.techoc.mybatis.part2.dynamic.BookDao">
<sql id="BOOKS_FIELD"><!--定义SQL片段-->
SELECT id, name, author, publish, sort
</sq1>
<select id="selectBookByCondition"resultType="com.techoc.mybatis.part2.dynamic.Book">
<include refid="BOOKS_FIELD"/><!--通过ID引用SQL片段-->
FROM t_books
</select>
</mapper>

9.2 < where >

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<select id="selectBookByCondition" resultType="com.techoc.mybatis.part2.dynamic.Book">
SELECT id , name , author , publish , sort
FROM t_books
<where>
<if test="id!=null"><!--WHERE,会自动忽略前后级(如:and|or)-->
id=#{id}
</if>
<if test="name != null">
and name=#{name}
</if>
<if test="author != null">
and author = #{author}
</if>
<if test="publish != null">
and publish = #(publish)
</if>
<if test="sort != null">
and sort = #{sort}
</if>
</where>
</select>

9.3 < set >

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<update id="updateBookByCondition">
UPDATE t_books
<set>
<if test="name!=null"><!--where子句中满足条件的if,会自动忽略后级(如:,)-->
name=#{name},
</if>
<if test="author != null">
author=#{author}
</if>
<if test="publish != null">
publish = #{publish}
</if>
<if test="sort != null">
sort=#{sort},
</if>
</set>
WHERE id = #{id}
</update>

9.4 < trim >

<trim prefix="" suffix="" prefixOverrides="" suffixOverrides="">代替<where><set>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<select id="selectBookByCondition" resultType="com.techoc.mybatis.day2.dynamic.Book">
SELECT id, name, author, publish, sort
FROM t_books
<trim prefix="WHERE prefixoverrides="AND|OR">
<!--增加WHERE前缀,自动忽略前缀-->
<if test="id != null">
and id = #{id}
</if>
<if test="name != null">
and name = #{name}
</if>
<if test="author != null">
and author = #{author}
</if>
<if test="publish != null">
and publish = #{publish}
</if>
<if test="sort != null">
and sort = #{sort}
</if>
</trim>
</select>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<update id="updateBookByCondition">
UPDATE t_books
<trim prefix="SET"suffixoverrides=","><!--增加SET前缀,自动忽略后缀-->
<if test="name != null">
name=#{name},
</if>
<if test="author != null">
author=#{author},
</if>
<if test="publish != null">
publish=#{publish},
</if>
<if test="sort != null">
sort=#{sort}
</if>
</trim>
WHERE id = #{id}
</update>

9.5 < foreach >

1
2
3
4
5
6
7
<delete id="deleteBookByIds">
DELETE FROM t_books
WHERE id IN
<foreach collection="list" open="(" separator="," close=")" item="id" index="1">
#{id}
</foreach>
</delete>
参数 描述 取值
collection 容器类型 list、 array、 map
open 起始符 (
close 结束符 )
separator 分隔符 ,
index 下标号 从0开始,依次递增
item 当前项 任意名称(循环中通过#{任意名称}表达式访问)

十、缓存(Cache)【重点】

内存中的一块存储空间,服务于某个应用程序,旨在将频繁读取的数据临时保存在内存中,便于二次快速访问。

无缓存时

有缓存时

10.1 一级缓存

SqlSession级别的缓存,同一个SqlSession的发起多次同构查询,会将数据保存在一级缓存中。

  • 注意:无需任何配置,默认开启一级缓存。

10.2 二级缓存

SqlSessionFactory级别的缓存,同一个SqlSessionFactory构建的SqlSession发起的多次同构查询,会将数据保存在二级缓存中。

  • 注意:在sqlSession.commit()或者sqlSession.close()之后生效。

10.2.1 开启全局缓存

<settings>是MyBatis中极为重要的调整设置,他们会改变MyBatis的运行行为,其他详细配置可参考官方文档。

1
2
3
4
5
6
7
8
<configuration>
<properties.../>
<!--注意书写位置-->
<settings>
<setting name="cacheEnabled"value="true"/>
<!--mybaits-config.xml中开启全局缓存(默认开启)-->
</settings>
</configuration>

10.2.2 指定Mapper缓存

1
2
3
4
5
6
<mapper namespace="com.techoc.mybatis.part2.cache.BookDao">
<cache/><!--指定缓存-->
<select id="selectBookByCondition" resultType="com.techoc.mybatis.part2.cache.Book">
SELECT*FROM t_books
</select>
</mapper>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Test
public void testMapperCache(){
Sq1Session sq1Session1 = MyBatisUtils. getSession();
BookDao bookDao1=sq1Session1.getMapper(BookDao.class);
bookDao1.selectBookByCondition(new Book());
sq1Session1.close();//必须关闭Sq1Session才可缓存数据

//-------------------------

Sq1Session sq1Session2=MyBatisUtils.getSession();
BookDao bookDao2=sq1Session2.getMapper(BookDao.class);
bookDao2.selectBookByCondition(new Book());
sq1Session2.close();//缓存击中
}

10.2.3 清空缓存并重新缓存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Test
public void testMapperCache(){
Sq1Session sq1Session1 = MyBatisUtils. getSession();
BookDao bookDao1=sq1Session1.getMapper(BookDao.class);
bookDao1.selectBookByCondition(new Book());
sq1Session1.close()://必须关闭Sq1Session才可缓存数据

//---------------------

Sq1Session sq1Session3=MyBatisUtils.getSession();
BookDao bookDao3=sq1Session3.getMapper(BookDao.class);
bookDao3.deleteBookById(102);
sq1Session3.commit();//DML成功,数据发生变化,缓存清空
sq1Session3.close();

//-------------------

Sq1Session sq1Session2=MyBatisUtils.getSession();
BookDao bookDao2=sq1Session2.getMapper(BookDao.class);
bookDao2.selectBookByCondition(new Book());
sq1Session2.close();//缓存未击中,重新查询数据库、重新缓存
}

十一、Druid连接池

Druid是阿里巴巴开源平台上的一个项目,整个项目由数据库连接池、插件框架和SQL解析器组成。该项目主要是为了扩展JDBC的一些限制,可以让程序员实现一些特殊的需求,比如向密钥服务请求凭证、统计SQL信息、SQL性能收集、SQL注入检查、SQL翻译等,程序员可以通过定制来实现自己需要的功能。

11.1 导入依赖

1
2
3
4
5
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>

11.2 创建DruidDataSourceFactory

创建MyDruidDataSourceFactory并继承PooledDataSourceFactory,并替换数据源。

1
2
3
4
5
public class MyDruidDataSourceFactory extends PooledDataSourceFactory {
public MyDruidDataSourceFactory(){
this.dataSource=new DruidDataSource()://替换数据源
}
}

11.3 修改mybatis-config.xml

mybatis-config.xml中连接池相关配置

1
2
3
4
5
6
<!--连接池-->
<dataSource type="com.techoc.mybatis.part2.utils.DruidDataSourceFactory"><!--数据源工厂-->
<property name="driverClass" value="${driver)"/>
<property name="username" value="${username)"/>
<property name="password" value="S{password)"/>
</dataSource>
  • 注意:<property name="属性名"/>属性名必须与com.alibaba.druid.pool.DruidAbstractDataSource中一致。

十二、 PageHelper分页工具

12.1 概念

PageHelper是适用于MyBatis框架的一个分页插件,使用方式极为便捷,支持任何复杂的单表、多表分页查询操作。

12.2 引入依赖

1
2
3
4
5
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.10</version>
</dependency>

12.3 配置mybatis-config.xml

1
2
3
4
<plugins>
<!-- com.github.pagehelper为PageHelper类所在包名 -->
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>

12.4 PageHelper应用方法

使用PageHelper提供的静态方法设置分页查询条件。

1
2
3
4
5
6
7
8
9
@Test
public void testPagehelper(){
UserDao userDao = MyBatisUtils.getMapper(UserDao.class);
PageHelper.startPage(1,2);//使用PageHelper设置分页条件
List<User>users=userDao.selectAllUsers();
for(User user:users){
System.out.println(user);
}
}

12.5 PageInfo对象

Pagelnfo对象中包含了分页操作中的所有相关数据。

12.5.1 Pagelnfo应用方式

使用Pagelnfo保存分页查询结果。

1
2
3
4
5
6
7
8
@Test
public void testPageInfo(){
UserDao userDao = MyBatisUtils. getMapper(UserDao. class);
PageHelper.startPage(6,2);
List<User>users=userDao.selectA11Users();
PageInfo<User>pageInfo=new PageInfo<User>(users);//将分页查询的结果集保存在PageInfo对象中
System.out.println(pageInfo);
}

12.6 注意事项

  • 只有在PageHelper.startPage()方法之后的第一个查询会有执行分页。
  • 分页插件不支持带有“for update”的查询语句。
  • 分页插件不支持“嵌套查询”,由于嵌套结果方式会导致结果集被折叠,所以无法保证分页结果数量正确。

十三、MyBatis注解操作

通过在接口中直接添加MyBatis注解,完成CRUD。

  • 注意:接口注解定义完毕后,需将接口全限定名注册到mybatis-config.xmt的<mappers>中。
  • 经验:注解模式属于硬编码到.java文件中,失去了使用配置文件外部修改的优势,可结合需求选用。
1
2
3
<mappers>
<mapper class="com.techoc.mybatis.part1.annotations.UserMapper"/><!--class="接口全限定名"-->
</mappers >

13.1 查询

1
2
3
4
public interface UserMapper {
@Select("SELECT*FROM t_users WHERE id=#{id)")
public User selectUserById(Integer id);
}

13.2 删除

1
2
@Delete(value = "DELETE FROM t_users WHERE id = #{id)")
public int deleteUser(Integer id);

13.3 修改

1
2
@Update("UPDATE t_users SET name=#{name},password=#{password},salary=#{salary},birthday=#{birthday} WHERE id = #(1d)")
public int updateUser(User user);

13.4 插入

1
2
3
4
5
6
@Insert("INSERT INTO t_users VALUES(#{id},#{name},#{password},#{salary},#(birthday},null)")
public int insertUser(User user);

@Options(useGeneratedKeys=true,keyProperty="id")//自增key,主键为id
@Insert("INSERT INTO t_users VALUES(#{id},#{name},#{password},#{salary},#{birthday},null)")
public int insertUserGeneratedKeys(User user);

13.5 $符号的应用场景

${attribute}属于字符串拼接SQL,而非预编译占位符,会有注入攻击问题,不建议在常规SQL中使用,常用于可解决动态生降序问题。

13.6 MyBatis处理关联关系-嵌套查询

思路:查询部门信息时,及联查询所属的员工信息。

  • DepartmentDao接口中定义selectDepartmentByld,并实现Mapper。
  • EmployeeDao接口中定义selectEmployeesByDeptld,并实现Mapper,
  • 当selectDepartmentByld被执行时,通过<collection>调用selectEmployeesByDeptld方法,并传入条件参数。

13.6.1 主表查询

定义selectEmployeesByDeptld,并书写Mapper,实现根据部门ID查询员工信息

1
2
3
4
5
6
7
8
public interface EmployeeDao {
/**
*根据部门编号查询员工信息
*@param did部门编号
*@return该部门中的所有员工
*/
public List<Employee> selectEmployeeByDeptId(@Param("did") String did);
}
1
2
3
4
5
6
<!--根据部门编号查询所有员工-->
<select id="selectEmployeeById" resultType="employee">
SELECT id, name, salary, dept_id
FROM t_employees
WHERE dept_id=#{did}
</select>

13.6.2 及联调用

定义selectDepartmentByld,并书写Mapper,实现根据部门ID查询部门信息,并及联查询该部门员工信息

1
2
3
4
5
6
7
8
public interface DepartmentDao {
/**
*查询部门信息
*@param id
*@return
*/
public Department selectDepartmentById(@Param("id") String id);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
<resultMap id="departmentResultMap" type="department">
<id property="id" column="id" />
<result property="name"column="name"/>
<result property="location" column="location"/>
<!--column="传入目标方法的条件参数" select="及联调用的查询目标"-->
<collection property="emps" ofType="Employee" column="id"
select="com.techoc.mybatis.part2.one2many.EmployeeDao.selectEmployeeByDeptId"/>
</resultMap>
<select id="selectAllDepartments" resultMap="departmentResultMap">
SELECT id , name , location
FROM t_departments
WHERE id = #{id}
</select>

13.7 延迟加载

mybatis-config.xml中开启延迟加载

1
2
3
<settings>
<setting name="lazyLoadingEnabled"value="true"/><!--开启延迟加载(默认false)-->
</settings>
  • 注意:开启延迟加载后,如果不使用及联数据,则不会触发及联查询操作,有利于加快查询速度、节省内存资源。

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!