MyBatis之进阶知识

MyBatis进阶知识笔记

MyBatis日志管理
<!-- 依赖添加 -->
<dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>
<!-- 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:程序运行的跟踪信息-->
    <root level="debug">
        <appender-ref ref="console"/>
    </root>
</configuration>
动态SQL
<!-- goods.xml文件内容 -->
<select id="dynamicSQL" parameterType="java.util.Map" resultType="com.imooc.mybatis.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;
        try {
            session = MyBatisUtils.openSession();
            Map param = new HashMap();
            param.put("categoryId", 44);
            param.put("currentPrice", 500);
            List<Goods> list = session.selectList("goods.dynamicSQL", param);
            for (Goods g : list){
                System.out.println(g.getTitle() + ":" + g.getCategoryId() + ":" + g.getCurrentPrice());
            }

        } catch (Exception e) {
            throw e;
        } finally {
            MyBatisUtils.closeSession(session);
        }
    }
MyBatis二级缓存

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

缓存的范围

截图来自慕课网

二级缓存的运行规则

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

<!-- 在goods.xml的<mapper>中添加缓存的标签 -->
<!-- 开启二级缓存 -->
    <cache eviction="LRU" flushInterval="600000" size="512" readOnly="true"/>

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 , 代表每次取出的是缓存对象的"副本",每一次取出的对象都是不同的,这种安全性较高
注: 当查询返回值为列表时,建议使用设置useCache=false 来取消缓存使用,因为列表的缓存命中率并不高。

多表级联查询

1<–>1 通常通过主键进行关联
n<–>n 在数据库设计的时候需要抽象出一张中间表,中间表中只有另外两张表的主键,另外两张表与中间表关联,就可以构建出多对多的关系。

OneToMany对象关联查询

实现查询一个商品,并查询出这个商品的所有商品详情。

  1. 写出商品详情的实体类
  2. 在商品的实体类中添加商品详情的列表属性
  3. 编写goods_detial.xml配置文件,并在里面实现通过商品id来查询商品详情的sql语句,最后添加到mybatis-config.xml的配置文件中。
  4. 在goods.xml中添加ResultMap并编写查询语句
  5. 编写测试类方法
// 1. 写出商品详情的实体类
public class GoodsDetail {
    private Integer gdId;
    private Integer goodsId;
    private String gdPicUrl;
    private Integer gdOrder;

    public Integer getGdId() {
        return gdId;
    }

    public void setGdId(Integer gdId) {
        this.gdId = gdId;
    }

    public Integer getGoodsId() {
        return goodsId;
    }

    public void setGoodsId(Integer goodsId) {
        this.goodsId = goodsId;
    }

    public String getGdPicUrl() {
        return gdPicUrl;
    }

    public void setGdPicUrl(String gdPicUrl) {
        this.gdPicUrl = gdPicUrl;
    }

    public Integer getGdOrder() {
        return gdOrder;
    }

    public void setGdOrder(Integer gdOrder) {
        this.gdOrder = gdOrder;
    }
}

// 2. 在商品的实体类中添加商品详情的列表属性
// 一个goods可以有多个goods_detail,所以用列表来保存
    private List<GoodsDetail> goodsDetails;
<!-- 3. 编写goods_detial.xml配置文件,并在里面实现通过商品id来查询商品详情的sql语句,最后添加到mybatis-config.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 -->
<mapper resource="mappers/goods_detail.xml"/>

<!-- 4. 在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命名空间的selectByGoodsId的sQL中执行查询,
        将得到的"商品详情"集合赋值给goodsDetails List对象.-->

        <collection property="goodsDetails" select="goodsDetail.selectByGoodsId"
                    column="goods_id"/>
    </resultMap>
    <select id="selectOneToMany" resultMap="rmGoods1">
        select * from t_goods limit 0,10
    </select>
// 5. 编写测试类方法
	@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 对象关联查询

实现通过查询商品详情来返回商品的标题

  1. 在商品详情的实体类中添加商品类的属性
  2. 编写goods_detial.xml中的查询语句,并添加ResultMap将结果映射到GoodsDetail中的goods变量
  3. 编写测试类方法
// 1. 在商品详情的实体类中添加商品类的属性
private Goods goods;
<!-- 2. 编写goods_detial.xml中的查询语句,并添加ResultMap将结果映射到GoodsDetail中的goods变量 -->
<resultMap id="rmGoodsDetail" type="com.imooc.mybatis.entity.GoodsDetail">
        <id column="gd_id" property="gdId"/>
        <!-- 因为association标签默认将goods_id返回给goods类,需要添加配置result标签来让goodsId被赋值-->
        <result column="goods_id" property="goodsId"/>
        <!-- 因为多的一方持有的是一的一方的实体,所以使用association关联标签,selectById查询语句在goods命名空间中已经实现 -->
        <association property="goods" select="goods.selectById" column="goods_id"></association>
    </resultMap>
    <select id="selectManyToOne" resultMap="rmGoodsDetail">
        select * from t_goods_detail limit 15,20
    </select>
// 3. 编写测试类方法
	@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分页插件工具

使用流程:
maven引入PageHelper与jsqlparser
mybatis-config.xml增加Plugin配置
代码中使用PageHelper.startPage()自动分页
helperDialect: 分页插件会自动检测当前的数据库链接,自动选择合适的分页方式。 你可以配置helperDialect属性来指定分页插件使用哪种方言.
reasonable: 分页合理化参数,默认值为false。当该参数设置为 true 时,pageNum<=0 时会查询第一页, pageNum>pages(超过总数时),会查询最后一页。默认false 时,直接根据参数进行查询.

<!-- maven依赖代码 -->
<dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.3.1</version>
        </dependency>
        <dependency>
            <groupId>com.github.jsqlparser</groupId>
            <artifactId>jsqlparser</artifactId>
            <version>4.4</version>
        </dependency>

<!-- 在mybatis-config.xml中启用插件 -->
<plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
            <property name="helperDialect" value="mysql"/>
            <property name="reasonable" value="true"/>
        </plugin>
    </plugins>

<!-- 在goods.xml中添加查询语句 -->
<select id="selectPage" resultType="com.imooc.mybatis.entity.Goods">
        select * from t_goods where current_price &lt; 1000
    </select>
// 测试类文件内容
@Test
    public void testSelectPage() throws Exception {
        SqlSession session = null;
        try {
            session = MyBatisUtils.openSession();
            /* startPage方法会自动将下一次查询进行分页 */
            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());
            System.out.println("当前页码:" + page.getPageNum());
            List<Goods> data = page.getResult(); // 当前页数据
            for (Goods g : data){
                System.out.println(g.getTitle());
            }

        } catch (Exception e) {
            throw e;
        } finally {
            MyBatisUtils.closeSession(session);
        }
    }
MyBatis批处理
<!-- 批量插入数据 -->
<!-- INSERT INTO t_goods (title, sub_title, original_cost, current_price, discount, is_free_delivery, category_id)
        VALUES 
        (?, ?, ?, ?, ?, ?, ?),(?, ?, ?, ?, ?, ?, ?),(?, ?, ?, ?, ?, ?, ?),(?, ?, ?, ?, ?, ?, ?),(?, ?, ?, ?, ?, ?, ?), ...  -->
    <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
        /* 遍历传入的列表类似for循环 */
        <foreach collection="list" item="item" index="index" separator=",">
            (#{item.title}, #{item.subTitle}, #{item.originalCost}, #{item.currentPrice}, #{item.discount}, #{item.isFreeDelivery}, #{item.categoryId})
        </foreach>
    </insert>

<!-- 批量删除数据 -->
<!-- DELETE FROM t_goods WHERE goods_id in ( ? , ? , ? , ...) -->
    <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 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);
                list.add(goods);
            }

            // insert()方法返回值代表本次成功插入的记录总数
            int num = session.insert("goods.batchInsert", list);
            session.commit(); // 提交事务数据
            long et = new Date().getTime();
            System.out.println("执行时间:" + (et - st) + "毫秒");
        } catch (Exception e) {
            if (session != null){
                session.rollback(); // 回滚事务
            }
            throw e;
        } finally {
            MyBatisUtils.closeSession(session);
        }
    }

    @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) + "毫秒");
        } catch (Exception e) {
            if (session != null){
                session.rollback(); // 回滚事务
            }
            throw e;
        } finally {
            MyBatisUtils.closeSession(session);
        }
    }
MyBatis注解开发
  1. 实现sql功能接口
  2. 在mybatis-config.xml中配置映射
  3. 编写测试代码

常用注解类型
截图来自慕课网

// 1. 实现sql功能接口
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) VALUES (#{title}, #{subTitle}, #{originalCost}, #{currentPrice}, #{discount}, #{isFreeDelivery})")
    @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>
            @Result(column = "title", property = "title"),
            @Result(column = "current_price", property = "currentPrice")
    })
    public  List<GoodsDTO> selectAll();
}

<!-- 2. 在mybatis-config.xml中配置映射 -->
<mappers>
        <!--<mapper class="com.imooc.mybatis.dao.GoodsDAO"/>-->
        <package name="com.imooc.mybatis.dao"/>
    </mappers>
// 3. 编写测试代码
@Test
    public void testSelectByPriceRange() throws Exception {
        SqlSession session = null;
        try {
            session = MyBatisUtils.openSession();
            GoodsDAO goodsDAO = session.getMapper(GoodsDAO.class);
            List<Goods> list = goodsDAO.selectByPriceRange(100f, 500f, 20);
            for (Goods g : list){
                System.out.println(g.getTitle());
            }

        } catch (Exception e) {
            throw e;
        } finally {
            MyBatisUtils.closeSession(session);
        }
    }

@Test
    public void testInsert() throws Exception {
        SqlSession session = null;
        try {
            session = MyBatisUtils.openSession();
            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 goodsDAO = session.getMapper(GoodsDAO.class);
            // insert()方法返回值代表本次成功插入的记录总数
            int num = goodsDAO.insert(goods);
            session.commit(); // 提交事务数据
            System.out.println(goods.getGoodsId());
        } catch (Exception e) {
            if (session != null){
                session.rollback(); // 回滚事务
            }
            throw e;
        } finally {
            MyBatisUtils.closeSession(session);
        }
    }

@Test
    public void testSelectAll() throws Exception {
        SqlSession session = null;
        try {
            session = MyBatisUtils.openSession();
            GoodsDAO goodsDAO = session.getMapper(GoodsDAO.class);
            List<GoodsDTO> list = goodsDAO.selectAll();
            System.out.println(list.size());

        } catch (Exception e) {
            throw e;
        } finally {
            MyBatisUtils.closeSession(session);
        }
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值