MyBatis日志管理
什么是日志?
日志文件是用于记录系统操作事件的记录文件或文件集合
日志保存历史数据,是诊断问题以及理解系统活动的重要依据
SLF4j与Logback
日志门面:Simple Logging Facade For Java(SLF4j) 和 Apache Commons-Logging
桥接
日志实现: log4j、logback、java.util.logging
实现与门面分开可以很方便的切换底层实现的包,如log4j和logback互相切换,使用的日志门面Simple Logging Facade For Java不需要修改,由日志门面适配
目前工作中主流:logback组件
MyBatis底层就是通过Simple Logging Facade For Java(SLF4j) 支持 logback
配置日志
添加依赖包 pom.xml
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.6</version>
</dependency>
配置文件,不创建使用默认配置
src/main/resources/logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!--
日志级别:TRACE < DEBUG < INFO < WARN < ERROR
TRACE: 追踪信息,最详细的信息
DEBUG: 调试信息
INFO: 一般信息
WARN: 警告信息
ERROR: 错误信息
debug表示输出DEBUG及以上级别的日志信息
生产环境中,一般设置为INFO
大小写不敏感
-->
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
更多查看官方文档:https://logback.qos.ch/
MyBatis动态SQL
动态SQL是指根据参数数据动态组织SQL的技术
src/main/resources/mappers/goods.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="cn.chengaofeng.mapper.GoodsMapper">
<!--结果映射-->
<resultMap id="rmGoods" type="cn.chengaofeng.dto.GoodsDTO">
<!--设置字段与属性映射-->
<id property="goods.goodsId" column="goods_id"/>
<result property="goods.title" column="title"/>
<result property="goods.originalCost" column="original_cost"/>
<result property="goods.discount" column="discount"/>
<result property="goods.isFreeDelivery" column="is_free_delivery"/>
<result property="goods.categoryId" column="category_id"/>
<result property="category.categoryId" column="category_id"/>
<result property="category.categoryName" column="category_name"/>
<result property="category.parentId" column="parent_id"/>
<result property="category.categoryLevel" column="category_level"/>
<result property="category.categoryOrder" column="category_order"/>
<result property="test" column="test"/>
</resultMap>
<select id="selectGoodsDTO" resultMap="rmGoods">
select g.*, c.*, '1' as test from t_goods g, t_category c
where g.category_id = c.category_id
</select>
<select id="dynamicSQL" parameterType="cn.chengaofeng.entity.Goods" resultMap="rmGoods">
select g.*, c.*, '1' as test from t_goods g, t_category c
where g.category_id = c.category_id
<if test="goodsId != null">
and g.goods_id = #{goodsId}
</if>
<if test="title != null">
and g.title like concat('%', #{title}, '%')
</if>
</select>
</mapper>
src/test/java/cn/chengaofeng/MybatisTest.java
@Test
public void testSelectByDynamicSQL() throws Exception {
SqlSession sqlSession = null;
try {
sqlSession = MyBatisUtils.openSession();
Goods goods = new Goods();
// goods.setTitle("测试");
goods.setGoodsId(739);
List<Goods> list = sqlSession.selectList("cn.chengaofeng.mapper.GoodsMapper.dynamicSQL", goods);
System.out.println(list);
} catch (Exception e) {
throw e;
} finally {
MyBatisUtils.closeSession(sqlSession);
}
}
说明
在使用where时,因为动态sql里面都有and,如果没加条件时会造成sql语法错误,可以使用where 1=1
为了解决这个问题,MyBatis提供了<where>标签,就不需要1=1这种写法了,会自动去掉and,保证语法正确
MyBatis的二级缓存
一级缓存默认开启,缓存范围SqlSession会话
@Test
public void testLevel1Cache() throws Exception {
SqlSession sqlSession = null;
try {
sqlSession = MyBatisUtils.openSession();
Goods goods1 = sqlSession.selectOne("cn.chengaofeng.mapper.GoodsMapper.selectGoodsById", 739);
Goods goods2 = sqlSession.selectOne("cn.chengaofeng.mapper.GoodsMapper.selectGoodsById", 739);
System.out.println(goods1.hashCode() + "=" + goods2.hashCode());
} catch (Exception e) {
throw e;
} finally {
MyBatisUtils.closeSession(sqlSession);
}
try {
sqlSession = MyBatisUtils.openSession();
Goods goods1 = sqlSession.selectOne("cn.chengaofeng.mapper.GoodsMapper.selectGoodsById", 739);
// sqlSession.commit(); // 手动清空
Goods goods2 = sqlSession.selectOne("cn.chengaofeng.mapper.GoodsMapper.selectGoodsById", 739);
System.out.println(goods1.hashCode() + "=" + goods2.hashCode());
} catch (Exception e) {
throw e;
} finally {
MyBatisUtils.closeSession(sqlSession);
}
}
可以看出在同一个sqlSession中,多次查询会直接使用同一个缓存的内存数据,在closeSession后清空。
二级缓存需要手动开启,属于范围Mapper Namespace
<?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="cn.chengaofeng.mapper.GoodsMapper">
<!--开启二级缓存-->
<cache eviction="LRU" flushInterval="600000" size="512" readOnly="true" />
</mapper>
@Test
public void testLevel2Cache() throws Exception {
SqlSession sqlSession1 = null;
SqlSession sqlSession2 = null;
try {
sqlSession1 = MyBatisUtils.openSession();
Goods goods1 = sqlSession1.selectOne("cn.chengaofeng.mapper.GoodsMapper.selectGoodsById", 739);
System.out.println(goods1.hashCode());
} catch (Exception e) {
throw e;
} finally {
MyBatisUtils.closeSession(sqlSession1);
}
try {
sqlSession2 = MyBatisUtils.openSession();
Goods goods2 = sqlSession2.selectOne("cn.chengaofeng.mapper.GoodsMapper.selectGoodsById", 739);
System.out.println(goods2.hashCode());
} catch (Exception e) {
throw e;
} finally {
MyBatisUtils.closeSession(sqlSession2);
}
}
开启二级缓存后,不同sqlSession使用了相同的缓存
二级缓存运行规则
二级开启后默认所有查询操作都使用缓存
写操作commit提交时对该namespace缓存强制清空
配置useCache=false时,可以不用缓存
配置flushCache=true代表强制清空缓存
cache标签属性说明
eviction是缓存的清除策略,当缓存对象数量达到上限后,自动触发对应算法对缓存对象清除
- LRU - 最近最久未使用:移除最长时间不使用的对象(另外有个LFU算法,最近一段时间内访问最少的移除)
- FIFO - 先进先出: 按对象进入缓存的顺序来移除它们
- SOFT - 软引用: 移除基于垃圾收集器状态和软引用规则的对象
- WEAK - 弱引用:更积极的移除基于垃圾收集器状态和弱引用规则的对象flushInterval 代表间隔多长时间自动清空缓存,单位毫秒,600000毫秒 = 10分钟,每隔十分钟自动对缓存进行清除
size 缓存存储上限,用于保存对象或集合(1个集合算1个对象)的数量上限,建议设置为>表中总条数
readOnly 设置为true, 代表返回只读缓存,每次从缓存取出的是缓存对象本身,这种执行效率较高
设置为false,代表每次取出的是缓存对象的“副本”,每一次取出的对象都是不同的,这种安全性较高
开启二级缓存后,其它标签可以使用的属性
useCache 是否使用缓存,比如查询所有的select标签上可以设置 useCache = "false"
flushCache = "true" 在sql执行后强制清空缓存,效果跟commit是一样的。比如在insert标签上使用,在插入新数据后,清空缓存。select也可以清空,在清空后,这条查询数据也不会放入缓存