- 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
@Test
public 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
@Test
public 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()); // 1402215471
System.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()); // 661047965
System.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 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="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会话,第二次没有执行,使用的是缓存
@Test
public 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()); // 520162288
System.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()); // 520162288
System.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
@Test
public 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
@Test
public 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
@Test
public 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);
}
}