MyBatis进阶

本文详细介绍了MyBatis的二级缓存配置、运行规则以及如何进行一级和二级缓存的管理。同时,讲解了一对多和多对一的对象关联查询,包括在映射文件中的配置和测试用例。此外,还讨论了PageHelper分页插件的使用,以及不同数据库的分页实现原理。最后提到了MyBatis的批处理操作以及C3P0连接池的配置。
摘要由CSDN通过智能技术生成

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属性

  1. 在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>
  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>
</mapper>
  1. 在mybatis-config.xml中,添加映射 标签
  2. 测试文件
/**
     * 一对多对象关联查询
     * @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连接池

  1. 在pom.xml中添加c3p0的依赖

            <dependency>
                <groupId>com.mchange</groupId>
                <artifactId>c3p0</artifactId>
                <version>0.9.5.4</version>
            </dependency>
    
  2. 创建 C3P0DataSourceFactory 类,继承 UnpooledDataSourceFactory 该类

    /**
     * C3P0与MyBatis兼容使用的数据源工厂类
     */
    public class C3P0DataSourceFactory extends UnpooledDataSourceFactory {
        public C3P0DataSourceFactory(){
            this.dataSource = new ComboPooledDataSource();
        }
    }
    
  3. 在 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&amp;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. 批量插入

  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>
    
  2. 测试文件

        /**
         * 批量插入测试
         * @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);
            }
        }
    
  3. 批量插入的局限

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

2. 批量删除

  1. 在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>
    
  2. 测试文件

        /**
         * 批量删除测试
         * @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文件

  1. 常用注解

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M4xrqDMr-1601385394502)(/C:/Users/tao/AppData/Roaming/Typora/typora-user-images/image-20200916112539801.png)]

**即,在接口中添加参数, 设置注解. 在参数上添加 @Param ,就可以在执行接口方法时,能够将设置的参数与注解的参数一一对应,完成传值的操作。 **

第一种:在mybatis-config.xml 中,添加mapper标签,

第二种:或者在mybatis-config.xml 中,添加 package 标签 (建议采用这种方式进行配置)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值