- MyBatis日志管理
- MyBatis动态SQL
- MyBatis二级缓存
- 在一次SqlSession会话中,对同一个数据进行反复提取时,就会利用一级缓存提高处理速度
#当SqlSession会话被释放的时候,一级缓存会被清空,因此时间较短,利用率不高 - 二级缓存开启后默认所有查询均使用缓存
#写操作commit提交时对该namespace缓存强制清空
#配置useCache=false可以不用缓存
#配置flushCache=true强制清空缓存 - selectAll:
设置useCache=”false”,表示不使用二级缓存,因为有可能找出来的内容很多
默认清空下,都会使用缓存,所有像selectAll这样的sql,要主动加上这个不使用缓存的配置
#insert:
设置flushCache=”true”,表示在执行完本次sql之后,立即强制清空缓存
默认清空下,不会强制清空缓冲 - jsqlparse:PageHelper的底层实现类
MyBatis日志管理
什么是日志
- 日志文件是用于记录系统操作事件的记录文件或文件集合
- 日志保存历史数据,是诊断问题以及理解系统活动的重要依据
日志门面与日志实现
- 日志门面就相当于插线板
- 日志实现就相当于插线板内部的具体电路实现

引入logback
#会同时引入SLF4
#引入之后就会输出更多的日志了
pom.xml
<dependencies>...<dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.3</version></dependency></dependencies>
自定义日志输出格式
src/main/resource/logback.xml
<?xml version="1.0" encoding="UTF-8"?><configuration><appender name="console" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern></encoder></appender><!--日志输出级别(优先级高到低)error:错误 - 系统的故障日志warn: 警告 - 存在风险或使用不当的日志info: 一般性消息debug: 程序内部用于调试信息trace: 程序运行的跟踪信息--><!-- level在debug以上的日志,都会以console的形式显示 --><root level="debug"><appender-ref ref="console"></appender-ref></root></configuration>
%d{HH:mm:ss.SSS}:时间格式
[%thread]:线程名称
%-5level:日志级别,-5表示按5个字符进行右对齐
%logger{36}:产生日志的类名
%msg:具体的日志内容
%n:换行
MyBatis动态SQL
动态SQL是指根据参数数据动态组织SQL的技术
src/main/resources/mappers/goods.xml
#在where标签内,可以直接使用and,当不需要的时候,会自动在sql语句里去掉and
<select id="dynamicSQL" parameterType="java.util.Map" resultType="com.song.mybatis.entity.Goods">select * from t_goods<where><if test="categoryId != null">and category_id = #{categoryId}</if><if test="currentPrice != null">and current_price = #{currentPrice}</if></where></select>
src/test/java/com.song.mybatis/MyBatisTestor.java
@Testpublic void testDynamicSQL(){SqlSession session = null;try{session = MyBatisUtils.openSession();Map param = new HashMap();param.put("categoryId",44);param.put("currentPrice",49);List<Goods> list = session.selectList("goods.dynamicSQL",param);for (Goods g:list){System.out.println(g.getTitle() + ":" + g.getCategoryId() + ":" + g.getCurrentPrice());}}catch (Exception e){if(session != null){session.rollback(); // 回滚事务}e.printStackTrace();}finally {MyBatisUtils.closeSession(session);}}
MyBatis二级缓存
- 一级缓存默认开启,缓存范围是SqlSession会话
在一次SqlSession会话中,对同一个数据进行反复提取时,就会利用一级缓存提高处理速度
#当SqlSession会话被释放的时候,一级缓存会被清空,因此时间较短,利用率不高
- 二级缓存手动开启,缓存范围Mapper Namespace
二级缓存开启后默认所有查询均使用缓存
#写操作commit提交时对该namespace缓存强制清空
#配置useCache=false可以不用缓存
#配置flushCache=true强制清空缓存

一级缓存
#第一次SqlSession会话,执行一次sql查询
#第二次SqlSession会话,执行二次sql查询
因为commit操作之后会强制清空缓存
src/test/java/com.song.mybatis/MyBatisTestor.java
@Testpublic void testLv1Cache(){SqlSession sqlSession = null;// 第一次sqlsession会话try{sqlSession = MyBatisUtils.openSession();Goods goods1 = sqlSession.selectOne("goods.selectById", 1602);Goods goods2 = sqlSession.selectOne("goods.selectById", 1602);System.out.println(goods1.hashCode()); // 1402215471System.out.println(goods2.hashCode()); // 1402215471}catch (Exception e){e.printStackTrace();}finally {MyBatisUtils.closeSession(sqlSession);}// 第二次sqlsession会话try{sqlSession = MyBatisUtils.openSession();Goods goods1 = sqlSession.selectOne("goods.selectById", 1602);sqlSession.commit();Goods goods2 = sqlSession.selectOne("goods.selectById", 1602);System.out.println(goods1.hashCode()); // 661047965System.out.println(goods2.hashCode()); // 873827336}catch (Exception e){e.printStackTrace();}finally {MyBatisUtils.closeSession(sqlSession);}}
二级缓存配置
- eviction
缓存的清除策略,当缓存对象数量达到上限后,自动触发对应算法清除缓存对象
LRU - 最近最少使用的:移除最长时间不被使用的对象
FIFO - 先进先出:按对象进入缓存的顺序来移除它们
SOFT - 软引用:移除基于垃圾回收器状态和软引用规则的对象
WEAK - 弱引用:更积极地移除基于垃圾回收器状态和弱引用规则的对象
- flushInterval
间隔多长时间自动清除缓存,单位毫秒
- size
缓存存储上限,用户保存对象或集合(1个集合算1个对象)的数量上限
- readOnly
设置为true:代表返回只读缓存,每次从缓存中取出的是缓存对象本身,执行效率较高
设置为false:代表每次取出的是缓存对象的”副本”,每次取出的对象是不同的,安全性较高
goods.xml
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapperPUBLIC "-//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="selectAll" resultType="com.song.mybatis.entity.Goods" useCache="false">select * from t_goods order by goods_id desc limit 10</select><insert id="insert" parameterType="com.song.mybatis.entity.Goods" flushCache="true">INSERT INTO t_goods(title, sub_title, original_cost, current_price, discount, is_free_delivery, category_id)VALUES (#{title}, #{subTitle}, #{originalCost}, #{currentPrice}, #{discount}, #{isFreeDelivery}, #{categoryId})<selectKey resultType="Integer" keyProperty="goodsId" order="AFTER">select last_insert_id()</selectKey></insert>...其他sql...</mapper>
selectAll:
设置useCache=”false”,表示不使用二级缓存,因为有可能找出来的内容很多
默认清空下,都会使用缓存,所有像selectAll这样的sql,要主动加上这个不使用缓存的配置
#insert:
设置flushCache=”true”,表示在执行完本次sql之后,立即强制清空缓存
默认清空下,不会强制清空缓冲
src/test/java/com.song.mybatis/MyBatisTestor.java
#只执行了第一次sqlsession会话,第二次没有执行,使用的是缓存
@Testpublic void testLv1Cache(){SqlSession sqlSession = null;// 第一次sqlsession会话try{sqlSession = MyBatisUtils.openSession();Goods goods1 = sqlSession.selectOne("goods.selectById", 1602);Goods goods2 = sqlSession.selectOne("goods.selectById", 1602);System.out.println(goods1.hashCode()); // 520162288System.out.println(goods2.hashCode()); // 520162288}catch (Exception e){e.printStackTrace();}finally {MyBatisUtils.closeSession(sqlSession);}// 第二次sqlsession会话try{sqlSession = MyBatisUtils.openSession();Goods goods1 = sqlSession.selectOne("goods.selectById", 1602);sqlSession.commit();Goods goods2 = sqlSession.selectOne("goods.selectById", 1602);System.out.println(goods1.hashCode()); // 520162288System.out.println(goods2.hashCode()); // 520162288}catch (Exception e){e.printStackTrace();}finally {MyBatisUtils.closeSession(sqlSession);}}
OneToMany对象关联查询
同时获取商品和商品详情信息,感觉不常用,省略。。。
PageHelper分页插件
官网:https://pagehelper.github.io/
使用:
- maven引入PageHelper和jsqlparse
jsqlparse:PageHelper的底层实现类
pom.xml
<dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper</artifactId><version>5.1.11</version></dependency><dependency><groupId>com.github.jsqlparser</groupId><artifactId>jsqlparser</artifactId><version>2.0</version></dependency>
- mybatis-config.xml增加Plugin配置
src/main/resources/mybatis-config.xml
<settings><!-- goods_id ==> goodsId 驼峰命名转换 --><setting name="mapUnderscoreToCamelCase" value="true"/></settings><!-- 启用PageHelper分页查询 --><plugins><plugin interceptor="com.github.pagehelper.PageInterceptor"><!-- 设置数据库类型 --><property name="helperDialect" value="mysql"/><!-- 分页合理化 --><property name="reasonable" value="true"/></plugin></plugins>
- 代码中使用PageHelper.startPage()自动分页
src/main/resources/mappers/goods.xml
<select id="selectPage" resultType="com.song.mybatis.entity.Goods">select * from t_goods where current_price < 1000</select>
src/test/java/com.song.mybatis/MyBatisTestor.java
@Testpublic void testSelectPage(){SqlSession session = null;try{session = MyBatisUtils.openSession();PageHelper.startPage(2,10);Page<Goods> page = (Page)session.selectList("goods.selectPage");System.out.println("总页数"+page.getPages());System.out.println("总记录数"+page.getTotal());System.out.println("当前页码"+page.getPageNum());List<Goods> result = page.getResult();for(Goods g:result){System.out.println(g.getTitle());}}catch (Exception e){if(session != null){session.rollback(); // 回滚事务}e.printStackTrace();}finally {MyBatisUtils.closeSession(session);}}
MyBatis整合C3P0连接池
现状
src/main/resources/mybatis-config.xml
#现在使用的是MyBatis自带的连接池,但是有更好的连接池来管理数据库连接,比如C3P0
<!--设置默认指向的数据库--><environments default="dev"><!--配置环境,不同的环境不同的id名字--><environment id="dev"><!-- 采用JDBC方式对数据库事务进行commit/rollback --><transactionManager type="JDBC"></transactionManager><!--采用连接池方式管理数据库连接--><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url"value="jdbc:mysql://localhost:3306/babytun?useUnicode=true&characterEncoding=UTF-8"/><property name="username" value="root"/><property name="password" value="xxhacys2015"/></dataSource></environment>
改善
pom.xml
<dependency><groupId>com.mchange</groupId><artifactId>c3p0</artifactId><version>0.9.5.4</version></dependency>
main/java/com.song.mybatis/datasource/C3P0DataSourceFactory(新建类)
package com.song.mybatis.datasource;import com.mchange.v2.c3p0.ComboPooledDataSource;import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory;/*** C3P0与MyBatis兼容使用的数据源工厂类*/public class C3P0DataSourceFactory extends UnpooledDataSourceFactory {public C3P0DataSourceFactory(){// 在C3P0DataSourceFactory类初始化的时候,dataSource由C3P0负责创建this.dataSource = new ComboPooledDataSource();}}
src/main/resources/mybatis-config.xml
<!--设置默认指向的数据库--><environments default="dev"><!--配置环境,不同的环境不同的id名字--><environment id="dev"><!-- 采用JDBC方式对数据库事务进行commit/rollback --><transactionManager type="JDBC"></transactionManager><!--采用C3P0连接池方式管理数据库连接--><dataSource type="com.song.mybatis.datasource.C3P0DataSourceFactory"><property name="driverClass" value="com.mysql.jdbc.Driver"/><property name="jdbcUrl"value="jdbc:mysql://localhost:3306/babytun?useUnicode=true&characterEncoding=UTF-8"/><property name="user" value="root"/><property name="password" value="xxhacys2015"/><property name="initialPoolSize" value="5"/></dataSource></environment>
MyBatis批处理
批量添加数据
src/main/resources/mappers/goods.xml
<insert id="batchInsert" parameterType="java.util.List">INSERT INTO t_goods(title, sub_title, original_cost, current_price, discount, is_free_delivery, category_id)VALUES<foreach collection="list" item="item" index="index" separator=",">(#{item.title},#{item.subTitle},#{item.originalCost},#{item.currentPrice},#{item.discount},#{item.isFreeDelivery},#{item.categoryId})</foreach></insert>
src/test/java/com.song.mybatis/MyBatisTestor.java
@Testpublic void testBatchInsert(){SqlSession session = null;try{session = MyBatisUtils.openSession();List list = new ArrayList();for (int i=0;i<10;i++){Goods goods = new Goods();goods.setTitle("测试商品");goods.setSubTitle("测试子标签");goods.setOriginalCost(200f);goods.setCurrentPrice(100f);goods.setDiscount(0.5f);goods.setIsFreeDelivery(1);goods.setCategoryId(43);list.add(goods);}session.insert("goods.batchInsert",list);session.commit();}catch (Exception e){if(session != null){session.rollback(); // 回滚事务}e.printStackTrace();}finally {MyBatisUtils.closeSession(session);}
批量插入数据的局限
- 无法获得插入数据的id
- 批量生产的sql太长,可能会被服务器拒绝
批量删除数据
src/main/resources/mappers/goods.xml
<delete id="batchDelete" parameterType="java.util.List">DELETE FROM t_goods WHERE goods_id in<foreach collection="list" item="item" index="index" open="(" close=")" separator=",">#{item}</foreach></delete>
src/test/java/com.song.mybatis/MyBatisTestor.java
@Testpublic void testBatchDelete(){SqlSession session = null;try{session = MyBatisUtils.openSession();List list = new ArrayList();list.add(2733);list.add(2734);list.add(2735);session.delete("goods.batchDelete",list);session.commit();}catch (Exception e){if(session != null){session.rollback(); // 回滚事务}e.printStackTrace();}finally {MyBatisUtils.closeSession(session);}}
Mybatis注解开发

