1.高级映射

一对一查询:使用resultType实现

案例:查询所有订单信息,关联查询下单用户部分信息。

注意:因为一个订单信息只会是一个人下的订单,所以从查询订单信息出发关联查询用户信息为一对一查询。如果从用户信息出发查询用户下的订单信息则为一对多查询,因为一个用户可以下多个订单。

user表数据:

mybaties--day02_IT

 orders表数据:

mybaties--day02_IT_02

 执行的查询语句:

SELECT orders.*,user.username,user.address FROM orders,user WHERE orders.user_id = user.id

由上面的查询结果我们可以使用orders的扩展子类来接收查询结果,扩展子类代码:

OrderExtension.java

//通过此类映射用户和订单查询结果,让此类继承包括字段较多的pojo类
public class OrderExtension extends Orders {
    private String username;// 用户姓名
    private String sex;// 性别
    private Date birthday;// 生日
    private String address;// 地址

    @Override
    public String toString() {
        return "OrderExtension{" +
                "username='" + username + '\'' +
                ", sex='" + sex + '\'' +
                ", birthday=" + birthday +
                ", address='" + address + '\'' +
                "} " + super.toString();
    }
...get
...set
}

注意:上面重写toString方法时,别忘了追加重写父类的toString:

mybaties--day02_IT_03

mapper.xml:

<mapper namespace="mybaties.mapper.OrderExtensionMapper">
<!--查询订单,关联查询用户信息-->
    <select id="findOrderAndUser"  resultType="mybaties.extension.OrderExtension">
    SELECT orders.*,user.username,user.address FROM orders,user WHERE orders.user_id = user.id
    </select>
</mapper>

mapper.java:

public interface OrderExtensionMapper {
    public List<OrderExtension> findOrderAndUser() throws Exception;
}

测试代码:

    @Test
    public void findOrderAndUser() throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
//        mybaties自动生成mapper代理对象
        OrderExtensionMapper mapper = sqlSession.getMapper(OrderExtensionMapper.class);
        List<OrderExtension> orderAndUser = mapper.findOrderAndUser();
        System.out.println(orderAndUser);
    }

一对一查询:使用resultMap实现

sql语句

同resultType实现的sql

使用resultMap映射的思路

使用resultMap将查询结果中的订单信息映射到Orders对象中,在orders类中添加User属性,将关联查询出来的用户信息映射到orders对象中的user属性中。

 需要Orders类中添加user属性

 mybaties--day02_IT_04

 mapper.xml:

<!--一对一:resultMap:查询订单,关联查询用户信息-->
<!--    定义resultMap:将整个查询结果映射到mybaties.pojo.Orders中-->
    <resultMap id="findOrderAndUserResultMap" type="mybaties.pojo.Orders">
      <!--配置映射的订单信息-->
<!--
        <id/>:指定查询列中的唯一标识,订单信息的中的唯一标识,如果有多个列组成唯一标识,配置多个id
            column:订单信息的唯一标识列
            property:订单信息的唯一标识列所映射到Orders中哪个属性
        <result/>普通列
-->
        <id column="id" property="id"></id>
        <result column="user_id" property="userId"></result>
        <result column="number" property="number"></result>
        <result column="createtime" property="createtime"></result>
        <result column="note" property="note"></result>
        <result column="" property=""></result>
        <!--配置映射的关联的用户信息-->
<!--
            association:用于映射关联查询单个对象的信息
            property:要将关联查询的用户信息映射到Orders中哪个属性
-->
        <association property="user" javaType="mybaties.pojo.User">
            <id column="user_id" property="id"></id>
            <result column="username" property="username"></result>
            <result column="address" property="address"></result>
        </association>
    </resultMap>
    <select id="findOrderAndUserResultMap"  resultMap="findOrderAndUserResultMap">
        SELECT orders.*,user.username,user.address FROM orders,user WHERE orders.user_id = user.id
    </select>

mapper.java:

    public List<Orders> findOrderAndUserResultMap() throws Exception;

测试:

   @Test
    public void findOrderAndUserResultMap() throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        OrderExtensionMapper mapper = sqlSession.getMapper(OrderExtensionMapper.class);
        List<Orders> orderAndUser = mapper.findOrderAndUserResultMap();
        System.out.println(orderAndUser);
    }
[Orders{id=1, userId=5, number='3', createtime=Wed Jul 15 14:46:50 CST 2020, note='第一个订单', user=User{id=5, username='刘悦后', sex='null', birthday=null, address='加拿大'}}, Orders{id=3, userId=1, number='1000010', createtime=Wed Feb 04 13:22:35 CST 2015, note='null', user=User{id=1, username='王五', sex='null', birthday=null, address='null'}}, Orders{id=4, userId=1, number='1000011', createtime=Tue Feb 03 13:22:41 CST 2015, note='null', user=User{id=1, username='王五', sex='null', birthday=null, address='null'}}, Orders{id=5, userId=10, number='1000012', createtime=Thu Feb 12 16:13:23 CST 2015, note='null', user=User{id=10, username='张三', sex='null', birthday=null, address='北京市'}}]

resultType和resultMap实现一对一查询小结

实现一对一查询:

resultType:使用resultType实现较为简单,如果pojo中没有包括查询出来的列名,需要增加列名对应的属性,即可完成映射。

如果没有查询结果的特殊要求建议使用resultType

resultMap:需要单独定义resultMap,实现有点麻烦,如果对查询结果有特殊的要求,使用resultMap可以完成将关联查询映射pojo的属性中。

resultMap可以实现延迟加载,resultType无法实现延迟加载。

一对多查询

 需求

查询订单及订单明细的信息。

sql语句

确定主查询表:订单表

确定关联查询表:订单明细表

在一对一查询基础上添加订单明细表关联即可。

SELECT

  orders.*,

  USER.username,

  USER.sex,

  USER.address,

  orderdetail.id orderdetail_id,

  orderdetail.items_id,

  orderdetail.items_num,

  orderdetail.orders_id

FROM

  orders,

  USER,

  orderdetail

WHERE orders.user_id = user.id AND orderdetail.orders_id=orders.id

 orderdetail表结构:

mybaties--day02_IT_05

分析

使用resultType将上边的 查询结果映射到pojo中,订单信息的就是重复。

mybaties--day02_IT_06

要求:

orders映射不能出现重复记录。

orders.java类中添加List<orderDetail> orderDetails属性。

最终会将订单信息映射到orders中,订单所对应的订单明细映射到orders中的orderDetails属性中。

映射成的orders记录数为两条(orders信息不重复)

每个orders中的orderDetails属性存储了该 订单所对应的订单明细。

 在orders中添加list订单明细属性

mybaties--day02_IT_07

mapper.xml:

<!--    查询订单关联用户以及订单明细,使用resultMap-->
<!--定义resultMap-->
    <resultMap id="findOrderUserOrderDetailResultMap" type="mybaties.pojo.Orders" extends="findOrderAndUserResultMap">
<!--        订单信息-->
<!--        用户信息-->
        <!-- 使用extends继承,不用在中配置订单信息和用户信息的映射 -->
<!--订单明细信息
        一个订单关联查询出多条明细:要使用collection进行映射
        <collection/>:用于将关联查询多个条记录映射到集合中
            property:指定将关联查询多个条记录映射到mybaties.pojo.Orders类中的哪个属性
            ofType:指定映射集合属性中的pojo类型(集合属性存放的数据类型)(集合属性的泛型)
-->
        <collection property="orderdetails" ofType="mybaties.pojo.Orderdetail">
            <id column="" property=""></id>
            <result column="orders_id" property="ordersId"></result>
            <result column="items_num" property="itemsNum"></result>
            <result column="items_id" property="itemsId"></result>
            <result column="orderdetail_id" property="id"></result>
        </collection>
    </resultMap>
    <select id="findOrderUserOrderDetailResultMap"  resultMap="findOrderUserOrderDetailResultMap">
        SELECT
        orders.*,
        orderdetail.orders_id,
        orderdetail.items_num,
        orderdetail.items_id,
        orderdetail.id orderdetail_id,
        user.username,
        user.address ,
        user.sex
        FROM orders,orderdetail,user
        WHERE orders.user_id = user.id AND orders.id=orderdetail.orders_id;
    </select>

mapper.java:

public List<Orders> findOrderUserOrderDetailResultMap() throws Exception;

 测试:

 public void findOrderUserOrderDetailResultMap() throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        OrderExtensionMapper mapper = sqlSession.getMapper(OrderExtensionMapper.class);
        List<Orders> orderAndUser = mapper.findOrderUserOrderDetailResultMap();
        System.out.println(orderAndUser);
    }

打印内容:

[Orders{id=3, userId=1, number='1000010', createtime=Wed Feb 04 13:22:35 CST 2015, note='null', user=User{id=1, username='王五', sex='null', birthday=null, address='null'}, orderdetails=[Orderdetail [id=1, ordersId=3, itemsId=1, itemsNum=1], Orderdetail [id=2, ordersId=3, itemsId=2, itemsNum=3]]}, Orders{id=4, userId=1, number='1000011', createtime=Tue Feb 03 13:22:41 CST 2015, note='null', user=User{id=1, username='王五', sex='null', birthday=null, address='null'}, orderdetails=[Orderdetail [id=3, ordersId=4, itemsId=3, itemsNum=4], Orderdetail [id=4, ordersId=4, itemsId=2, itemsNum=3]]}]

小结

mybatis使用resultMapcollection对关联查询的多条记录映射到一个list集合属性中。

使用resultType实现:

将订单明细映射到orders中的orderdetails中,需要自己处理,使用双重循环遍历,去掉重复记录,将订单明细放在orderdetails中。

多对多查询:

1.1 需求

查询用户及用户购买商品信息。

1.2 sql语句

查询主表是:用户表

关联表:由于用户和商品没有直接关联,通过订单和订单明细进行关联,所以关联表:

orders、orderdetail、items

sql:

SELECT 
orders.*,
orderdetail.orders_id,
orderdetail.items_num,
orderdetail.items_id,
orderdetail.id orderdetail_id,
items.id items_id,
items.name,
items.price,
items.createtime items_createtime,
items.pic,
items.detail,
user.username,
user.address,
user.sex
FROM orders,orderdetail,items,user 
WHERE orders.user_id = user.id AND orders.id=orderdetail.orders_id and orderdetail.items_id=items.id;

1.3 映射思路

将用户信息映射到user中。

user类中添加订单列表属性List<Orders> orderslist,将用户创建的订单映射到orderslist

Orders中添加订单明细列表属性List<OrderDetail>orderdetials,将订单的明细映射到orderdetials

OrderDetail中添加Items属性,将订单明细所对应的商品映射到Items

mapper.xml:

<!--查询用户及购买的商品信息-->
<!--定义resultMap-->
    <resultMap id="findOrderUserAndItemsResultMap" type="mybaties.pojo.User">
<!--        用户信息-->
        <id column="user_id" property="id"></id>
        <result column="username" property="username"></result>
        <result column="address" property="address"></result>
        <result column="sex" property="sex"></result>
<!--        订单信息-->
        <collection property="orders" ofType="mybaties.pojo.Orders">
            <id column="id" property="id"></id>
            <result column="user_id" property="userId"></result>
            <result column="number" property="number"></result>
            <result column="createtime" property="createtime"></result>
            <result column="note" property="note"></result>
<!--            订单明细信息-->
            <collection property="orderdetails" ofType="mybaties.pojo.Orderdetail">
                <id column="" property=""></id>
                <result column="orders_id" property="ordersId"></result>
                <result column="items_num" property="itemsNum"></result>
                <result column="items_id" property="itemsId"></result>
                <result column="orderdetail_id" property="id"></result>
<!--              商品信息  -->
                <association property="items" javaType="mybaties.pojo.Items">
                    <id column="items_id" property="id"></id>
                    <result column="name" property="name"></result>
                    <result column="price" property="price"></result>
                    <result column="pic" property="pic"></result>
                    <result column="tems_createtime" property="createtime"></result>
                    <result column="detail" property="detail"></result>
                </association>
            </collection>
        </collection>
    </resultMap>
    <select id="findOrderUserAndItemsResultMap"  resultMap="findOrderUserAndItemsResultMap">
        SELECT
        orders.*,
        orderdetail.orders_id,
        orderdetail.items_num,
        orderdetail.items_id,
        orderdetail.id orderdetail_id,
        items.id items_id,
        items.name,
        items.price,
        items.createtime items_createtime,
        items.pic,
        items.detail,
        user.username,
        user.address,
        user.sex
        FROM orders,orderdetail,items,user
        WHERE orders.user_id = user.id AND orders.id=orderdetail.orders_id and orderdetail.items_id=items.id;
    </select>

测试代码:

//    多对多
    @Test
    public void findOrderUserAndItemsResultMap() throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        OrderExtensionMapper mapper = sqlSession.getMapper(OrderExtensionMapper.class);
        List<User> orderAndUser = mapper.findOrderUserAndItemsResultMap();
        System.out.println(orderAndUser);
    }

打印结果:

mybaties--day02_IT_08

1.4多对多查询总结

将查询用户购买的商品信息明细清单,(用户名、用户地址、购买商品名称、购买商品时间、购买商品数量)

针对上边的需求就使用resultType将查询到的记录映射到一个扩展的pojo中,很简单实现明细清单的功能。

一对多是多对多的特例,如下需求:

查询用户购买的商品信息,用户和商品的关系是多对多关系。

需求1

查询字段:用户账号、用户名称、用户性别、商品名称、商品价格(最常见)

企业开发中常见明细列表,用户购买商品明细列表,

使用resultType将上边查询列映射到pojo输出。

需求2

查询字段:用户账号、用户名称、购买商品数量、商品明细(鼠标移上显示明细)

使用resultMap将用户购买的商品明细列表映射到user对象中。

总结:

使用resultMap是针对那些对查询结果映射有特殊要求的功能,,比如特殊要求映射成list中包括 多个list

resultMap总结

resultType

作用:

将查询结果按照sql列名pojo属性名一致性映射到pojo中。

场合:

常见一些明细记录的展示,比如用户购买商品明细,将关联查询信息全部展示在页面时,此时可直接使用resultType将每一条记录映射到pojo中,在前端页面遍历listlist中是pojo)即可。

resultMap

如果sql查询字段名和pojo的属性名不一致,可以通过resultMap将字段名和属性名作一个对应关系,resultMap实质上还需要将查询结果映射到pojo对象中。

resultMap可以实现将查询结果映射为复杂类型的pojo,比如在查询结果映射对象中包括pojo和list实现一对一查询和一对多查询。

使用associationcollection完成一对一和一对多高级映射(对结果有特殊的映射要求)。

association

  作用:

  将关联查询信息映射到一个pojo对象中。

  场合:

  为了方便查询关联信息可以使用association将关联订单信息映射为用户对象的pojo属性中,比如:查询订单及关联用户信息。

  使用resultType无法将查询结果映射到pojo对象的pojo属性中,根据对结果集查询遍历的需要选择使用resultType还是resultMap

collection

  作用:

  将关联查询信息映射到一个list集合中。

  场合:

  为了方便查询遍历关联信息可以使用collection将关联信息映射到list集合中,比如:查询用户权限范围模块及模块下的菜单,可使用collection将模块映射到模块list                 中,将菜单列表映射到模块对象的菜单list属性中,这样的作的目的也是方便对查询结果集进行遍历查询。

如果使用resultType无法将查询结果映射到list集合中。

 延迟加载

1.配置mybaties开启延迟加载

mybatis默认没有开启延迟加载,需要在SqlMapConfig.xmlsetting配置。

mybatis核心配置文件中配置:

lazyLoadingEnabled、aggressiveLazyLoading

设置项

描述

允许值

默认值

lazyLoadingEnabled

全局性设置懒加载。如果设为‘false’,则所有相关联的都会被初始化加载。

true | false

false

aggressiveLazyLoading

当设置为‘true’的时候,懒加载的对象可能被任何懒属性全部加载。否则,每个属性都按需加载。

true | false

true

    <!--    全局配置参数,需要时再配置:-->
    <settings>
        <!--        打开延迟加载的开关-->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!--将积极加载改为消极加载即按需加载-->
        <setting name="aggressiveLazyLoading" value="false"/>
    </setting

mybaties--day02_IT_09

 

 

 2.mapper.xml

.

<!--查询订单,关联查询用户信息:延迟加载方式-->
<!--定义resultMap实现延迟加载-->
    <resultMap id="findOrderAndUserLazyLoadingMap" type="mybaties.pojo.Orders">
<!--        先查询出订单信息并进行映射配置-->
        <id column="id" property="id"></id>
        <result column="user_id" property="userId"></result>
        <result column="number" property="number"></result>
        <result column="createtime" property="createtime"></result>
        <result column="note" property="note"></result>
<!--
实现对用户信息进行延迟加载
select:指定延迟加载需要执行的statement的id(是根据user_id查询用户信息的statement)
要使用UserMapper.xml中findUserById完成根据用户id(userid)查询用户信息,如果findUserById不在本mapper 中,需要加上所引用mapper的namespace
column:订单信息中关联用户信息查询的列,是user_id
-->
        <association property="user" select="mybaties.mapper.UserMapper.findUserById" column="user_id">

        </association>
    </resultMap>
    <select id="findOrderAndUserLazyLoading" resultMap="findOrderAndUserLazyLoadingMap">
        SELECT  *  FROM  ORDERS
    </select>

3.测试思路

1、执行mapper方法(findOrderAndUserLazyLoading),内部去调用中的findOrderAndUserLazyLoading只查询orders信息(单表)。

2、在程序中去遍历上一步骤查询出的List<Orders>,当我们调用Orders中的getUser方法时,开始进行延迟加载。

3、延迟加载,去调用UserMapper.xmlfindUserbyId这个方法获取用户信息。

4.测试代码:

//    延迟加载
    @Test
    public void findOrderAndUserLazyLoading() throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        OrderExtensionMapper mapper = sqlSession.getMapper(OrderExtensionMapper.class);
        List<Orders> list = mapper.findOrderAndUserLazyLoading();
        for (Orders orders:list){
            User user = orders.getUser();
            System.out.println(user);
        }
        System.out.println(list);
    }

 查询缓存

什么是查询缓存

mybatis提供查询缓存,用于减轻数据压力,提高数据库性能。

mybaits提供一级缓存,和二级缓存。

mybaties--day02_IT_10

一级缓存是SqlSession级别的缓存。在操作数据库时需要构造 sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。

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

为什么要用缓存?

如果缓存中有数据就不用从数据库中获取,大大提高系统性能。

 一级缓存

 一级缓存工作原理

 mybaties--day02_IT_11

第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从数据库查询用户信息。

得到用户信息,将用户信息存储到一级缓存中。

如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。

第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。

 一级缓存测试

mybatis默认支持一级缓存,不需要在配置文件去配置。

按照上边一级缓存原理步骤去测试。

//    一级缓存
    @Test
    public void catchTest1() throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user1 = mapper.findUserById(1);
        System.out.println(user1);
        User user = user1;
        user.setAddress("测试一级缓存更新数据");
        mapper.updateUser(user);
//        如果sqlSession执行了commit操作(执行插入删除,更新),mybaties会清空对应的一级缓存,这样可以保证缓存中一直是最新数据,避免脏读
//        如果两次查询之间没有进行上述数据更改操作,只会执行一次SQL:SELECT * FROM USER WHERE ID=?,第二次查询的数据是从一级缓存中加载
        sqlSession.commit();
        User user2 = mapper.findUserById(1);
        System.out.println(user2);
    }

 一级缓存应用

正式开发,是将mybatisspring进行整合开发,事务控制在service中。

一个service方法中包括 很多mapper方法调用。

service{

//开始执行时,开启事务,创建SqlSession对象

//第一次调用mapper的方法findUserById(1)

 

//第二次调用mapper的方法findUserById(1),从一级缓存中取数据

//方法结束,sqlSession关闭

}

如果是执行两次service调用查询相同 的用户信息,不走一级缓存,因为session方法结束,sqlSession就关闭,一级缓存就清空。

 二级缓存

 原理

 mybaties--day02_IT_12

首先开启mybatis的二级缓存。

sqlSession1去查询用户id1的用户信息,查询到用户信息会将查询数据存储到二级缓存中。

如果SqlSession3去执行相同 mappersql,执行commit提交,清空该 mapper下的二级缓存区域的数据。

sqlSession2去查询用户id1的用户信息,去缓存中找是否存在数据,如果存在直接从缓存中取出数据。

二级缓存与一级缓存区别,二级缓存的范围更大,多个sqlSession可以共享一个UserMapper的二级缓存区域。

UserMapper只有一个二级缓存区域(按namespace分) ,其它mapper也有自己的二级缓存区域(按namespace分)。

每一个namespacemapper都有一个二缓存区域,两个mappernamespace如果相同,这两个mapper执行sql查询到数据将存在相同 的二级缓存区域中。

1.1.1 开启二级缓存

mybaits的二级缓存是mapper范围级别,除了在SqlMapConfig.xml设置二级缓存的总开关,还要在具体的mapper.xml中开启二级缓存。

在核心配置文件SqlMapConfig.xml中加入

<setting name="cacheEnabled" value="true"/>

 

描述

允许值

默认值

cacheEnabled

对在此配置文件下的所有cache 进行全局性开/关设置。

true false

true

UserMapper.xml中开启二缓存,UserMapper.xml下的sql执行完成会存储到它的缓存区域(HashMap)。

mybaties--day02_IT_13

 

 

 

1.1.1 调用pojo类实现序列化接口

 mybaties--day02_IT_14

 

 

 

为了将缓存数据取出执行反序列化操作,因为二级缓存数据存储介质多种多样,不一定在内存

 测试方法

//    二级缓存
    @Test
    public void catchTest2() throws Exception {
        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        SqlSession sqlSession2 = sqlSessionFactory.openSession();
        SqlSession sqlSession3 = sqlSessionFactory.openSession();
        UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
        UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
        UserMapper mapper3 = sqlSession3.getMapper(UserMapper.class);
        User user1 = mapper1.findUserById(1);
        System.out.println(user1);
//        这里执行关闭操作,将sqlSession中的数据写到二级缓存中
        sqlSession1.close();

//        更新数据了
//        User user3 = mapper3.findUserById(1);
//        user3.setUsername("改名字了");
//        mapper3.updateUser(user3);
////        执行提交,清空UserMapper下面的二级缓存
//        sqlSession3.commit();
//        sqlSession3.close();

        User user2 = mapper2.findUserById(1);
        System.out.println(user2);
        sqlSession2.close();

    }

 useCache配置

statement中设置useCache=false可以禁用当前select语句的二级缓存,即每次查询都会发出sql去查询,默认情况是true,即该sql使用二级缓存。

<select id="findOrderListResultMap" resultMap="ordersUserMap" useCache="false">

总结:针对每次查询都需要最新的数据sql,要设置成useCache=false,禁用二级缓存。

 刷新缓存(就是清空缓存)

mapper的同一个namespace中,如果有其它insertupdatedelete操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读。

 设置statement配置中的flushCache="true" 属性,默认情况下为true即刷新缓存,如果改成false则不会刷新。使用缓存时如果手动修改数据库表中的查询数据会出现脏读。

如下:

<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User" flushCache="true">

总结:一般下执行完commit操作都需要刷新缓存,flushCache=true表示刷新缓存,这样可以避免数据库脏读。

  mybatis整合ehcache

ehcache是一个分布式缓存框架。

分布缓存

我们系统为了提高系统并发,性能、一般对系统进行分布式部署(集群部署方式)

 

mybaties--day02_IT_15

不使用分布缓存,缓存的数据在各各服务单独存储,不方便系统 开发。所以要使用分布式缓存对缓存数据进行集中管理。

mybatis无法实现分布式缓存,需要和其它分布式缓存框架进行整合。

 整合方法(掌握)

mybatis提供了一个cache接口,如果要实现自己的缓存逻辑,实现cache接口开发即可。

mybatisehcache整合,mybatisehcache整合包中提供了一个cache接口的实现类。

mybaties--day02_IT_16

 

 

mybatis默认实现cache类是:

mybaties--day02_IT_17

 

 加入ehcache

 

<!--    分布式缓存ehcache-->
    <dependency>
      <groupId>net.sf.ehcache</groupId>
      <artifactId>ehcache-core</artifactId>
      <version>2.6.5</version>
    </dependency>
<!--    mybaties整合ehcache包-->
    <!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
    <dependency>
      <groupId>org.mybatis.caches</groupId>
      <artifactId>mybatis-ehcache</artifactId>
      <version>1.0.2</version>
    </dependency>

整合ehcache

配置mappercache中的typeehcache的cache接口的实现类型。

<mapper namespace="mybaties.mapper.UserMapper">
<!--&lt;!&ndash;    开启二级缓存&ndash;&gt;-->
<!--    <cache></cache>-->
<!--
开启本mapper的namespace下的二缓存
type:指定cache接口的实现类的类型,mybatis默认使用PerpetualCache
要和ehcache整合,需要配置type为ehcache实现cache接口的类型
-->
    <cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>

 加入ehcache的配置文件

classpath下配置ehcache.xml

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
    <diskStore path="F:\develop\ehcache" />
    <defaultCache
            maxElementsInMemory="1000"
            maxElementsOnDisk="10000000"
            eternal="false"
            overflowToDisk="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
    </defaultCache>
</ehcache>

 二级应用场景

对于访问多的查询请求且用户对查询结果实时性要求不高,此时可采用mybatis二级缓存技术降低数据库访问量,提高访问速度,业务场景比如:耗时较高的统计分析sql、电话账单查询sql等。

实现方法如下:通过设置刷新间隔时间,由mybatis每隔一段时间自动清空缓存,根据数据变化频率设置缓存刷新间隔flushInterval,比如设置为30分钟、60分钟、24小时等,根据需求而定。

mybaties--day02_IT_18

 

 二级缓存局限性

mybatis二级缓存对细粒度的数据级别的缓存实现不好,比如如下需求:对商品信息进行缓存,由于商品信息查询访问量大,但是要求用户每次都能查询最新的商品信息,此时如果使用mybatis的二级缓存就无法实现当一个商品变化时只刷新该商品的缓存信息而不刷新其它商品的信息,因为mybaits的二级缓存区域以mapper为单位划分,当一个商品信息变化会将所有商品信息的缓存数据全部清空。解决此类问题需要在业务层根据需求对数据有针对性缓存。

springmybatis整合

整合思路

需要spring通过单例方式管理SqlSessionFactory

springmybatis整合生成代理对象,使用SqlSessionFactory创建SqlSession。(springmybatis整合自动完成)

持久层的mapper都需要由spring进行管理。

1.2 整合环境

<!--    mybaties和spring整合包-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.2.2</version>
    </dependency>

 sqlSessionFactory

applicationContext.xml配置sqlSessionFactory和数据源

sqlSessionFactorymybatisspring的整合包下。

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.2.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">
    <context:property-placeholder location="db.properties"></context:property-placeholder>
<!--sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--    加载mybaties配置文件-->
    <property name="configLocation" value="SqlMapConfig.xml"></property>
<!--    数据源-->
    <property name="dataSource" ref="comboPooledDataSource"></property>
</bean>

<!--    数据源-->
    <!--    创建数据源的bean:使用ComboPooledDataSource数据源需要导入外部jar包:c3p0-->
    <bean id="comboPooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"></property>
        <property name="user" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

</beans>

剩下的省略....