美文网首页
MyBatis高级特性

MyBatis高级特性

作者: Vicent_Z | 来源:发表于2023-06-25 09:43 被阅读0次

    1.日志

    用于记录系统操作记录,保存历史数据,诊断问题

    1.1.SLF4j与Logback

    日志门面:统一的对外接口

    1-1.jpg

    pom中插入依赖

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

    resources下面添加logback.xml日志配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
        <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
            <encoder>
                <!-- %thread对应的线程 %-5表示按5个字符左对齐
                %logger表示哪个类产生的日志 %msg日志具体输出,%n换行-->
                <pattern>[%thread] %d{HH:mm:ss.SSS}  %-5level %logger{36} - %msg%n</pattern>
            </encoder>
        </appender>
        <!--
                日志输出级别(高-低)
                error:错误 - 系统的故障日志
                warn:警告 - 存在风险或者使用不当的日志
                info: 一般性信息
                debug: 程序内部用于调试的信息
                trace:程序运行的跟踪信息
           -->
        <root level="debug">
            <appender-ref ref="console"/>
        </root>
    </configuration>
    

    2.动态SQL

    根据参数数据动态组织SQL

    goods.xml:

    <!--    动态SQL-->
    <select id="dynamicSQL" parameterType="java.util.Map" resultType="org.example.entity.Goods">
        select * from t_goods
        <where>
            <if test="categoryId != null">
                and category_id = #{categoryId}
            </if>
            <if test="currentPrice != null">
                and current_price &lt; #{currentPrice}
            </if>
        </where>
    </select>
    

    调用:

    @Test
    public void testDynamicSQL() throws Exception {
        SqlSession session = null;
        session = MyBatisUtils.openSession();
        Map param = new HashMap();
        param.put("category", 44);
        param.put("currentPrice", 500);
        //可以不加namespace,确保id是唯一即可
        List<Goods> list = session.selectList("goods.dynamicSQL",param);
        for (int i = 0; i < list.size(); i++) {
            Goods goods =  list.get(i);
            System.out.println(goods.getTitle());
        }
        MyBatisUtils.closeSession(session);
    }
    

    3.缓存

    主要是MyBatis二级缓存

    • 一级缓存默认开启,缓存范围SqlSession会话
    • 二级缓存手动开启,缓存范围是Mapper Namespace
    1-2.jpg

    3.1.二级缓存运行规则

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

    一级缓存的测试代码:

    @Test
    public void testLv1Cache() {
        SqlSession session = null;
        session = MyBatisUtils.openSession();
        Goods goods = session.selectOne("goods.selectById",1603);
        Goods goods1 = session.selectOne("goods.selectById",1603);
        System.out.println("goods hashCode:" + goods.hashCode() + "-goods1 hashCode:" +  goods1.hashCode());
        MyBatisUtils.closeSession(session);
    
    
    
        SqlSession session1 = null;
        session1 = MyBatisUtils.openSession();
        Goods goods2 = session1.selectOne("goods.selectById",1603);
        Goods goods3 = session1.selectOne("goods.selectById",1603);
    
        System.out.println("goods2 hashCode:" + goods2.hashCode() + "-goods3 hashCode:" +  goods3.hashCode());
        MyBatisUtils.closeSession(session1);
    }
    

    运行结果:

    goods hashCode:574268151-goods1 hashCode:574268151
    goods2 hashCode:423583818-goods3 hashCode:423583818
    

    可以看到同一个session的hashCode相同,说明是同一个对象,并且debug日志也只会执行一次sql查询,如果在goods和goods1之间插入session.commit()方法,则goods和goods1则会是两个不同的对象。

    二级缓存测试代码:

    goods.xml中添加
    
    <!--    开启二级缓存-->
    <cache eviction="LRU" flushInterval="600000" readOnly="true"/>
    
    测试代码与一级缓存一样不变
    

    运行结果:

    goods hashCode:270095066-goods1 hashCode:270095066
    goods2 hashCode:270095066-goods3 hashCode:270095066
    

    说明开启了二级缓存后,不同session也会公用一个缓存数据。

    3.2.二级缓存参数说明

    二级缓存相关:

    <mapper namespace="goods">
        <!--
            开启二级缓存
            eviction是缓存的清除策略,当缓存对象数量达到上限后,自动出发对应算法对缓存对象清除
            1.LRU-最近最久未使用:移除最长时间不被使用的对象
            O1 O2 ... O512
            14 99     893
            则会移除O512的对象,因为它举例上次使用时间最长
            2.LFU-最近最少使用:移除最近访问频率最低的对象
            3.FIFO-先进先出:按对象进入缓存的顺序来移除
            4.SOFT-软引用:移除基于垃圾收集器状态和软引用规则的对象
            5.WEAK-弱引用:更积极的移除基于垃圾收集器状态和弱引用规则的对象
    
            3,4,5很少使用。
    
            flushInterval 代表间隔多长时间自动清空缓存,单位毫秒,600000毫秒=10分钟
            size 缓存的存储上限,用于保存对象或集合(1个集合算1个对象)的数量上限
            readOnly 设置为true,代表返回只读缓存,每次从缓存取出的是缓存对象本身,执行效率高
                     设置为false,代表每次取出的是缓存对象的副本,每次取出对象是不同的,安全性高。
        -->
        <cache eviction="LRU" flushInterval="600000" readOnly="true"/>
    ...
    

    单条相关:

        <!--    useCache="false"代表不使用缓存-->
         <select id="selectAll" resultType="org.example.entity.Goods" useCache="false">
             select * from t_goods order by goods_id desc limit 10
         </select>
    
        ----------
        
        <!--    flushCache="true"在sql执行后强制清空缓存,同时本条sql执行结果也不会缓存-->
        <insert id="insert"
                parameterType="org.example.entity.Goods"
                useGeneratedKeys="true"
                keyProperty="goodsId"
                keyColumn="goods_id"
                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})
        </insert>
    

    4.多表级联查询

    1-3.jpg

    4.1.一对多查询

    商品与商品图片详情就是1对多的关系

    开发步骤:

    4.1.1.resources下创建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="org.example.entity.GoodsDetail">
            select * from t_goods_detail where goods_id = #{value}
        </select>
    </mapper>
    

    4.1.2.goods.xml中配置一对多

    <!--
        resultMap可用于说明一对多或者多对一的映射逻辑
        id是resultMap属性引用的标志
        type指向One的实体(Goods)
    -->
    <resultMap id="rmGoods1" type="org.example.entity.Goods">
        <!--
            映射goods对象的主键到goods_id字段
            由于其他字段都能与数据库对应上,所以不用再次列举
        -->
        <id column="goods_id" property="goodsId"></id>
        <collection property="goodsDetails" select="goodsDetail.selectByGoodsId"
                    column="goods_id"/>
    </resultMap>
    <!--    一对多查询-->
    <select id="selectOneToMany" resultMap="rmGoods1">
        select * from t_goods limit 0,1
    </select>
    

    4.1.3.mybatis-config.xml中加入应用

    加入对goods_detail.xml的引用

    <mapper resource="mappers/goods_detail.xml"/>
    

    4.1.4.调用测试

    @Test
    public void testOneToMany(){
        SqlSession session = null;
        session = MyBatisUtils.openSession();
        List<Goods> list = session.selectList("goods.selectOneToMany");
        for (int i = 0; i < list.size(); i++) {
            Goods goods =  list.get(i);
            System.out.println(goods.getTitle() + " : " +goods.getGoodsDetails().size());
        }
        MyBatisUtils.closeSession(session);
    }
    

    4.2.多对一查询

    商品图片详情与商品是多对一

    开发步骤:

    4.2.1.mapper.xml配置

    goods_detail.xml中添加多对一查询语句和resultMap:

    <!--    多表对应查询实体的resultMap-->
    <resultMap id="rmGoodsDetail" type="org.example.entity.GoodsDetail">
        <!--        多表的主键-->
        <id column="gd_id" property="gdId"></id>
        <!--        多表的外键,一表的主键-->
        <result column="goods_id" property="goodsId"/>
        <!--        对表对应的selectById查询语句-->
        <association property="goods" select="goods.selectById" column="goods_id"></association>
    </resultMap>
    
    <!--    多表的查询语句-->
    <select id="selectManyToOne" resultMap="rmGoodsDetail">
        select * from t_goods_detail limit 0,10
    </select>
    

    4.2.2.调用

    @Test
    public void testManyToOne(){
        SqlSession session = null;
        session = MyBatisUtils.openSession();
        List<GoodsDetail> list = session.selectList("goodsDetail.selectManyToOne");
        for (int i = 0; i < list.size(); i++) {
            GoodsDetail goodsDetail =  list.get(i);
            if (goodsDetail.getGoods() == null)
                continue;
            System.out.println(goodsDetail.getGdPicUrl() + " : " + goodsDetail.getGoods().getTitle());
        }
        MyBatisUtils.closeSession(session);
    
    }
    

    5.分页PageHelper

    原理:

    • 当前数据查询使用语句

    select * from tab limit 0,10

    • 总记录数查询

    select count(*) from tab

    • 程序计算总页数、当前页、上一页下一页码

    5.1.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配置

        <!--    启用PageHelper分页插件-->
        <plugins>
            <plugin interceptor="com.github.pagehelper.PageInterceptor">
                <!--            设置数据库类型-->
                <property name="helperDialect" value="mysql"/>
                <!--            分页合理化,页数小于查询第一页,大于总页数则显示最后一页-->
                <property name="reasonable" value="true"/>
            </plugin>
        </plugins>
      
    • mapper.xml中添加查询sql

        <!--    查询价格小于1000的数据-->
        <select id="selectPage" resultType="org.example.entity.Goods">
            select * from t_goods where current_price &lt; 1000
        </select>
    
    • 代码中使用PageHelper.startPage()自动分页
        @Test
        public void testPageHelper(){
            SqlSession session = null;
            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.getStartRow());
            System.out.println("当前页码:" + page.getEndRow());
            List<Goods> data = page.getResult();//当前页数据
            for (int i = 0; i < data.size(); i++) {
                Goods goods =  data.get(i);
                System.out.println(goods.getTitle());
            }
            MyBatisUtils.closeSession(session);
        }
    
    
        --结果
    
        总页数:181
        总记录数:1810
        开始行号:10
        当前页码:20
        康泰 家用智能胎心仪 分体探头操作方便 外放聆听 与家人分享宝宝心声
    

    6.C3P0连接池

    配置流程:

    • maven引入c3p0的引用

        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.4</version>
        </dependency>
      
    • 创建C3P0DataSourceFactory

        //C3P0与MyBatis兼容使用的数据源工厂类
        public class C3P0DataSourceFactory extends UnpooledDataSourceFactory {
            public C3P0DataSourceFactory(){
                this.dataSource = new ComboPooledDataSource();
            }
        }
      
    • mybatis.xml中引入datasource配置

            <!--            C3P0连接池-->
            <dataSource type="org.example.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="111111"/>
                <property name="initialPoolSize" value="5"/>
                <property name="maxPoolSize" value="20"/>
                <property name="minPoolSize" value="5"/>
            </dataSource>
    

    剩下的不便

    7.MyBatis批处理

    在mapper.xml中使用foreach标签

    <!--    insert into table-->
    <!--    values("a","a1","a2"),("a","a1","a2"),(....)-->
    <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>
    

    调用:

    @Test
    //分别插入
    public void testBatchInsert1(){
        SqlSession session = null;
        session = MyBatisUtils.openSession();
        long st = new Date().getTime();
        for (int i = 0; i < 1000; 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);
            session.insert("goods.insert", goods);
        }
        session.commit();
        long et = new Date().getTime();
        System.out.println("执行时间:" + (et - st) + "毫秒");
        MyBatisUtils.closeSession(session);
    }
    
    @Test
    //批量插入
    public void testBatchInsert2(){
        SqlSession session = null;
        session = MyBatisUtils.openSession();
        long st = new Date().getTime();
        List list = new ArrayList();
        for (int i = 0; i < 1000; 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();
        long et = new Date().getTime();
        System.out.println("执行时间:" + (et - st) + "毫秒");
        MyBatisUtils.closeSession(session);
    }
    

    分别插入的时间:

    执行时间:976毫秒

    批处理插入的时间:

    执行时间:679毫秒

    由此可见批处理的效率很高

    批量删除:

    <!-- 批量删除 (1, 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>
    
    --调用
    
    @Test
    //批量删除
    public void testBatchDelete(){
        SqlSession session = null;
        session = MyBatisUtils.openSession();
        long st = new Date().getTime();
        List list = new ArrayList();
        for (int i = 4670; i <= 4677; i++) {
            list.add(i);
        }
        session.insert("goods.batchDelete",list);
        session.commit();
        long et = new Date().getTime();
        System.out.println("执行时间:" + (et - st) + "毫秒");
        MyBatisUtils.closeSession(session);
    }
    

    8.注解

    mapper可以通过注解方式配置

    新建GoodsDAO:

    public interface GoodsDAO {
    
        @Select("select * from t_goods where current_price between #{min} and #{max} order by current_price limit 0,#{limit}")
        public List<Goods> selectByPriceRange(@Param("min") Float min, @Param("max") Float max, @Param("limit") Integer limit);
    
        @Insert("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(statement = "select last_insert_id()", before = false, keyProperty = "goodsId", resultType = Integer.class)
        int insert(Goods goods);
    
        @Select("select * from t_goods")
        //配置返回值map
        @Results({
                @Result(column = "goods_id", property = "goodsId", id = true),
                @Result(column = "current_price", property = "currentPrice")
        })
        List<GoodsDTO> selectAll();
    }
    

    mybatis-config.xml中配置dao

    <mappers>
    <!--        对应的sql mapper映射类或者包-->
    <!--        <mapper class="org.example.dao.GoodsDAO"/>-->
        <package name="org.example.dao"/>
    </mappers>
    

    调用:

    @Test
    public void testSelect(){
        SqlSession session = null;
        session = MyBatisUtils.openSession();
        GoodsDAO goodsDAO = session.getMapper(GoodsDAO.class);
        List<Goods> list = goodsDAO.selectByPriceRange(100f, 500f, 20);
        System.out.println(list.size());
        MyBatisUtils.closeSession(session);
    }
    
    @Test
    public void testInsert(){
        SqlSession session = null;
        session = MyBatisUtils.openSession();
        GoodsDAO goodsDAO = session.getMapper(GoodsDAO.class);
        Goods goods = new Goods();
        goods.setTitle("测试商品");
        goods.setSubTitle("测试子标题");
        goods.setOriginalCost(200f);
        goods.setCurrentPrice(100f);
        goods.setDiscount(0.5f);
        goods.setIsFreeDelivery(1);
        goods.setCategoryId(43);
        goodsDAO.insert(goods);
        session.commit();
        MyBatisUtils.closeSession(session);
    }
    
    @Test
    public void testSelectAll(){
        SqlSession session = null;
        session = MyBatisUtils.openSession();
        GoodsDAO goodsDAO = session.getMapper(GoodsDAO.class);
        List<GoodsDTO> goodsDTOS = goodsDAO.selectAll();
        System.out.println(goodsDTOS.size());
        MyBatisUtils.closeSession(session);
    }

    相关文章

      网友评论

          本文标题:MyBatis高级特性

          本文链接:https://www.haomeiwen.com/subject/qtsiydtx.html