MyBatis二级缓存
1. 一级缓存与二级缓存
- 一级缓存默认开启,缓存范围:SqlSession 会话 (范围小)
- 二级缓存手动开启,缓存范围:Mapper Namespace (范围大)
- SqlSession 生命周期短,销毁后一级缓存中的内容不存在
- 任意一个一级缓存需要数据时都可以在二级缓存中调用
2. 二级缓存配置
(在goods.xml中写,即书写sql语句的xml文件中)
<!--开启了二级缓存
eviction是缓存的清除策略,当缓存对象数量达到上限后,自动触发对应算法对缓存对象清除
1.LRU – 最近最少使用的:移除最长时间不被使用的对象。(最近被访问的时间)
LFU - 最近最少使用的;移除最近访问频率最低的对象。(最近被访问的次数)
O1 O2 O3 O4 .. O512
14 99 83 1 893
2.FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
3.SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
4.WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
flushInterval 代表间隔多长时间自动清空缓存,单位毫秒,600000毫秒 = 10分钟
size 缓存存储上限,用于保存对象或集合(1个集合算1个对象)的数量上限
readOnly 设置为true ,代表返回只读缓存,每次从缓存取出的是缓存对象本身.这种执行效率较高
设置为false , 代表每次取出的是缓存对象的"副本",每一次取出的对象都是不同的,这种安全性较高
-->
<cache eviction="LRU" flushInterval="600000" size="512" readOnly="true"/>
<!-- useCache="false"代表不使用缓存 -->
3. 二级缓存运行规则
- 二级开启默认所有查询操作均使用缓存
- 写操作 commit 提交时对该 namespace 缓存强制清空 (为了保证数据的一致性)
- 配置 useCache = false 可以不用缓存
- 配置 flushCache = true 代表强制清空缓存
OneToMany 对象关联查询
(一的一方要关联到多的一方,只需要持有多的一方的List):所以在一的一方的实体中添加多的一方的List作为属性
用到的是 collection 标签,集合,对应了List属性
- 在goods.xml中
<!-- 一对多的结果映射
对象关联:利用 resultMap 实现
resultMap可用于说明一对多或者多对一的映射逻辑
id 是resultMap属性引用的标志
type 指向One的实体(Goods)
-->
<resultMap id="rmGoods1" type="com.imooc.mybatis.entity.Goods">
<!-- 映射goods对象的主键到goods_id字段 -->
<id column="goods_id" property="goodsId"></id>
<!--
collection的含义是,在
select * from t_goods limit 0,1 得到结果后,对所有Goods对象遍历得到goods_id字段值,
并代入到goodsDetail命名空间的findByGoodsId的SQL中执行查询,
将得到的"商品详情"集合赋值给goodsDetails List对象.
-->
<collection property="goodsDetails" select="goodsDetail.selectByGoodsId"
column="goods_id"/> <!--column 关联列-->
</resultMap>
<!--对象关联的描述-->
<select id="selectOneToMany" resultMap="rmGoods1">
select * from t_goods limit 0,10
</select>
- 新建的goods_detail.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="goodsDetail">
<select id="selectByGoodsId" parameterType="Integer"
resultType="com.imooc.mybatis.entity.GoodsDetail">
select * from t_goods_detail where goods_id = #{value}
</select>
</mapper>
- 在mybatis-config.xml中,添加映射 标签
- 测试文件
/**
* 一对多对象关联查询
* @throws Exception
*/
@Test
public void testOneToMany() throws Exception {
SqlSession session = null;
try {
session = MyBatisUtils.openSession();
List<Goods> list = session.selectList("goods.selectOneToMany");
for(Goods goods:list) {
System.out.println(goods.getTitle() + ":" + goods.getGoodsDetails().size());
}
} catch (Exception e) {
throw e;
} finally {
MyBatisUtils.closeSession(session);
}
}
ManyToOne对象关联查询
(多的一方要关联到 一的一方,只需要持有 一的一方的实体即可):所以在多的一方的实体中添加一的一方的对象作为属性
利用 association 关联标签
1. goods_detail.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="goodsDetail">
<select id="selectByGoodsId" parameterType="Integer"
resultType="com.imooc.mybatis.entity.GoodsDetail">
select * from t_goods_detail where goods_id = #{value}
</select>
<!--多对一的结果映射-->
<resultMap id="rmGoodsDetail" type="com.imooc.mybatis.entity.GoodsDetail">
<id column="gd_id" property="gdId"/>
<result column="goods_id" property="goodsId"/>
<!--association 关联标签:从多的一方关联到一的一方
property="goods" :在GoodsDetail中引用的一的一方的对象作为的属性 -->
<association property="goods" select="goods.selectById" column="goods_id"></association>
</resultMap>
<!--对象关联的描述-->
<select id="selectManyToOne" resultMap="rmGoodsDetail">
select * from t_goods_detail limit 0,20
</select>
</mapper>
2. 测试文件
/**
* 测试多对一对象关联映射
*/
@Test
public void testManyToOne() throws Exception {
SqlSession session = null;
try {
session = MyBatisUtils.openSession();
List<GoodsDetail> list = session.selectList("goodsDetail.selectManyToOne");
for(GoodsDetail gd:list) {
System.out.println(gd.getGdPicUrl() + ":" + gd.getGoods().getTitle());
}
} catch (Exception e) {
throw e;
} finally {
MyBatisUtils.closeSession(session);
}
}
PageHelper分页插件
1. 分页查询的麻烦事
- 当前页数据查询 - select * from tab limit 0,10
- 总记录数查询 - select count(*) from tab
- 程序计算总页数、上一页页码、下一页页码
2. PageHelper使用流程
-
maven 引入 PageHelper 与 jsqlparser 依赖 (在pom.xml中)
<!--PageHelper 分页插件 引入PageHelper 与 jsqlparser --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.1.10</version> </dependency> <dependency> <!--jsqlparser 解释器--> <groupId>com.github.jsqlparser</groupId> <artifactId>jsqlparser</artifactId> <version>2.0</version> </dependency>
-
mybatis-config.xml 增加 Plugin 配置
<!--启用Pagehelper分页插件--> <plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"> <!--设置数据库类型 不同的数据库分页实现的原理不同 MySQL使用 limit--> <property name="helperDialect" value="mysql"/> <!--分页合理化--> <property name="reasonable" value="true"/> </plugin> </plugins>
-
代码中使用 PageHelper.startPage() 自动分页
@Test /** * PageHelper分页查询 */ public void testSelectPage() throws Exception { SqlSession session = null; try { session = MyBatisUtils.openSession(); /*startPage方法会自动将下一次查询进行分页*/ PageHelper.startPage(2,10);//分页范围:第二页,每页有10条数据 Page<Goods> page = (Page) session.selectList("goods.selectPage"); System.out.println("总页数:" + page.getPages()); System.out.println("总记录数:" + page.getTotal()); System.out.println("开始行号:" + page.getStartRow()); System.out.println("结束行号:" + page.getEndRow()); System.out.println("当前页码:" + page.getPageNum()); List<Goods> data = page.getResult();//当前页数据 for (Goods g : data) { System.out.println(g.getTitle()); } System.out.println(""); } catch (Exception e) { throw e; } finally { MyBatisUtils.closeSession(session); } }
3. 不同数据库分页的实现原理
- MySQL 分页
select * from table limit 10,20
<!--limit 的两个参数: 起始行号 从起始行号之后向后取多少条-->
- Oracle
select t3.* from(
<!--rownum 伪列,代表了当前的行号是什么-->
select t2.*,rownum as row_num from (
select* from table order by id asc <!--核心 ,外侧两层几乎是固定的-->
)t2 where rownum <= 20
)t3
where t2.row_num>11
- SQL Server 2000 (旧版)
select top 3 * from table
where
id not in
(select top 15 id from table)
<!--获取到的数据是 第16-18条数据 : 查询前15条数据,然后再查询除去这前15条数据后取前三条数据-->
- SQL Server 2012+ (2012后的版本,新版)
select * from table order by id
offset 4 rows fetch next 5 rows only
<!--offset:偏移量 偏移4行,即起始行号从第5条开始,
fetch next:向后取多少行-->
MyBatis 配置C3P0连接池
-
在pom.xml中添加c3p0的依赖
<dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.4</version> </dependency>
-
创建 C3P0DataSourceFactory 类,继承 UnpooledDataSourceFactory 该类
/** * C3P0与MyBatis兼容使用的数据源工厂类 */ public class C3P0DataSourceFactory extends UnpooledDataSourceFactory { public C3P0DataSourceFactory(){ this.dataSource = new ComboPooledDataSource(); } }
-
在 mybatis-config.xml 中添加配置环境
<!--配置环境,不同的环境不同的id名字--> <environment id="dev"> <!-- 采用JDBC方式对数据库事务进行commit/rollback --> <transactionManager type="JDBC"></transactionManager> <!--采用C3P0连接池方式管理数据库连接 此处的type为自己所建的 C3P0DataSourceFactory 类--> <dataSource type="com.imooc.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="myt12345"/> <property name="initialPoolSize" value="5"/> <property name="maxPoolSize" value="20"/> <property name="minPoolSize" value="5"/> <!--...--> </dataSource>
MyBatis 批处理
1. 批量插入
-
在goods.xml中 批量插入多条数据
<!--批处理--> <!--INSERT INTO table--> <!--VALUES ("a" , "a1" , "a2"),("b" , "b1" , "b2"),(....)--> <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=","> <!--collection:代表迭代的数据源从哪来,为list,不能修改 item :迭代变量 index:循环索引 separator:每一个数据之间的分割符,用逗号进行分割--> (#{item.title},#{item.subTitle}, #{item.originalCost}, #{item.currentPrice}, #{item.discount}, #{item.isFreeDelivery}, #{item.categoryId}) </foreach> </insert>
-
测试文件
/** * 批量插入测试 * @throws Exception */ @Test public void testBatchInsert() throws Exception { SqlSession session = null; try { long st = new Date().getTime(); session = MyBatisUtils.openSession(); List list = new ArrayList(); for (int i = 0; i < 10000; 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); //insert()方法返回值代表本次成功插入的记录总数 list.add(goods); } session.insert("goods.batchInsert", list); session.commit();//提交事务数据 long et = new Date().getTime(); System.out.println("执行时间:" + (et - st) + "毫秒"); // System.out.println(goods.getGoodsId()); } catch (Exception e) { if (session != null) { session.rollback();//回滚事务 } throw e; } finally { MyBatisUtils.closeSession(session); } }
-
批量插入的局限
- 无法获得插入数据的 id
- 批量生成的SQL太长,可能会被服务器拒绝
2. 批量删除
-
在goods.xml 中
<!--批量删除--> <!--in (1901,1902)--> <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} <!--集合中不再是实体,而只是goods_id编号--> </foreach> </delete>
-
测试文件
/** * 批量删除测试 * @throws Exception */ @Test public void testBatchDelete() throws Exception { SqlSession session = null; try { long st = new Date().getTime(); session = MyBatisUtils.openSession(); List list = new ArrayList(); list.add(1920); list.add(1921); list.add(1922); session.delete("goods.batchDelete", list); session.commit();//提交事务数据 long et = new Date().getTime(); System.out.println("执行时间:" + (et - st) + "毫秒"); // System.out.println(goods.getGoodsId()); } catch (Exception e) { if (session != null) { session.rollback();//回滚事务 } throw e; } finally { MyBatisUtils.closeSession(session); } }
MyBatis 注解开发
接口+注解 来替代xml文件
-
常用注解
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M4xrqDMr-1601385394502)(/C:/Users/tao/AppData/Roaming/Typora/typora-user-images/image-20200916112539801.png)]
**即,在接口中添加参数, 设置注解. 在参数上添加 @Param ,就可以在执行接口方法时,能够将设置的参数与注解的参数一一对应,完成传值的操作。 **
第一种:在mybatis-config.xml 中,添加mapper标签,
第二种:或者在mybatis-config.xml 中,添加 package 标签 (建议采用这种方式进行配置)