精华内容
下载资源
问答
  • Mybatis高级特性(三)

    2021-07-07 15:27:04
    Mybatis多表级联查询(一对多) 1.创建goods_detail.xml文件 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" ...

    Mybatis多表级联查询(一对多)

    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.mybatis.entity.GoodsDetail">
            SELECT * FROM t_goods_detail WHERE goods_id = #{value}
        </select>
    </mapper>

    2.在Goods实体类中增加商品详情的字段

    @Data
    public class Goods {
    
        private Integer goodsId;//商品编号
        private String title;//标题
        private String subTitle;//子标题
        private Float originalCost;//原始价格
        private Float currentPrice;//当前价格
        private Float discount;//折扣率
        private Integer isFreeDelivery;//是否包邮 ,1-包邮 0-不包邮
        private Integer categoryId;//分类编号
    
        private List<GoodsDetail> goodsDetails;
    }
    

    3.创建GoodsDetail实体类

    @Data
    public class GoodsDetail {
    
        private Integer gdId;
    
        private Integer goodsId;
    
        private String gdPicUrl;
    
        private Integer gdOrder;
    
    }

    4.在goods.xml中增加相关的SQL语句

    <resultMap id="rmGoods1" type="com.mybatis.entity.Goods">
            <id column="goods_id" property="goodsId"></id>
            <collection property="goodsDetails" select="goodsDetail.selectByGoodsId" column="goods_id"></collection>
        </resultMap>
    
        <select id="selectOneToMany" resultMap="rmGoods1">
            SELECT * FROM  t_goods LiMIT 0,3
        </select>

    5.编写测试类

    @Test
        public void testOneToMany() {
            SqlSession sqlSession = null;
            try {
                sqlSession = MybatisUtils.openSqlSession();
                List<Goods> list2 = sqlSession.selectList("goods.selectOneToMany");
                for (Goods goods : list2) {
                    System.out.println(goods.getTitle()+":"+goods.getGoodsDetails().toString());
                }
            } catch (Exception e) {
                throw e;
            } finally {
                MybatisUtils.closeSqlSession(sqlSession);
            }
        }

    Mybatis多表级联查询(多对一)

    在上述代码的基础上

    1.在GoodsDetail实体类中增加Goods字段

    @Data
    public class GoodsDetail {
    
        private Integer gdId;
    
        private Integer goodsId;
    
        private String gdPicUrl;
    
        private Integer gdOrder;
    
        private Goods goods;
    
    }

    2.在goods_detail.xml中增加多对一的SQL语句配置

    <resultMap id="rmGoodsDetail" type="com.mybatis.entity.GoodsDetail">
            <id column="gd_id" property="gdId"></id>
            <result column="goods_id" property="goodsId"></result>
            <association property="goods" select="goods.selectById" column="goods_id"></association>
        </resultMap>
        
        <select id="selectManyToOne" resultMap="rmGoodsDetail">
            SELECT * FROM t_goods_detail Limit 0,1
        </select>

    3.在goods.xml文件中增加根据Id查询的配置

    <select id="selectById" parameterType="Integer" resultType="com.mybatis.entity.Goods">
            SELECT * FROM t_goods WHERE goods_id = #{value}
        </select>

    4.编写测试类

    @Test
        public void testManyToOne() {
            SqlSession sqlSession = null;
            try {
                sqlSession = MybatisUtils.openSqlSession();
                List<GoodsDetail> list2 = sqlSession.selectList("goodsDetail.selectManyToOne");
                for (GoodsDetail goodsDetail : list2) {
                    System.out.println(goodsDetail.getGoods().getTitle());
                }
            } catch (Exception e) {
                throw e;
            } finally {
                MybatisUtils.closeSqlSession(sqlSession);
            }
        }
    展开全文
  • Mybatis高级特性(二)

    2021-07-06 14:17:35
    Mybatis二级缓存 一级缓存默认开启,缓存范围SqlSession会话 二级缓存手动开启,属于范围Mapper Namespace,二级缓存开启之后,默认所有查询操作均使用缓存,写操作commit提交对该namespace缓存强制清空。 可以...

    Mybatis二级缓存

    一级缓存默认开启,缓存范围SqlSession会话

    二级缓存手动开启,属于范围Mapper Namespace,二级缓存开启之后,默认所有查询操作均使用缓存,写操作commit提交对该namespace缓存强制清空。

    可以配置userCache=false

    配置flushCache=true代表强制清空缓存

    一级缓存

    直接上代码

    @Test
        public void testLv1Cache() {
    
            SqlSession sqlSession = null;
            try {
                sqlSession = MybatisUtils.openSqlSession();
                List<Goods> list1 = sqlSession.selectList("goods.selectAll");
                List<Goods> list2 = sqlSession.selectList("goods.selectAll");
                for (Goods goods : list1) {
                    System.out.println(goods.getTitle());
                }
                for (Goods goods : list2) {
                    System.out.println(goods.getTitle());
                }
            } catch (Exception e) {
                throw e;
            } finally {
                MybatisUtils.closeSqlSession(sqlSession);
            }

     以上代码中,对于列表查询了两次,但是在日志打印中,sql语句只执行了一次。说明第二次的查询结果是从缓存中读取的,以上代码中的list1和list2的hashcode()的值也相同,如果在List<Goods> list1 = sqlSession.selectList("goods.selectAll");之后增加sqlSession.commit,会对缓存进行强制清空。

    二级缓存

    开启二级缓存

    在goods.xml文件中添加如下代码,即可开启mybatis的二级缓存

    <cache eviction="LRU" flushInterval="600000" size="512" readOnly="true"/>

    编写测试类

    @Test
        public void testLv2Cache1() {
    
            SqlSession sqlSession = null;
            try {
                sqlSession = MybatisUtils.openSqlSession();
                List<Goods> list1 = sqlSession.selectList("goods.selectAll");
                for (Goods goods : list1) {
                    System.out.println(goods.getTitle());
                }
            } catch (Exception e) {
                throw e;
            } finally {
                MybatisUtils.closeSqlSession(sqlSession);
            }
    
            try {
                sqlSession = MybatisUtils.openSqlSession();
                List<Goods> list2 = sqlSession.selectList("goods.selectAll");
                for (Goods goods : list2) {
                    System.out.println(goods.getTitle());
                }
            } catch (Exception e) {
                throw e;
            } finally {
                MybatisUtils.closeSqlSession(sqlSession);
            }
        }

    二级缓存的关键字的相关概念

    eviction:是缓存清楚策略,当缓存对象数量达到上限之后,自动触发对应算法对缓存对象进行清除

    LRU:移除最长时间不被使用的对象

    LFU:移除访问频率最低的字段

    FIFO:先进先出,按对象进入缓存的顺序来移除它们

     flushInterval:代表隔多长时间自动清除缓存,单位是毫秒,可以对内存及时有效的进行回收

    size:缓存的长度,二级缓存最多可以缓存多少个对象

    readOnly

    设置为true的时候,代表返回只读缓存,每次从缓存中读取出来的对象都是缓存本身,这种执行效率比较高

    设置为false的时候,代表每次读取出来的都是缓存的副本,每次取出来的对象是不同的,这种执行效率相对比较低,但是安全性比较高

    useCache:为true的时候,代表查询结果放入缓存。为false的时候,代表查询结果不放入缓存

    flushCache:为true,强制清空缓存 

    展开全文
  • Mybatis高级特性(一)

    2021-07-05 17:22:20
    引入日志相关的jar包 ch.qos.logback logback-classic 1.2.3 编写日志-logback.xml文件 [%thread] %d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n Mybatis动态SQL 动态SQL是指根据参数数据动态组织SQL的技术 1....

    日志

    日志文件是用于记录系统操作时间文件或者文件的集合。

    引入日志相关的jar包

    <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>[%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>

    Mybatis动态SQL

    动态SQL是指根据参数数据动态组织SQL的技术

    1.在goods.xml文件中增加相关的动态SQL语句

    <select id="dynamicSQL" parameterType="java.util.Map" resultType="com.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>

    2.测试类

    @Test
        public void testDynamicSQL() {
            SqlSession sqlSession = null;
            try {
                sqlSession = MybatisUtils.openSqlSession();
                Map param = new HashMap();
    //            param.put("categoryId",44);
                param.put("currentPrice",500);
                List<Goods> list = sqlSession.selectList("goods.dynamicSQL",param);
                for (Goods goods : list) {
                    System.out.println(goods.getTitle());
                }
            } catch (Exception e) {
                throw e;
            } finally {
                MybatisUtils.closeSqlSession(sqlSession);
            }
        }

    展开全文
  • SSM第六讲 MyBatis高级特性

    万次阅读 2020-08-17 22:04:41
    MyBatis高级特性 1 学习目标 1,理解Mybatis多对多关系 2,掌握Mybatis的延迟加载 3,掌握Mybatis缓存 4,掌握逆向工程生成mapper和实体类 2 Mybatis 延迟加载 2.1 延迟加载介绍 通过前面的学习,我们已经掌握了...

    MyBatis的高级特性

    1 学习目标

    1,理解Mybatis多对多关系

    2,掌握Mybatis的延迟加载

    3,掌握Mybatis缓存

    4,掌握逆向工程生成mapper和实体类

    2 Mybatis 延迟加载

    2.1 延迟加载介绍

    通过前面的学习,我们已经掌握了 Mybatis 中一对一,一对多,多对多关系的配置及实现,可以实现对象的关联查询。实际开发过程中有时候我们并不需要在加载用户信息时,就加载他的账户信息。 而是在使用用户账号的时候,再向数据库查询,此时就是我们所说的延迟加载。

    即时加载:

    // 条件查询
    @Test
    public void findAll() throws Exception {
        //  SELECT u.*,a.* FROM USER u LEFT JOIN account a ON a.UID=u.id;
        //  执行userDao.findAll()方法,执行查询用户、账户信息,一次加载所有的数据,这种叫做及时加载。
        List<User> list = userDao.findAll();
        for(User u : list){
            System.out.println(u);
            System.out.println(u.getAccounts());
        }
    }
    

    2.2 何为延迟加载?

    延迟加载:就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载.

    好处: 先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。
    坏处: 因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。

    如何实现延迟加载:前面实现多表操作时,我们使用了resultMap 来实现一对一,一对多,多对多关系的操作。主要是通过 association、 collection 实现一对一及一对多映射。 association、 collection 具备延迟加载功能。

    思考:什么是及时加载?什么是延时加载?

    及时加载:一次加载所有数据。

    延迟加载:也叫做懒加载,再需要用到数据时候再查询数据。

    2.3 一对一实现延时加载

    2.3.1 实现需求

    ​ 需求:查询Account账户信息时候,也要显示User用户信息,但User用户信息用到的时候再向数据库发送查询语句。

    数据库中实现:

    -- 一对一延迟加载
    -- 需求:查询账户,同时也要显示用户。但用户信息是再用到的时候再查询.
    -- 实现过程:
    -- 1) 查询账户
    SELECT * FROM account
    -- 3) 使用用户对象数据时候,查询用户
    SELECT * FROM USER WHERE id=46 
    

    2.3.2 entity

    public class Account {
        private int accountId;
        private int uid;
        private double money;
        // 一个账户对应一个用户
        private User user;
    }
    
    
    public class User {
        private int  id;
        private String username;
        private Date birthday;
        private String sex;
        private String address;
        // 一个用户,对应多个账户
        private List<Account> accounts;
    }
    
    

    2.3.3 dao

    public interface AccountMapper {
        /**
         * 查询账户
         */
        List<Account> findAll();
    }
    
    
    public interface UserMapper {
        /**
         * 根据用户id查询
         */
        User findById(int id);
    }
    
    

    2.3.4 UserMapper.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="com.itdfbz.dao.UserMapper" >
       <select id="findById" resultType="user">
          select * from user where id=#{id}
      </select>
    </mapper>
    

    2.3.5 AccountMapper.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="com.itdfbz.dao.AccountMapper">
    
       <resultMap id="accountResultMap" type="account">
           <!--1. 账户信息映射-->
           <id property="accountId" column="accountId"></id>
           <result property="uid" column="uid"></result>
           <result property="money" column="money"></result>
           <!--2. 延时加载的方式,查询账户的用户-->
           <!--select 延时加载的语句id(名称空间.方法)-->
           <!--column="uid" 表外键字段-->
           <association property="user" javaType="user" column="uid"
                        select="com.itdfbz.dao.UserMapper.findById"></association>
       </resultMap>
    
       <select id="findAll" resultMap="accountResultMap">
           SELECT * FROM account
       </select>
    </mapper>
    

    2.3.6 测试,观察问题

    @Test
    public void test1() {
        AccountMapper accountMapper = session.getMapper(AccountMapper.class);
        List<Account> accountList = accountMapper.findAll();
        for (Account account : accountList) {
            System.out.println(account.getAccountId());
        }
    }
    

    ​ 执行结果:

    在这里插入图片描述

    ​ 发现,我们在测试中,只输出了账户信息,并没有使用用户信息,但还是查询了用户,并没有应用懒加载。如何解决?

    2.3.7 解决方案

    2.3.7.1 解决1:

    查看mybatis官网

    http://www.mybatis.org/mybatis-3/zh/configuration.html
    

    打开页面,找到settings

    在这里插入图片描述

    然后在SqlMapConfig.xml中配置开启懒加载支持

    在这里插入图片描述

    最后,再次执行测试

    1.测试类中没有使用Account账户的User信息

    @Test
    public void find() throws  Exception{
        List<Account> list = accountDao.findAccounts();
        for (Account a : list) { 
            System.out.println(a.getAccountId() + ","+a.getUid() + "," +a.getMoney());
        }
    }
    
    

    测试结果

    在这里插入图片描述

    2.测试类中有使用Account账户的User信息

    @Test
    public void test1() {
        AccountMapper accountMapper = session.getMapper(AccountMapper.class);
        List<Account> accountList = accountMapper.findAll();
        for (Account account : accountList) {
            System.out.println(account.getAccountId()+"----"+account.getUser());
        }
    }
    

    测试结果: 可以看到使用对象数据的时候,才向数据库发送查询的SQL
    在这里插入图片描述

    2.3.7.2 解决2:

    (1) 注释SqlMapConfig.xml中全局懒加载的的开启

    在这里插入图片描述

    (2) 修改映射文件

    在这里插入图片描述

    通过打印的SQL可以看到,懒加载一样生效:

    在这里插入图片描述

    2.3.8 小结

    通过本示例,我们可以发现 Mybatis 的延迟加载对于提升软件性能这是一个不错的手段。实现的关键: association 的配置

    <resultMap id="accountResultMap" type="account">
        <!--1. 账户信息映射-->
        <id property="accountId" column="accountId"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
        <!--2. 延时加载的方式,查询账户的用户-->
        <!--select 延时加载的语句id(名称空间.方法)-->
        <!--column="uid" 表外键字段(要传入findById方法的参数)-->
        <!--
            fetchType:懒加载设置
                lazy:开启懒加载
                eager:立即加载
        -->
        <association property="user" javaType="user" column="uid" fetchType="lazy"
                     select="com.itdfbz.dao.UserMapper.findById"></association>
    </resultMap>
    

    2.4 一对多实现延时加载

    2.4.1 需求

    查询用户信息,再使用用户账户时候再查询账户。

    数据库实现:

    -- 一对多延迟加载
    -- 1) 查询用户
    SELECT * FROM USER;
    -- 2) 使用用户的账户对象数据时候再查询账户
    SELECT * FROM account WHERE uid=46;
    

    2.4.2 entity

    public class User {
       private Integer id;
       private String username;
       private Date birthday;
       private String sex;
       private String address;
       // 一个用户,对应多个账户
       private List<Account> accounts;
    }
    

    2.4.3 dao

    public interface UserMapper {
        /**
         * 查询所有用户
         */
        List<User> findAll();
    }
    
    
    public interface AccountMapper {
        /**
         * 根据用户的id查询
         */
        List<Account> findAccountByUserId(Integer userId);
    }
    
    

    2.4.4 AccountMapper.xml

    <!--根据用户,查询其下的多个账户-->
    <select id="findAccountByUserId" resultType="account">
       select * from account where uid=#{userId}
    </select>
    
    

    2.4.5 UserMapper.xml 延时加载关键配置

    <resultMap id="userResultMap" type="user">
       <id property="id" column="id"></id>
       <result column="username" property="username"></result>
       <result column="sex" property="sex"></result>
       <result column="address" property="address"></result>
    
       <!--
           collection:一对多映射
               property:JavaBean中的实体类的多方属性名
               column: 本表的id(要传入findAccountByUserId的参数)
               ofType:属性类型
               select:映射的方法
       -->
       <collection property="accounts" column="id"
                   ofType="account"
                   select="com.itdfbz.dao.AccountMapper.findAccountByUserId">
       </collection>
    </resultMap>
    <select id="findAll" resultMap="userResultMap">
       select * from user
    </select>
    

    2.4.6 开启延时加载支持

    在这里插入图片描述

    2.4.7 测试

    @Test
    public void test1() {
        UserMapper userMapper = session.getMapper(UserMapper.class);
        List<User> userList = userMapper.findAll();
        for (User user : userList) {
            System.out.println(user.getSex());
        }
    }
    

    测试结果:

    在这里插入图片描述

    // 查询用户
    @Test
    public void find() throws  Exception{
        List<User> list = userDao.findUsers();
        for (User u : list) {
            System.out.println(u.getId() + "," + u.getUsername());
            System.out.println(u.getAccounts());
        }
    }
    
    

    在这里插入图片描述

    2.5. 注解实现延迟加载开发

    2.5.1 注解实现一对一映射及延迟加载

    在这里插入图片描述

    2.5.1.1 entity

    (1) 账户与用户,一对一

    public class Account implements Serializable {
        private int accountId;
        
        private int uid;
        
        private double money;
        
        //一个账号对应一个账户
        private User user;
    }
    

    (2) 用户与账户,一对多

    public class User implements Serializable {
        private Integer id;
        private String username;
        private Date birthday;
        private String sex;
        private String address;
        
        //一个用户对应多个账号
        private List<Account> accountList;
    }
    

    2.5.1.2 dao

    public interface UserMapper {
    
        // 根据用户id查询
        @Select("select * from user where id=#{id}")
        User findById(int id);
    }
    
    
    package com.itdfbz.dao;
    
    import com.itdfbz.entity.Account;
    import com.itdfbz.entity.User;
    import org.apache.ibatis.annotations.One;
    import org.apache.ibatis.annotations.Result;
    import org.apache.ibatis.annotations.Results;
    import org.apache.ibatis.annotations.Select;
    import org.apache.ibatis.mapping.FetchType;
    
    public interface AccountMapper {
    
        /*
        *
        * @One:代表一对一
        * select:要映射到本表属性的数据提供方法
        * user:本类中要映射的属性名
        * uid:外键,要传入findById方法的参数
        * fetchType: 配置懒加载
        *   LAZY:懒加载
        *   EAGER:立即加载
        *
        * */
        @Select("select * from account where accountId=#{id}")
        @Results({
                @Result(id=true,property = "accountId",column = "accountId"),
                @Result(property = "uid",column = "uid"),
                @Result(property = "money",column = "money"),
                @Result(property = "user",column = "uid",javaType = User.class,
                    one = @One(select = "com.itdfbz.dao.UserMapper.findById",fetchType = FetchType.EAGER )
    
                )
        })
        Account findById(Integer id);
    }
    
    

    2.5.1.3 测试

    @Test
    public void test1(){
        AccountMapper mapper = session.getMapper(AccountMapper.class);
        Account account = mapper.findById(1);
        System.out.println(account.getAccountId());
        System.out.println(account.getUser());
    }
    

    2.5.2 注解实现一对多映射及延迟加载

    2.5.1.1 dao 接口

    /**
     * many:代表一对多
     * accountList:代表本类中的多方属性名
     * id:要传入findByUserId的参数
     * List.class:多方集合类型
     * FetchType.LAZY:懒加载
     * 
     */
    @Select("select * from user where id=#{id}")
    @Results({
            @Result(id = true,property = "id",column = "id"),
            @Result(property = "username",column = "username"),
            @Result(property = "birthday",column = "birthday"),
            @Result(property = "sex",column = "sex"),
            @Result(property = "address",column = "address"),
            @Result(property = "accountList",column = "id",javaType = List.class,
                    many=@Many(select = "com.itdfbz.dao.AccountMapper.findByUserId",fetchType = FetchType.LAZY)
            )
    })
    

    2.5.1.2 测试

    @Test
    public void test2() {
        UserMapper userMapper = session.getMapper(UserMapper.class);
    
        User user = userMapper.findById(46);
        System.out.println(user.getUsername());
        List<Account> accountList = user.getAccountList();
        for (Account account : accountList) {
            System.out.println(account);
        }
    
    }
    

    3 Mybatis 缓存机制

    3.1 缓存介绍

    像大多数的持久化框架一样, Mybatis 也提供了缓存策略,通过缓存策略来减少数据库的查询次数,从而提高性能。Mybatis 中缓存分为一级缓存,二级缓存。

    在这里插入图片描述

    从图中可以看出什么 ? 一级缓存是基于SqlSessoion的缓存,一级缓存的内容不能跨sqlsession。由mybatis自动维护。二级缓存是基于映射文件的缓存,缓存范围比一级缓存更大。不同的sqlsession可以访问二级缓存的内容。哪些数据放入二级缓存需要自己指定。

    3.2 一级缓存测试

    同一个SqlSession的一级缓存的测试:

    @Test
    public void test1() {
        SqlSession session = factory.openSession();
    
        UserMapper userMapper1 = session.getMapper(UserMapper.class);
    
        User user1 = userMapper1.findById(41);   //发送sql查询出来,放入一级缓存
        System.out.println(user1.getUsername());
        
        User user2 = userMapper1.findById(41);       //直接从一级缓存中查询(不发送sql)
        System.out.println(user2.getUsername());
    
        session.close();            //session关闭(此session的一级缓存销毁)
    
        SqlSession session2 = factory.openSession();
        UserMapper userMapper2 = session2.getMapper(UserMapper.class);
        User user3 = userMapper2.findById(41);          //发送sql查询一级缓存
    
        System.out.println(user3.getUsername());
    
    }
    

    ​ 测试结果分析:

    在这里插入图片描述

    小结

    在这里插入图片描述

    tips:如果更改了一级缓存中的任何数据那么一级缓存会被清空

    3.3 二级缓存

    3.3.1 介绍

    二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession 的。

    二级缓存结构图:

    在这里插入图片描述

    首先开启 mybatis的二级缓存。
    sqlSession1去查询用户信息,查询到用户信息会将查询数据存储到二级缓存中。sqlSession2去查询与 sqlSession1 相同的用户信息, 首先会去缓存中找是否存在数据,如果存在直接从缓存中取出数据。如果 SqlSession3 去执行相同 mapper 映射下 sql,执行 commit 提交, 将会清空该 mapper映射下的二级缓存区域的数据。

    3.3.2 测试二级缓存

    3.3.2.1 第一步:开启二级缓存

    在这里插入图片描述

    因为 cacheEnabled 的取值默认就为 true,所以这一步可以省略不配置。为 true代表开启二级缓存;为 false 代表不开启二级缓存。

    官网介绍:

    在这里插入图片描述

    3.3.2.2 第二步:配置mapper

    <mapper namespace="com.itdfbz.dao.UserMapper">
    
        <!--
            cache: 表示当前这个mapper对象的查询结果会放入二级缓存
            userCache: 表示当前sql语句查询结果是否被存入二级缓存(首先应该配置<cache>标签)
                true:存入二级缓存(默认值)
                false:不存入二级缓存
        -->
        <cache></cache>
        <select id="findById" parameterType="int" resultType="user" useCache="true">
            select * from user where id=#{id}
        </select>
    </mapper>
    

    将 UserMapper.xml 映射文件中的标签中设置 useCache=”true”代表当前这个 statement 要使用二级缓存,如果不使用二级缓存可以设置为 false。
    注意: 针对每次查询都需要最新的数据 sql,要设置成useCache=false,禁用二级缓存。

    3.3.2.3 第三步:实体类实现Serializable

    当我们在使用二级缓存时,所缓存的类一定要实现 java.io.Serializable 接口:
    在这里插入图片描述

    3.3.2.4 测试

    public void test1() throws IOException {
    
        InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
    
        SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(is);
    
        SqlSession session1 = factory.openSession();
    
        UserMapper mapper1 = session1.getMapper(UserMapper.class);
    
        User user1 = mapper1.findById(41);          //发送sql查询,放入一级缓存
        System.out.println(user1);
    
        session1.close();           //关闭一级缓存,并将一级缓存的内容写入二级缓存
    
        SqlSession session2 = factory.openSession();
        UserMapper mapper2 = session2.getMapper(UserMapper.class);
    
        User user2 = mapper2.findById(41);          //从二级缓存查询(反序列化)直接返回(此时一级缓存还是空的)
        User user3 = mapper2.findById(41);      //查询二级缓存(反序列化)
    
        System.out.println(user3==user2);       //false
    
        session2.close();
        is.close();
    }
    

    测试结果

    在这里插入图片描述

    二级缓存执行原理:

    在这里插入图片描述

    tips:如果更改了任何数据那么二级缓存和一级缓存都将被清空

    3.4 MyBatis缓存深入

    一级缓存测试:

    @Test
    public void test2() throws IOException {
        InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
    
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(is);
    
        SqlSession session1 = factory.openSession();
    
        UserMapper mapper1 = session1.getMapper(UserMapper.class);
    
        User user1 = mapper1.findById(41);          //发送sql查询,放入一级缓存
        user1.setAddress("NanChang");
        mapper1.update(user1);              //清空一级缓存 update delete 
    
        User user2 = mapper1.findById(41);          //再次发送sql
    
        session1.close();           //关闭一级缓存,并将一级缓存的内容写入二级缓存
    }
    

    二级缓存测试:

    @Test
    public void test3() throws IOException {
        InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
    
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(is);
    
        SqlSession session1 = factory.openSession();
    
        UserMapper mapper1 = session1.getMapper(UserMapper.class);
    
        User user1 = mapper1.findById(41);          //发送sql查询,放入一级缓存
        System.out.println(user1);
    
        session1.close();           //关闭一级缓存,并将一级缓存的内容写入二级缓存
    
        SqlSession session2 = factory.openSession();
        UserMapper mapper2 = session2.getMapper(UserMapper.class);
    
        User user2 = mapper2.findById(41);          //从二级缓存查询(反序列化)直接返回(此时一级缓存还是空的)
        mapper2.update(user2);                      //清空二级缓存、一级缓存
    
        User user3 = mapper2.findById(41);      //查询二级缓存--->一级缓存--->数据库
        User user4 = mapper2.findById(41);      //查询二级缓存(空的)---->查询一级缓存---返回结果
        System.out.println(user3 == user4);       //true
    
        session2.close();
        is.close();
    }
    

    3.5 MyBatis缓存总结

    一级缓存和二级缓存的执行顺序为:二级缓存---->一级缓存---->数据库

    查询语句始终是由一级缓存发送的

    一级缓存默认由MyBatis维护,我们只需了解使用即可

    二级缓存需要我们开启:

    1、在SqlMapConfig.xml中开启二级缓存(默认开启)

    <settings>
        <!--开启二级缓存,默认是开启状态 false为关闭二级缓存-->
        <setting name="cacheEnabled" value="true"/>
    </settings>
    

    2、在mapper文件中加入标签代表本mapper文件的所有查询结果放入二级缓存,并保证useCache不为false(默认为true)

    <cache></cache>
    <select id="findById" parameterType="int" resultType="user" useCache="true">
        select * from user where id=#{id}
    </select>
    

    3、MyBatis的二级缓存默认采用序列化/反序列化来保证对象的存取,所以所有的entity对象都应该实现serializable接口

    3.6 mybatis自身缓存的弊

    A. 分布式的项目架构下,也就是最少使用两个服务器,如果使用两个服务器mybatis的缓存技术就无法在两个服务器通用就是,也就是两个服务器无法达到数据通用,比如我在一个服务器存储了我的信息,但是我转跳到另一个服务器那使用mybatis数据就是需要从新加载,这里就是一个非常大的问题。

    B. mybatis无法实现细粒度的缓存管理,当你查询大量数据的时候而且将数据存储到mybatis二级缓存中的时候,但是一旦队一个数据操作增加,删除,修改,这里二级缓存就全部清空,而mybatis无法实现对这里单个信息的修改

    问题:如何解决这个缺陷?

    答:使用第三方的缓存如:ehcache、redis

    在这里插入图片描述

    3.7.ehcache的整合配置

    ehcache需要slf4j这个日志包支持

    3.7.1.导入包

    <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
    
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.25</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.35</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.caches</groupId>
            <artifactId>mybatis-ehcache</artifactId>
            <version>1.1.0</version>
        </dependency>
    

    3.7.2.引入缓存配置文件

    classpath下添加:ehcache.xml(EhcacheCache实现类的默认配置文件)

    <?xml version="1.0" encoding="UTF-8"?>
    <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
      updateCheck="false">
    <!--diskStore:缓存数据持久化的目录 地址  -->
    <diskStore path="F:\temp\cache"/>
    <!--
    maxElementsInMemory:内存中最大缓存对象数  
        maxElementsOnDisk:磁盘中最大缓存对象数,若是0表示无穷大  
        eternal:Element是否永久有效,一但设置了,timeout将不起作用  
        overflowToDisk:配置此属性,当内存中Element数量达到maxElementsInMemory时,Ehcache将会Element写到磁盘中  
        diskPersistent:是否缓存虚拟机重启期数据
        timeToIdleSeconds:设置Element在失效前的允许闲置时间。仅当element不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大  
        timeToLiveSeconds:设置Element在失效前允许存活时间。最大时间介于创建时间和失效时间之间。仅当element不是永久有效时使用,默认是0.,
        也就是element存活时间无穷大   
        memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),
        可选的有LFU(最不常使用)和FIFO(先进先出)
     -->
    <defaultCache 
    maxElementsInMemory="1000" 
    maxElementsOnDisk="10000000" 
    eternal="false" 
    overflowToDisk="true" 
    diskPersistent="false" 
    timeToIdleSeconds="120" 
    timeToLiveSeconds="120" 
    memoryStoreEvictionPolicy="LRU">
    </defaultCache>
    </ehcache>
    

    3.7.3.开启ehcache缓存

    EhcacheCache 是ehcache对Cache接口的实现;修改mapper.xml文件,在cache标签的type属性中指定EhcacheCache。覆盖其默认实现

    根据需求调整缓存参数:(根据条件对EhcacheCache实现类的参数调整,可选)

        <cache type="org.mybatis.caches.ehcache.EhcacheCache" >
           <!--最大的空闲时间  -->
           <property name="timeToIdleSeconds" value="10000"/>
           <!-- 最大的在线时间 -->
           <property name="timeToLiveSeconds" value="20000"/>
           <!-- 内存的大小 b字节 m1 =1024k 1k=1024b -->
           <property name="maxEntriesLocalHeap" value="2000000"/>
           <!-- 文件的大小 b字节-->
           <property name="maxEntriesLocalDisk" value="20000000"/>
           <!-- 算法 LRU:最少使用优先, "LFU" or "FIFO:先进先出 -->
           <property name="memoryStoreEvictionPolicy" value="LRU"/>
        </cache> 
    

    结果:观察缓存命中,且在写入磁盘配置路径处可以看到生成文件

    在这里插入图片描述

    4 逆向工程

    idea的逆向工程,通过maven的插件来实现

    4.1.创建maven项目

    在这里插入图片描述

    在这里插入图片描述

    4.2. 引入插件mybatis-generator-maven-plugin

    在maven项目的pom.xml 添加mybatis-generator-maven-plugin 插件

    <build>
    
        <plugins>
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.2</version>
            </plugin>
        </plugins>
    </build>
    

    更新下载依赖包

    在这里插入图片描述

    4.3. generatorConfig.xml

    在maven项目下的src/main/resources 目录下建立名为 generatorConfig.xml的配置文件,作为mybatis-generator-maven-plugin 插件的执行目标,模板如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE generatorConfiguration
            PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
            "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
    
    <generatorConfiguration>
        <!--引入数据库配置文件以解耦-->
        <properties resource="mybatis.properties"/>
        <!-- mysql jar 文件位置 -->
        <classPathEntry location="D:\.m2\repository\mysql\mysql-connector-java\5.1.35\mysql-connector-java-5.1.35.jar" />
        
        <context id="testTables" targetRuntime="MyBatis3">
            <commentGenerator>
                <!-- 是否去除自动生成的注释,true:是;false:否 -->
                <property name="suppressAllComments" value="true" />
            </commentGenerator>
            <!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
            <jdbcConnection driverClass="${jdbc.driver}"
                            connectionURL="${jdbc.url}" userId="${jdbc.username}"
                            password="${jdbc.password}">
            </jdbcConnection>
            <!--Oracle数据库的连接信息-->
            <!-- <jdbcConnection driverClass="oracle.jdbc.OracleDriver"
                connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:yycg"
                userId="yycg"
                password="yycg">
            </jdbcConnection> -->
    
            <!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和
                NUMERIC 类型解析为java.math.BigDecimal -->
            <javaTypeResolver>
                <property name="forceBigDecimals" value="false" />
            </javaTypeResolver>
    
            <!-- targetProject:生成PO类的位置 -->
            <javaModelGenerator targetPackage="cn.nyse.entity"
                                targetProject="src/main/java">
                <!-- enableSubPackages:是否让schema作为包的后缀 -->
                <property name="enableSubPackages" value="false" />
                <!-- 从数据库返回的值被清理前后的空格 -->
                <property name="trimStrings" value="true" />
            </javaModelGenerator>
            <!-- targetProject:mapper映射文件生成的位置 -->
            <sqlMapGenerator targetPackage="cn.nyse.mapper"
                             targetProject="src/main/java">
                <!-- enableSubPackages:是否让schema作为包的后缀 -->
                <property name="enableSubPackages" value="false" />
            </sqlMapGenerator>
            <!--
            targetPackage:mapper接口生成的位置,遵循MyBatis规范,让mapper.xml
            和mapper.java在同一目录下
            ANNOTATEDMAPPER:注解方式
            XMLMAPPER:xml方式
             -->
            <javaClientGenerator type="XMLMAPPER"
                                 targetPackage="cn.nyse.mapper"
                                 targetProject="src/main/java">
                <!-- enableSubPackages:是否让schema作为包的后缀 -->
                <property name="enableSubPackages" value="false" />
            </javaClientGenerator>
            <!-- 指定数据库表 -->
            <table tableName="${jdbc.table.items}"></table>
    
          
    
            <!-- 有些表的字段需要指定java类型
             <table schema="" tableName="">
                <columnOverride column="" javaType="" />
            </table> -->
        </context>
    </generatorConfiguration>
    
    

    mybatis.properties:

    <!--jdbc连接数据库配置信息-->
    jdbc.driver = com.mysql.jdbc.Driver
    jdbc.url = jdbc:mysql://localhost:3306/mybatis
    jdbc.username = root
    jdbc.password = 123456
    
    #数据库中要生成的表
    jdbc.table.items = student
    

    项目目录如下

    在这里插入图片描述

    4.4. 使用maven运行mybatis-generator-maven-plugin插件

    在这里插入图片描述

    观察控制台输出结果:

    在这里插入图片描述

    生成的文件:

    在这里插入图片描述

        <!-- 有些表的字段需要指定java类型
         <table schema="" tableName="">
            <columnOverride column="" javaType="" />
        </table> -->
    </context>
    
    
    
    
    mybatis.properties:
    
    ```properties
    <!--jdbc连接数据库配置信息-->
    jdbc.driver = com.mysql.jdbc.Driver
    jdbc.url = jdbc:mysql://localhost:3306/mybatis
    jdbc.username = root
    jdbc.password = 123456
    
    #数据库中要生成的表
    jdbc.table.items = student
    

    项目目录如下

    在这里插入图片描述

    4.4. 使用maven运行mybatis-generator-maven-plugin插件

    在这里插入图片描述

    观察控制台输出结果:

    在这里插入图片描述

    生成的文件:

    在这里插入图片描述

    展开全文
  • mybatis-plus 高级特性-动态表名

    万次阅读 2020-12-16 18:59:52
    背景 在分表的背景下,有时候查询数据的时候需要跨表查询,那此时就需要MP在解析的时候,能够很好的自适应表格名称 实现 MP中是通过PaginationInterceptor(分页插件)完成动态...mybatis-plus-boot-starter<...
  • 文章目录MyBatis是什么ORM是什么为什么说Mybatis是半自动ORM映射工具对于传统的JDBC,MyBatis的优势有哪些传统 JDBC 开发存在的问题MyBatis解决JDBC编程的不足MyBatis 的优缺点优点缺点MyBatis 框架适用场景Hibernate...
  • MyBatis高级

    2021-04-25 20:20:28
    } 缺点: Java代码中出现大量if,可读性和维护性比较差 拼接字符串容易出现错误 1.2动态SQL简介  MyBatis的一个重要特性就是动态SQL,能通过条件动态生成SQL语句  动态SQL包含几种标签: 1.sql 2.if 3.where 4....

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 14,045
精华内容 5,618
热门标签
关键字:

mybatis高级特性