MyBatis日志管理

什么是日志

  • 日志文件是用于记录系统操作事件的记录文件或文件集合
  • 日志保存历史数据,是诊断问题以及理解系统活动的重要依据

日志门面与日志实现

  • 日志门面就相当于插线板
  • 日志实现就相当于插线板内部的具体电路实现

image.png

引入logback
#会同时引入SLF4
#引入之后就会输出更多的日志了
pom.xml

  1. <dependencies>
  2. ...
  3. <dependency>
  4. <groupId>ch.qos.logback</groupId>
  5. <artifactId>logback-classic</artifactId>
  6. <version>1.2.3</version>
  7. </dependency>
  8. </dependencies>

自定义日志输出格式
src/main/resource/logback.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <configuration>
  3. <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
  4. <encoder>
  5. <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
  6. </encoder>
  7. </appender>
  8. <!--
  9. 日志输出级别(优先级高到低)
  10. error:错误 - 系统的故障日志
  11. warn: 警告 - 存在风险或使用不当的日志
  12. info: 一般性消息
  13. debug: 程序内部用于调试信息
  14. trace: 程序运行的跟踪信息
  15. -->
  16. <!-- leveldebug以上的日志,都会以console的形式显示 -->
  17. <root level="debug">
  18. <appender-ref ref="console"></appender-ref>
  19. </root>
  20. </configuration>

%d{HH:mm:ss.SSS}:时间格式
[%thread]:线程名称
%-5level:日志级别,-5表示按5个字符进行右对齐
%logger{36}:产生日志的类名
%msg:具体的日志内容
%n:换行

MyBatis动态SQL

动态SQL是指根据参数数据动态组织SQL的技术
微信图片_20200406201651.png

src/main/resources/mappers/goods.xml
#在where标签内,可以直接使用and,当不需要的时候,会自动在sql语句里去掉and

  1. <select id="dynamicSQL" parameterType="java.util.Map" resultType="com.song.mybatis.entity.Goods">
  2. select * from t_goods
  3. <where>
  4. <if test="categoryId != null">
  5. and category_id = #{categoryId}
  6. </if>
  7. <if test="currentPrice != null">
  8. and current_price = #{currentPrice}
  9. </if>
  10. </where>
  11. </select>

src/test/java/com.song.mybatis/MyBatisTestor.java

  1. @Test
  2. public void testDynamicSQL(){
  3. SqlSession session = null;
  4. try{
  5. session = MyBatisUtils.openSession();
  6. Map param = new HashMap();
  7. param.put("categoryId",44);
  8. param.put("currentPrice",49);
  9. List<Goods> list = session.selectList("goods.dynamicSQL",param);
  10. for (Goods g:list){
  11. System.out.println(g.getTitle() + ":" + g.getCategoryId() + ":" + g.getCurrentPrice());
  12. }
  13. }catch (Exception e){
  14. if(session != null){
  15. session.rollback(); // 回滚事务
  16. }
  17. e.printStackTrace();
  18. }finally {
  19. MyBatisUtils.closeSession(session);
  20. }
  21. }

MyBatis二级缓存

  • 一级缓存默认开启,缓存范围是SqlSession会话

在一次SqlSession会话中,对同一个数据进行反复提取时,就会利用一级缓存提高处理速度
#当SqlSession会话被释放的时候,一级缓存会被清空,因此时间较短,利用率不高

  • 二级缓存手动开启,缓存范围Mapper Namespace

二级缓存开启后默认所有查询均使用缓存
#写操作commit提交时对该namespace缓存强制清空
#配置useCache=false可以不用缓存
#配置flushCache=true强制清空缓存

image.png

一级缓存
#第一次SqlSession会话,执行一次sql查询
#第二次SqlSession会话,执行二次sql查询
因为commit操作之后会强制清空缓存

src/test/java/com.song.mybatis/MyBatisTestor.java

  1. @Test
  2. public void testLv1Cache(){
  3. SqlSession sqlSession = null;
  4. // 第一次sqlsession会话
  5. try{
  6. sqlSession = MyBatisUtils.openSession();
  7. Goods goods1 = sqlSession.selectOne("goods.selectById", 1602);
  8. Goods goods2 = sqlSession.selectOne("goods.selectById", 1602);
  9. System.out.println(goods1.hashCode()); // 1402215471
  10. System.out.println(goods2.hashCode()); // 1402215471
  11. }catch (Exception e){
  12. e.printStackTrace();
  13. }finally {
  14. MyBatisUtils.closeSession(sqlSession);
  15. }
  16. // 第二次sqlsession会话
  17. try{
  18. sqlSession = MyBatisUtils.openSession();
  19. Goods goods1 = sqlSession.selectOne("goods.selectById", 1602);
  20. sqlSession.commit();
  21. Goods goods2 = sqlSession.selectOne("goods.selectById", 1602);
  22. System.out.println(goods1.hashCode()); // 661047965
  23. System.out.println(goods2.hashCode()); // 873827336
  24. }catch (Exception e){
  25. e.printStackTrace();
  26. }finally {
  27. MyBatisUtils.closeSession(sqlSession);
  28. }
  29. }

二级缓存配置

  • eviction

缓存的清除策略,当缓存对象数量达到上限后,自动触发对应算法清除缓存对象
LRU - 最近最少使用的:移除最长时间不被使用的对象
FIFO - 先进先出:按对象进入缓存的顺序来移除它们
SOFT - 软引用:移除基于垃圾回收器状态和软引用规则的对象
WEAK - 弱引用:更积极地移除基于垃圾回收器状态和弱引用规则的对象

  • flushInterval

间隔多长时间自动清除缓存,单位毫秒

  • size

缓存存储上限,用户保存对象或集合(1个集合算1个对象)的数量上限

  • readOnly

设置为true:代表返回只读缓存,每次从缓存中取出的是缓存对象本身,执行效率较高
设置为false:代表每次取出的是缓存对象的”副本”,每次取出的对象是不同的,安全性较高

goods.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE mapper
  3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5. <mapper namespace="goods">
  6. <!-- 开启二级缓存 -->
  7. <cache eviction="LRU" flushInterval="600000" size="512" readOnly="true"/>
  8. <select id="selectAll" resultType="com.song.mybatis.entity.Goods" useCache="false">
  9. select * from t_goods order by goods_id desc limit 10
  10. </select>
  11. <insert id="insert" parameterType="com.song.mybatis.entity.Goods" flushCache="true">
  12. INSERT INTO t_goods(title, sub_title, original_cost, current_price, discount, is_free_delivery, category_id)
  13. VALUES (#{title}, #{subTitle}, #{originalCost}, #{currentPrice}, #{discount}, #{isFreeDelivery}, #{categoryId})
  14. <selectKey resultType="Integer" keyProperty="goodsId" order="AFTER">
  15. select last_insert_id()
  16. </selectKey>
  17. </insert>
  18. ...其他sql...
  19. </mapper>

selectAll:
设置useCache=”false”,表示不使用二级缓存,因为有可能找出来的内容很多
默认清空下,都会使用缓存,所有像selectAll这样的sql,要主动加上这个不使用缓存的配置
#insert:
设置flushCache=”true”,表示在执行完本次sql之后,立即强制清空缓存
默认清空下,不会强制清空缓冲

src/test/java/com.song.mybatis/MyBatisTestor.java
#只执行了第一次sqlsession会话,第二次没有执行,使用的是缓存

  1. @Test
  2. public void testLv1Cache(){
  3. SqlSession sqlSession = null;
  4. // 第一次sqlsession会话
  5. try{
  6. sqlSession = MyBatisUtils.openSession();
  7. Goods goods1 = sqlSession.selectOne("goods.selectById", 1602);
  8. Goods goods2 = sqlSession.selectOne("goods.selectById", 1602);
  9. System.out.println(goods1.hashCode()); // 520162288
  10. System.out.println(goods2.hashCode()); // 520162288
  11. }catch (Exception e){
  12. e.printStackTrace();
  13. }finally {
  14. MyBatisUtils.closeSession(sqlSession);
  15. }
  16. // 第二次sqlsession会话
  17. try{
  18. sqlSession = MyBatisUtils.openSession();
  19. Goods goods1 = sqlSession.selectOne("goods.selectById", 1602);
  20. sqlSession.commit();
  21. Goods goods2 = sqlSession.selectOne("goods.selectById", 1602);
  22. System.out.println(goods1.hashCode()); // 520162288
  23. System.out.println(goods2.hashCode()); // 520162288
  24. }catch (Exception e){
  25. e.printStackTrace();
  26. }finally {
  27. MyBatisUtils.closeSession(sqlSession);
  28. }
  29. }

OneToMany对象关联查询

同时获取商品和商品详情信息,感觉不常用,省略。。。

PageHelper分页插件

官网:https://pagehelper.github.io/
使用:

  1. maven引入PageHelper和jsqlparse

jsqlparse:PageHelper的底层实现类

pom.xml

  1. <dependency>
  2. <groupId>com.github.pagehelper</groupId>
  3. <artifactId>pagehelper</artifactId>
  4. <version>5.1.11</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>com.github.jsqlparser</groupId>
  8. <artifactId>jsqlparser</artifactId>
  9. <version>2.0</version>
  10. </dependency>
  1. mybatis-config.xml增加Plugin配置

src/main/resources/mybatis-config.xml

  1. <settings>
  2. <!-- goods_id ==> goodsId 驼峰命名转换 -->
  3. <setting name="mapUnderscoreToCamelCase" value="true"/>
  4. </settings>
  5. <!-- 启用PageHelper分页查询 -->
  6. <plugins>
  7. <plugin interceptor="com.github.pagehelper.PageInterceptor">
  8. <!-- 设置数据库类型 -->
  9. <property name="helperDialect" value="mysql"/>
  10. <!-- 分页合理化 -->
  11. <property name="reasonable" value="true"/>
  12. </plugin>
  13. </plugins>
  1. 代码中使用PageHelper.startPage()自动分页

src/main/resources/mappers/goods.xml

  1. <select id="selectPage" resultType="com.song.mybatis.entity.Goods">
  2. select * from t_goods where current_price &lt; 1000
  3. </select>

src/test/java/com.song.mybatis/MyBatisTestor.java

  1. @Test
  2. public void testSelectPage(){
  3. SqlSession session = null;
  4. try{
  5. session = MyBatisUtils.openSession();
  6. PageHelper.startPage(2,10);
  7. Page<Goods> page = (Page)session.selectList("goods.selectPage");
  8. System.out.println("总页数"+page.getPages());
  9. System.out.println("总记录数"+page.getTotal());
  10. System.out.println("当前页码"+page.getPageNum());
  11. List<Goods> result = page.getResult();
  12. for(Goods g:result){
  13. System.out.println(g.getTitle());
  14. }
  15. }catch (Exception e){
  16. if(session != null){
  17. session.rollback(); // 回滚事务
  18. }
  19. e.printStackTrace();
  20. }finally {
  21. MyBatisUtils.closeSession(session);
  22. }
  23. }

MyBatis整合C3P0连接池

现状
src/main/resources/mybatis-config.xml
#现在使用的是MyBatis自带的连接池,但是有更好的连接池来管理数据库连接,比如C3P0

  1. <!--设置默认指向的数据库-->
  2. <environments default="dev">
  3. <!--配置环境,不同的环境不同的id名字-->
  4. <environment id="dev">
  5. <!-- 采用JDBC方式对数据库事务进行commit/rollback -->
  6. <transactionManager type="JDBC"></transactionManager>
  7. <!--采用连接池方式管理数据库连接-->
  8. <dataSource type="POOLED">
  9. <property name="driver" value="com.mysql.jdbc.Driver"/>
  10. <property name="url"
  11. value="jdbc:mysql://localhost:3306/babytun?useUnicode=true&amp;characterEncoding=UTF-8"/>
  12. <property name="username" value="root"/>
  13. <property name="password" value="xxhacys2015"/>
  14. </dataSource>
  15. </environment>

改善
pom.xml

  1. <dependency>
  2. <groupId>com.mchange</groupId>
  3. <artifactId>c3p0</artifactId>
  4. <version>0.9.5.4</version>
  5. </dependency>

main/java/com.song.mybatis/datasource/C3P0DataSourceFactory(新建类)

  1. package com.song.mybatis.datasource;
  2. import com.mchange.v2.c3p0.ComboPooledDataSource;
  3. import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory;
  4. /**
  5. * C3P0与MyBatis兼容使用的数据源工厂类
  6. */
  7. public class C3P0DataSourceFactory extends UnpooledDataSourceFactory {
  8. public C3P0DataSourceFactory(){
  9. // 在C3P0DataSourceFactory类初始化的时候,dataSource由C3P0负责创建
  10. this.dataSource = new ComboPooledDataSource();
  11. }
  12. }

src/main/resources/mybatis-config.xml

  1. <!--设置默认指向的数据库-->
  2. <environments default="dev">
  3. <!--配置环境,不同的环境不同的id名字-->
  4. <environment id="dev">
  5. <!-- 采用JDBC方式对数据库事务进行commit/rollback -->
  6. <transactionManager type="JDBC"></transactionManager>
  7. <!--采用C3P0连接池方式管理数据库连接-->
  8. <dataSource type="com.song.mybatis.datasource.C3P0DataSourceFactory">
  9. <property name="driverClass" value="com.mysql.jdbc.Driver"/>
  10. <property name="jdbcUrl"
  11. value="jdbc:mysql://localhost:3306/babytun?useUnicode=true&amp;characterEncoding=UTF-8"/>
  12. <property name="user" value="root"/>
  13. <property name="password" value="xxhacys2015"/>
  14. <property name="initialPoolSize" value="5"/>
  15. </dataSource>
  16. </environment>

MyBatis批处理

批量添加数据
src/main/resources/mappers/goods.xml

  1. <insert id="batchInsert" parameterType="java.util.List">
  2. INSERT INTO t_goods(title, sub_title, original_cost, current_price, discount, is_free_delivery, category_id)
  3. VALUES
  4. <foreach collection="list" item="item" index="index" separator=",">
  5. (#{item.title},#{item.subTitle},#{item.originalCost},#{item.currentPrice},#{item.discount},#{item.isFreeDelivery},#{item.categoryId})
  6. </foreach>
  7. </insert>

src/test/java/com.song.mybatis/MyBatisTestor.java

  1. @Test
  2. public void testBatchInsert(){
  3. SqlSession session = null;
  4. try{
  5. session = MyBatisUtils.openSession();
  6. List list = new ArrayList();
  7. for (int i=0;i<10;i++){
  8. Goods goods = new Goods();
  9. goods.setTitle("测试商品");
  10. goods.setSubTitle("测试子标签");
  11. goods.setOriginalCost(200f);
  12. goods.setCurrentPrice(100f);
  13. goods.setDiscount(0.5f);
  14. goods.setIsFreeDelivery(1);
  15. goods.setCategoryId(43);
  16. list.add(goods);
  17. }
  18. session.insert("goods.batchInsert",list);
  19. session.commit();
  20. }catch (Exception e){
  21. if(session != null){
  22. session.rollback(); // 回滚事务
  23. }
  24. e.printStackTrace();
  25. }finally {
  26. MyBatisUtils.closeSession(session);
  27. }

批量插入数据的局限

  • 无法获得插入数据的id
  • 批量生产的sql太长,可能会被服务器拒绝

批量删除数据
src/main/resources/mappers/goods.xml

  1. <delete id="batchDelete" parameterType="java.util.List">
  2. DELETE FROM t_goods WHERE goods_id in
  3. <foreach collection="list" item="item" index="index" open="(" close=")" separator=",">
  4. #{item}
  5. </foreach>
  6. </delete>

src/test/java/com.song.mybatis/MyBatisTestor.java

  1. @Test
  2. public void testBatchDelete(){
  3. SqlSession session = null;
  4. try{
  5. session = MyBatisUtils.openSession();
  6. List list = new ArrayList();
  7. list.add(2733);
  8. list.add(2734);
  9. list.add(2735);
  10. session.delete("goods.batchDelete",list);
  11. session.commit();
  12. }catch (Exception e){
  13. if(session != null){
  14. session.rollback(); // 回滚事务
  15. }
  16. e.printStackTrace();
  17. }finally {
  18. MyBatisUtils.closeSession(session);
  19. }
  20. }

Mybatis注解开发

image.png