# MyBatis-10:高级特性
# 导航
# MyBatis日志管理
logback要比log4j性能要好
# 引入logback
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
1
2
3
4
5
2
3
4
5
# 配置logback
在resources目录下新建logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%thread] %d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!--
日志输出级别(优先级高到底):
error:错误 - 系统的故障日志
warn: 警告 - 存在风险或使用不当的日志
info: 一般性日志
debug: 程序内部用于调试信息
trace:程序运行的跟踪信息
一般开发环境,日志级别为:debug
生产环境,为:info
-->
<root level="debug">
<appender-ref ref="console"/>
</root>
</configuration>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%thread] %d{HH:mm:ss.SSS} %-5level %logger{10} - %msg%n</pattern>
</encoder>
</appender>
<!--生产按天记录日志,一天一个日志文件-->
<appender name="accessHisoryLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>d:/logs/history.%d.log</fileNamePattern>
</rollingPolicy>
<encoder>
<pattern>[%thread] %d{HH:mm:ss.SSS} %-5level %logger{10} - %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="console"/>
</root>
<logger name="com.torey.interception.interceptor.AccessHistoryInterceptor" leval="INFO" additivity="false">
<appender-ref ref="accessHisoryLog"/>
</logger>
</configuration>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 测试logback
再次执行testSelectAll方法,打印日志如下:
[main] 20:53:58.271 DEBUG o.a.i.d.pooled.PooledDataSource - Created connection 168907708.
[main] 20:53:58.271 DEBUG o.a.i.t.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@a1153bc]
[main] 20:53:58.274 DEBUG goods.selectAll - ==> Preparing: SELECT * FROM t_goods ORDER BY goods_id limit 10
[main] 20:53:58.335 DEBUG goods.selectAll - ==> Parameters:
[main] 20:53:58.506 DEBUG goods.selectAll - <== Total: 1
1
2
3
4
5
6
7
2
3
4
5
6
7
# 动态SQL
动态SQL是指根据参数数据动态组织SQL的技术
where 1=1
<if test="postId">
and u.post_id=#{postId}
</if>
<if test="deptId">
and u.dept_id=#{deptId}
</if>
<if test="userId">
and d.user_id=#{userId}
</if>
<if test="addOrReduce">
and d.add_or_reduce=#{addOrReduce}
</if>
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# MyBatis二级缓存
- 一级缓存默认开启,缓存范围SqlSession会话
- 二级缓存手动开启,属于范围Mapper Namespace
一级缓存使用率不高
# 二级缓存运行规则
- 二级开启后默认所有查询操作均使用缓存
- 写操作commit提交时对该namespace缓存强制清空
- 配置useCache=false可以不用缓存
- 配置flushCache=true代表强制清空缓存
# 一级缓存测试
/**
* 测试一级缓存
* @throws Exception
*/
@Test
public void testLv1Cache() throws Exception {
SqlSession sqlSession=null;
try{
sqlSession = MyBatisUtils.openSession();
TGoodsEntity tGoodsEntity = sqlSession.selectOne("goods.selectById",743);
TGoodsEntity tGoodsEntity2 = sqlSession.selectOne("goods.selectById",743);
System.out.println("========");
//hashCode相等,证明是同一个对象
System.out.println(tGoodsEntity.hashCode());
System.out.println(tGoodsEntity2.hashCode());
System.out.println("========");
}catch (Exception ex){
ex.printStackTrace();
throw ex;
}finally {
if (null!=sqlSession) {
MyBatisUtils.closeSession(sqlSession);
}
}
//不属于通一个sql session,所以会再次查询
try{
sqlSession = MyBatisUtils.openSession();
TGoodsEntity tGoodsEntity = sqlSession.selectOne("goods.selectById",743);
//commit提交时对该namespace缓存强制清空
sqlSession.commit();
TGoodsEntity tGoodsEntity2 = sqlSession.selectOne("goods.selectById",743);
System.out.println("========");
//hashCode相等,证明是同一个对象
System.out.println(tGoodsEntity.hashCode());
System.out.println(tGoodsEntity2.hashCode());
System.out.println("========");
}catch (Exception ex){
ex.printStackTrace();
throw ex;
}finally {
if (null!=sqlSession) {
MyBatisUtils.closeSession(sqlSession);
}
}
}
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
45
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
45
# 开启二级缓存
合理使用二级缓存,对程序速度提升有很大的提升
<cache eviction="LRU" flushInterval="600000" size="512" readOnly="true" />
1
所有代码如下:
<?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="goods">
<!--开启了二级缓存-->
<cache eviction="LRU" flushInterval="600000" size="512" readOnly="true" />
<select id="selectById"
resultType="com.torey.mybatis.entity.TGoodsEntity">
SELECT * FROM t_goods WHERE goods_id=#{value}
</select>
<select id="selectByTitle"
resultType="com.torey.mybatis.entity.TGoodsEntity">
SELECT * FROM t_goods WHERE title=#{title}
</select>
</mapper>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 测试二级缓存
* @throws Exception
*/
@Test
public void testLv2Cache() throws Exception {
SqlSession sqlSession=null;
try{
sqlSession = MyBatisUtils.openSession();
TGoodsEntity tGoodsEntity = sqlSession.selectOne("goods.selectById",743);
//hashCode相等,证明是同一个对象
System.out.println(tGoodsEntity.hashCode());
}catch (Exception ex){
ex.printStackTrace();
throw ex;
}finally {
if (null!=sqlSession) {
MyBatisUtils.closeSession(sqlSession);
}
}
//不属于通一个sql session,所以会再次查询
try{
sqlSession = MyBatisUtils.openSession();
TGoodsEntity tGoodsEntity = sqlSession.selectOne("goods.selectById",743);
System.out.println(tGoodsEntity.hashCode());
}catch (Exception ex){
ex.printStackTrace();
throw ex;
}finally {
if (null!=sqlSession) {
MyBatisUtils.closeSession(sqlSession);
}
}
}
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
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
打印日志如下:
[main] 18:49:06.638 DEBUG o.a.i.t.jdbc.JdbcTransaction - Opening JDBC Connection
[main] 18:49:07.528 DEBUG o.a.i.d.pooled.PooledDataSource - Created connection 2009221452.
[main] 18:49:07.529 DEBUG o.a.i.t.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@77c2494c]
[main] 18:49:07.545 DEBUG goods.selectById - ==> Preparing: SELECT * FROM t_goods WHERE goods_id=?
[main] 18:49:07.582 DEBUG goods.selectById - ==> Parameters: 743(Integer)
[main] 18:49:08.859 DEBUG goods.selectById - <== Total: 1
1977310713
[main] 18:49:08.860 DEBUG o.a.i.t.jdbc.JdbcTransaction - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@77c2494c]
[main] 18:49:08.865 DEBUG o.a.i.t.jdbc.JdbcTransaction - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@77c2494c]
[main] 18:49:08.865 DEBUG o.a.i.d.pooled.PooledDataSource - Returned connection 2009221452 to pool.
[main] 18:49:08.865 DEBUG goods - Cache Hit Ratio [goods]: 0.5
1977310713
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 二级缓存详情
# cache命令详解
<cache eviction="LRU" flushInterval="600000" size="512" readOnly="true" />
1
# eviction
是缓存的清除策略,当缓存对象数量达到上限后,自动触发对应算法对缓存对象清除,选项如下:
- LRU: 最近最久未使用:移除最长时间不被使用的对象(实际项目中就用这个,以下3个一般不使用)
- FIFO: 先进先出:按对象进入缓存的顺序来移除它们
- SOFT: 软引用:移除基于垃圾收集器状态和软引用规则的对象
- WEAK: 弱引用:更积极的移除基于垃圾收集器状态和弱引用规则的对象
# flushInterval
flushInterval 代表间隔多长时间自动清空缓存,单位毫秒,600000毫秒=10分钟,可以相对的设置长一些,比如 1小时
# size
size: 缓存存储的上限,用于保存对象或者集合(一个集合算一个对象)的数量上限
# readOnly
readOnly 设置为true:代表返回的是只读缓存,每次从缓存取出的是缓存对象本身,这种执行效率较高,一般设置为true
readOnly 设置为false: 代表每次取出的是缓存对象的“副本”,每一次取出的对象都是不同的,这种安全性较高
# 设置某个查询语句不被缓存
像一般的一个sql查询出多条语句的时候,就尽量不要缓存,因为命中率并不高,那么这时候设置:useCache="false",就不会被缓存
# 立即清理缓存
flushCache="true" ,sql语句执行完后强制清空缓存,既可以用在写操作上,也可以用到读操作,也就是select
<insert id="add" flushCache="true">
...
</insert>
1
2
3
2
3
<select id="selectByList" resultType="com.torey.mybatis.entity.TGoodsEntity" useCache="false">
select * from t_goods
</select>
1
2
3
2
3
# 导航,上一页,下一页
9MyBatis预防SQL注入攻击
12OneToMany对象关联查询