文章目录

  • MyBatis进阶
  • 一、MyBatis日志管理
  • 1、什么是日志
  • 2、SLF4j与Logback
  • 二、MyBatis动态SQL
  • 1、动态SQl
  • 三、MyBatis二级缓存
  • 1、缓存的范围
  • 2、二级缓存运行规则
  • 3、二级缓存配置
  • 四、MyBatis多表级联查询
  • 1、OneToMany对象关联查询
  • 2、ManyToOne对象关联查询
  • 五、分页插件PageHelper
  • 1、分页查询的麻烦事
  • 2、PageHelper使用流程
  • 3、不同数据库分页的实现原理
  • 六、MyBatis配置C3P0连接池
  • 七、MyBatis批处理
  • 1、批量新增
  • 2、批量删除
  • 八、MyBatis注解开发
  • 1、xml和注解开发区别


MyBatis进阶

一、MyBatis日志管理

1、什么是日志

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

2、SLF4j与Logback

springboot cloud mybatis 开启了debug 但日志中不打印sql语句_MyBatis


logback和log4j是同一个人开发的,logback是log4j的升级版,从维护性和执行效率上都得到了提升。

在pom.xml中添加logback依赖:

<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-classic</artifactId>
  <version>1.2.11</version>
</dependency>

在resources目录下新增logback.xml配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
	<!--
	appender 输出器,表示在什么地方日志输出
	ConsoleAppender 向控制台日志的打印输出
	-->
	<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
		<!--encoder 编码-->
		<encoder>
			<!--
			pattern 规定日志输出的格式
			%d{HH:mm:ss.SSS} 时间输出格式
			[%thread] 输出的线程的名字
			%-5level 日志的级别,按5个字符右对齐
			%logger{36} 日志产生的类,这条字符串最多允许36字符,如果超过36字符会把类路径简写压缩
			%msg 具体的日志内容
			%n 换行
			-->
			<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
		</encoder>
	</appender>
	<!--
	root 日志打印的根标签
	level 日志输出级别(优先级高到低):
	error:错误 - 系统故障日志
	warn: 警告 - 存在风险或使用不当的日志
	info: 一般性消息
	debug:程序内部用于调试信息
	trace:程序运行的跟踪消息
	-->
	<root level="debug">
		<appender-ref ref="console"/>
	</root>
</configuration>

其他详细信息可以参考logback官网
https://logback.qos.ch/

二、MyBatis动态SQL

1、动态SQl

动态SQL是指根据参数数据动态组织SQL的技术。

springboot cloud mybatis 开启了debug 但日志中不打印sql语句_java_02


where标签会动态判断去掉第一个子语句的and。

三、MyBatis二级缓存

一级缓存默认开启,缓存范围SqlSession会话。
二级缓存手动开启,属于范围Mapper Namespace。

1、缓存的范围

springboot cloud mybatis 开启了debug 但日志中不打印sql语句_MyBatis_03

2、二级缓存运行规则

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

3、二级缓存配置

在相关mapper.xml文件中开启并配置二级缓存:

<mapper namespace="goods">
	<!--开启了二级缓存
	eviction 是缓存的清楚策略,当缓存对象数量达到上限后,自动触发对应算法对缓存对象清楚
		1.LRU - 最近最久未使用:移除最长时间不被使用的对象。(推荐)
		2.LFU - 最近最少使用:移除最近访问频率最低的对象。
		3.FIFO - 先进先出:按对象进入缓存的顺序来移除它们。(无法保证缓存命中率,不推荐)
		4.SOFT - 软引用:移除基于垃圾收集器状态和软引用规则的对象。(基于JVM,而不受MyBatis控制,不推荐)
		5.WEAK - 弱引用:更积极的移除基于垃圾收集器状态和弱引用规则的对象。(基于JVM,而不受MyBatis控制,不推荐)
	flushInterval 代表间隔多长时间自动清空缓存,单位毫秒,600000毫秒=10分钟。
	size 缓存存储上限,用于保存对象或集合(1个集合算1个对象)的数量上限。
	readOnly 设置为true,代表返回只读缓存,每次从缓存取出是缓存对象本身,这种执行效率高。(推荐)
		设置为false,代表每次取出的是缓存对象的“副本”,每一次取出的对象都是不同的,这种安全性较高。		
	-->
	<cache eviction="LRU" flushInterval="600000" size="512" readOnly="true"/>
	<!--useCache="false" 代表不使用缓存,返回集合不推荐使用缓存,因为缓存命令率低,占用内存大-->
	<select id="selectAll" useCache="false" ...>
		select ...
	</select>
	<!--flushCache="true" 在sql执行后强制清空缓存,用在select语句时,该语句结果也不会放入缓存里-->
	<insert id="insert" flushCache="true" ...>
		insert ...
	</insert>
</mapper>

四、MyBatis多表级联查询

1、OneToMany对象关联查询

首先在主实体类中添加关联集合:

public class Goods{
	private Integer goodsId;
	private String title;
	private List<GooodsDetail> goodsDetails;
	...
}

然后在对应的mapper.xml文件中添加级联查询:

<!--
	resultMap可用说明一对多或者多对一的映射逻辑
	id是resultMap属性引用的标志
	type 指向One的实体(Goods)
-->
<resultMap id="rmGoods1" type="com.ql.mybatis.entity.Goods">
	<!--映射goods对象的主键到goods_id字段
	除了id字段,其他字段只要符合驼峰命名转换规则,且开启了驼峰命名转换,就不需要一一配置-->
	<id column="goods_id" property="goodsId"></id>
	<!--
		collection的含义是,在select语句得到结果以后,对所有Goods对象遍历得到goods_id字段值,并代入到goodsDetail命名空间的findByGoodsId的SQL中执行查询,将得到的“商品详情”集合赋值给goodsDetails List对象。
	-->
	<collection property="goodsDetails" select="goodsDetail.selectByGoodsId" column="goods_id"/>
</resultMap>
<select id="selectOneToMany" resultMap="rmGoods1">
	select * from t_goods limit 0,1
</select>

2、ManyToOne对象关联查询

首先在多的一方实体类中添加关联集合:

public class GoodsDetail{
	private Integer gdId;
	private Integer goodsId;
	private Goods goods;
	...
}

然后在对应的mapper.xml文件中添加级联查询:

<resultMap id="rmGoodsDetail" type="com...GoodsDetail">
	<id column="gd_id" property="gdId"/>
	<!--因为在association标签中配置了goods_id字段,所以额外配置一下,不然goodsId属性接受不到值,其他字段不用-->
	<result column="goods_id" property="goodsId"/>
	<!--用于一对一和多对一-->
	<association property="goods" select="goods.selectById" column="goods_id"></association>
</resultMap>
<select id="selectManyToOne" resultMap="rmGoodsDetail">
	select * from t_good_detail
</select>

五、分页插件PageHelper

1、分页查询的麻烦事

当前页数据查询 - select * from tab limit 0,10
总记录数查询 - select count(*) from tab
程序计算总页数、上一页页码、下一页页码。
PageHelper官网:
https://pagehelper.github.io/

2、PageHelper使用流程

maven引入PageHelper与jsqlparser

<dependency>
  <groupId>com.github.pagehelper</groupId>
  <artifactId>pagehelper</artifactId>
  <version>5.1.10</version>
</dependency>
<dependency>
  <groupId>com.github.jsqlparser</groupId>
  <artifactId>jsqlparser</artifactId>
  <version>2.0</version>
</dependency>

mybatis-config.xml增加Plugin配置

<configuration>
	<!--启用PageHelper分页插件-->
	<plugins>
		<plugin interceptor="com.github.pagehelper.PageInterceptor">
			<!--设置数据库方言-->
			<property name="helperDialect" value="mysql"/>
			<!--分页合理化-->
			<property name="reasonable" value="true"/>
		</plugin>
	</plugins>
</configuration>

代码中使用PageHelper.startPage()自动分页

/*startPage(pageNum, paseSize)方法会自动将下一次查询进行分页*/
PageHelper.startPage(2,10);
Page<Goods> page = (Page)session.selectList("goods.selectPage");
//selectPage的sql语句就是普通的select语句
page.getPages();//总页数
page.getTotal();//总记录数
page.getStartRow();//开始行号
page.getEndRow();//结束行号
page.getPageNum();//当前页码
List<Goods> data = page.getResult();//当前页数据

3、不同数据库分页的实现原理

MySQL分页

springboot cloud mybatis 开启了debug 但日志中不打印sql语句_二级缓存_04


Oracle

springboot cloud mybatis 开启了debug 但日志中不打印sql语句_二级缓存_05


SQL Server 2000

springboot cloud mybatis 开启了debug 但日志中不打印sql语句_缓存_06


SQL Sever 2012+

springboot cloud mybatis 开启了debug 但日志中不打印sql语句_xml_07

六、MyBatis配置C3P0连接池

首先在pom.xml中引入依赖:

<dependency>
  <groupId>com.mchange</groupId>
  <artifactId>c3p0</artifactId>
  <version>0.9.5.5</version>
</dependency>

创建C3P0DataSourceFactory(可以在项目目录下创建datasource包,在创建此类)

/*
C3P0与MyBatis兼容使用的数据源工厂类
*/
public class C3P0DataSourceFactory extends UnpooledDataSourceFactory{
	public C3P0DataSourceFactory(){
		this.dataSource = new ComboPooledDataSource();
	}
}

mybatis-config.xml中配置数据源

<dataSource type="com.ql.mybatis.datasource.C3P0DataSourceFactory">
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/databaseName?useUnicode=true&characterEncoding=UTF-8"/>
        <property name="user" value="root"/>
        <property name="password" value="root"/>
        <property name="initialPoolSize" value="5"/>
        <property name="maxPoolSize" value="20"/>
        <property name="minPoolSize" value="5"/>
      </dataSource>

七、MyBatis批处理

1、批量新增

在相应的Mapper.xml中编写SQL

<!--其中collection的值固定为list-->
<insert id="batchInsert" parameterType="java.util.List">
	insert into t_goods (title, sub_title, original_cost ... )
	values 
	<foreach collection="list" item="item" index="index" separator=",">
		(#{item.title},#{item.subTitle}...)
	</foreach>
</insert>

在Java中调用时传List即可

session.insert("goods.batchInsert", list);

批量插入数据的局限
1、无法获得插入数据的id
2、批量生成的SQL太长,可能会被服务器拒绝。需要插入的数据太多时可以分批次插入。

2、批量删除

<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>
session.delete("goods.batchDelete", list);

八、MyBatis注解开发

springboot cloud mybatis 开启了debug 但日志中不打印sql语句_缓存_08


在dao包里创建GoodsDAO接口

public interface GoodsDAO{
	@Select("select * from t_goods where current_price between #{min} and #{max} order by current_price limit 0,#{limt}")
	public List<Goods> selectByPriceRange(@param("min") Float min, @Param("max") Float max, @Param("limt") Integer limt);
	
	@Insert("insert into t_goods (title, sub_title,...) values (#{title},#{subTitle},...)")
	//<selectKey>
	@SelectKey(statement="select last_insert_id()", before=false, keyProperty="goodsId", resultType=Integer.class)
	public int insert(Goods goods);

	@Select("select * from t_goods")
	//<resultMap>
	@Results({
		//<id>
		@Result(column="goods_id", property="goodsId", id=true),
		@Result(column="title", property="title"),
		@Result(column="current_price", property="currentPrice")
	})
	public List<GoodsDTO> selectAll();
}

在mybatis-config.xml配置文件中增加相应的说明:

<mappers>
	<!--
		使用注解开发时mapper配置有两种写法,我们更推荐直接配置包,这样里面的文件不用一个一个再去配置
		<mapper class="com.ql.mybatis.dao.GoodsDAO"/>
	-->
	<package name="com.ql.mybatis.dao"/>
</mappers>

最后在Java代码中调用

//采用注解方式开发时先获取映射器,它会基于代理模式自动生成对应的实现类
GoodsDAO goodsDAO = session.getMapper(GoodsDAO.class);
List<Goods> list = goodsDAO.selectByPriceRange(100f,500f,20);
//insert()方法返回值代表本次成功插入的记录总数
int num = goodsDAO.insert(goods);
session.commit();//提交事务数据
List<GoodsDTO> list2 = goodsDAO.selectAll();

1、xml和注解开发区别

利用xml有更好的维护性,可以让我们在xml中对sql灵活的修改,更适合大型团队协作的项目。
利用注解有更好的程序编码的体验,不需要添加xml文件,在程序中直接修改即可,更适合小型敏捷开发的工程。