【MyBatis】的高级关系映射和查询缓存详解

1.高级结果映射(一对一、一对多、多对多)

  • 一对一映射
1)resultType
/**
 *订单信息实体类
 * @see
 * @since
 */
public class Orders { 
    private Integer id;
    private Integer user_id;    //下单用户id
    private String number;      //订单号
    private Date createtime;    //创建订单时间 
    private String note;        //备注     
    private String userName;    //用户名
    private String sex;         //性别

    private User user;          //下单用户  
    private List<OrderDetail> orderDetailList;  //订单明细
    private List<Items> itemsList;  //商品
    ....
    省略getXxx()和setXxx()
}

public class User {
    private Integer id;
    private String userName;    //用户名
    private String sex;         //性别
    private String birthday;    //生日
    private String address;     //地址    
    private List<Items> itemsList;  //商品
    private List<Orders> orders;    //订单信息
    ....
    省略getXxx()和setXxx()
}

映射文件:
<select id="findOrdersAndUser" resultType="com.mybatis.assess.pojo.Orders">
    SELECT 
      orders.`id`,
      orders.`user_id`,
      orders.`number`,
      user.`username`,
      user.`sex` 
    FROM
      orders,
      USER 
    WHERE orders.`user_id` = user.`id`;
</select>

Mapper接口:
public interface OrdersMapper {
    //一对一之resultType
    List<Orders> findOrdersAndUser() throws Exception;
}

测试代码:
/**
 * 查询订单信息,关联查询用户信息
 * @throws Exception
 */
@Test
public void findOrdersAndUser() throws Exception{
    SqlSession sqlSession = getSqlSessionFactory().openSession();
    OrdersMapper mapper = sqlSession.getMapper(OrdersMapper.class); 
    List<Orders> list = mapper.findOrdersAndUser();
    System.out.println(list.toString());
    //关闭资源
    sqlSession.close();
}

小结:使用resultType来进行一对一结果映射,查询出的列的个数和映射的属性的个数要一致。而且映射的属性要存在与一个大的对象中,它是一种平铺式的映射,即数据库查询出多少条记录,则映射成多少个对象。

2)resultMap
使用resultMap来进行一对一结果映射,它是将关联对象添加到主信息的对象中,具体说是对象嵌套对象的一种映射方式。

映射文件:
<!-- id:用于标识resultMap,type值为resultMap的结果类型 -->
<resultMap type="Orders" id="OrdersMap">
    <!-- 基本数据类型映射,column:数据库的列,property:该列所对应的java属性 -->
    <id column="id" property="id"/>
    <result column="user_id" property="user_id"/>
    <result column="number" property="number"/>
    <result column="createtime" property="createtime"/>
    <result column="note" property="note"/>

    <!-- 1)配置关联对象: 
            association:映射一对一关联关系
            property:当前Orsders对象的关联对象名
            javaType:关联对象属性所属的完全限定类名或别名
            select:所请求的select映射的ID,该select使用子查询获取其所关联的属性对象
            column:传给子查询的字段,即外键列
    -->     
        <association property="user" javaType="com.mybatis.assess.pojo.User" column="user_id">
            <result column="userName" property="userName"/>
            <result column="sex" property="sex"/>
        </association>      
</resultMap>

<select id="findOrdersAndUserRstMap" resultMap="OrdersMap">
    SELECT 
      orders.`id`,
      orders.`user_id`,
      orders.`number`,
      user.`username`,
      user.`sex` 
    FROM
      orders,
      USER 
    WHERE orders.`user_id` = user.`id`;
</select>

测试代码:
@Test
public void findOrdersAndUserRstMap() throws Exception{
    SqlSession sqlSession = getSqlSessionFactory().openSession();   
    OrdersMapper mapper = sqlSession.getMapper(OrdersMapper.class); 
    List<Orders> list = mapper.findOrdersAndUser(); 
    System.out.println(list.toString());    
    //关闭资源
    sqlSession.close();
}

小结:在一对一结果映射时,使用resultType更加简单方便,如果有特殊要求(对象嵌套对象)时,需要使用resultMap进行映射,比如:查询订单列表,然后在点击列表中的查看订单明细按钮,这个时候就需要使用resultMap进行结果映射。而resultType更适应于查询明细信息,比如,查询订单明细列表。

  • 一对多映射
映射文件:
<resultMap type="Orders" id="OrdersMap">
    <!-- 基本数据类型映射,column:数据库的列,property:该列所对应的java属性 -->
    <id column="id" property="id"/>
    <result column="user_id" property="user_id"/>
    <result column="number" property="number"/>
    <result column="createtime" property="createtime"/>
    <result column="note" property="note"/>

    <!-- 1)配置关联对象: 
            association:映射一对一关联关系
            property:当前Orsders对象的关联对象名
            javaType:关联对象属性所属的完全限定类名或别名
            select:所请求的select映射的ID,该select使用子查询获取其所关联的属性对象
            column:传给子查询的字段,即外键列
    -->     
    <association property="user" javaType="com.mybatis.assess.pojo.User" column="user_id">
        <result column="userName" property="userName"/>
        <result column="sex" property="sex"/>
    </association>

    <!-- 2)配置关联对象:
            collection:映射一对多关联关系
            property:集合属性的值
            ofType:集合中元素的类型
     -->
    <collection property="orderDetailList" ofType="com.mybatis.assess.pojo.OrderDetail">
        <id column="id" property="id"/>
        <result column="items_id" property="items_id"/>
        <result column="orders_id" property="orders_id"/>
        <result column="items_num" property="items_num"/>
    </collection>
</resultMap>

<select id="findOrdersAndOrderDetailRstMap" resultMap="OrdersMap">
    SELECT 
      orders.`id`,
      orders.`user_id`,
      orders.`number`,
      user.`username`,
      user.`sex`,
      orderdetail.`id` detailId,
      orderdetail.`items_id`,
      orderdetail.`items_num` 
    FROM
      orders,
      USER,
      orderdetail 
    WHERE orders.`user_id` = user.`id` 
      AND orders.`id` = orderdetail.`orders_id`;
</select>

测试代码:
/**
 * 查询订单信息,关联查询订单详情
 * @throws Exception
 */
@Test
public void findOrdersAndOrderDetailRstMap() throws Exception{
    SqlSession sqlSession = getSqlSessionFactory().openSession();   
    OrdersMapper mapper = sqlSession.getMapper(OrdersMapper.class); 
    List<Orders> orders = mapper.findOrdersAndOrderDetailRstMap();  
    for(Orders order : orders){
        System.out.println(order.getOrderDetailList().toString());
        System.out.println(order.getUser());
    }    
    //关闭资源
    sqlSession.close();
}
  • 多对多映射
    多对多映射是一对多映射的特例
/**
 * 订单明细实体类
 * @see
 * @since
 */
public class OrderDetail {
    private Integer id;
    private Integer orders_id;  //订单id
    private Integer items_id;   //商品id
    private Integer items_num;  //商品购买数量
    private Orders orders;      //订单    
    private Items items;    //商品信息
    .....
    //省略getXxx()和setXxx()
}   
/**
 * 商品信息实体类
 * @see
 * @since
 */
public class Items {    
    private Integer id;
    private String name;        //商品名称
    private Float price;        //商品定价
    private String detail;      //商品描述
    private String pic;         //商品图片
    private Date createtime;    //生产日期  
    private List<Orders> OrdersList; //订单
    .....
    //省略getXxx()和setXxx()
}   

Mapper接口:
public interface OrdersMapper {
    List<User> findUserAndItemsRstMap() throws Exception;   
}

映射文件:
<resultMap type="com.mybatis.assess.pojo.User" id="UserAndItemsRstMap">
    <!-- 用户信息 -->
    <id column="user_id" property="id"/>
    <result column="userName" property="userName"/>
    <result column="sex" property="sex"/>
    <!-- 订单信息(一对多) -->
    <collection property="orders" ofType="com.mybatis.assess.pojo.Orders">
        <id column="orders_id" property="id"/>
        <result column="user_id" property="user_id"/>
        <result column="number" property="number"/>
        <!-- 订单明细信息(一对多) -->
        <collection property="orderDetailList" ofType="com.mybatis.assess.pojo.OrderDetail">
            <id column="id" property="id"/>
            <result column="items_id" property="items_id"/>
            <result column="orders_id" property="orders_id"/>
            <result column="items_num" property="items_num"/>
            <!-- 商品信息(一对一) -->
            <collection property="items" javaType="com.mybatis.assess.pojo.Items" column="items_id">
                <result column="name" property="name"/>
                <result column="price" property="price"/>
            </collection>   
        </collection>
    </collection>
</resultMap>

<select id="findUserAndItemsRstMap" resultMap="OrdersMap">
    SELECT 
      orders.`id`,
      orders.`user_id`,
      orders.`number`,
      user.`username`,
      user.`sex`,
      orderdetail.`id` detailId,
      orderdetail.`items_id`,
      orderdetail.`items_num`,
      items.`name`,
      items.`price` 
    FROM
      orders,
      USER,
      orderdetail,
      items 
    WHERE orders.`user_id` = user.`id` 
      AND orders.`id` = orderdetail.`orders_id` 
      AND orderdetail.`items_id` = items.`id`;
</select>

测试代码:
/**
 * 查询用户信息,关联查询该用户购买的商品信息
 * @throws Exception
 */
@Test
public void findUserAndItemsRstMap() throws Exception{
    SqlSession sqlSession = getSqlSessionFactory().openSession();
    OrdersMapper mapper = sqlSession.getMapper(OrdersMapper.class);
    List<User> list = mapper.findUserAndItemsRstMap();
    //关闭资源
    sqlSession.close();
}

2.延迟加载

  • 2.1什么是延迟加载
    延迟加载又叫懒加载,也叫按需加载。也就是说先加载主信息,在需要的时候,再去加载从信息。在mybatis中,resultMap标签 的association标签和collection标签具有延迟加载的功能。
OrdersMapper映射文件:
<resultMap type="Orders" id="lazyLoadingRstMap">
    <id column="id" property="id"/>
    <result column="user_id" property="user_id"/>
    <result column="number" property="number"/>
    <result column="createtime" property="createtime"/>

    <association property="user" select="com.mybatis.assess.dao.IUserDao.queryUserById" column="user_id">
        <result column="userName" property="userName"/>
        <result column="sex" property="sex"/>
    </association>
</resultMap>

<!-- 延迟加载 -->
<select id="findOrderAndUserLazyLoading" resultMap="lazyLoadingRstMap">
    select * from Orders;
</select>

UserMapper映射文件:
<?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.mybatis.assess.dao.UserMapper">  
    <select id="queryUserById" parameterType="int" resultType="User">
        select * from user where id = #{id}
    </select>
</mapper>

测试代码:
@Test
public void findOrderAndUserLazyLoading() throws Exception{
    SqlSession sqlSession = getSqlSessionFactory().openSession();   
    OrdersMapper mapper = sqlSession.getMapper(OrdersMapper.class); 
    List<Orders> list = mapper.findOrderAndUserLazyLoading();   
    for (Orders orders : list) {
        //在需要的时候,再去加载从信息(数据库)
        System.out.println(orders.getUser());
    }   
    //关闭资源
    sqlSession.close();
}
  • 2,2设置延迟加载
<settings>
    <!--开启延迟加载,默认值为true-->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!--设置积极的懒加载,false的话按需加载,默认true-->
    <setting name="aggressiveLazyLoading" value="false"/>
</settings> 

3.查询缓存

  • 3.1Mybatis的缓存理解
    这里写图片描述
  • 3.2一级缓存
    这里写图片描述
测试代码:
@Test
public void oneLevelCatch() throws Exception{
    SqlSession sqlSession = getSqlSessionFactory().openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user1 = mapper.queryUserById(1);
    System.out.println(user1);
    System.out.println("========================");
    User user2 = mapper.queryUserById(1);
    System.out.println(user2);
    //关闭资源
    sqlSession.close();
}

这里写图片描述

@Test
public void oneLevelCatchTest() throws Exception{
    SqlSession sqlSession = getSqlSessionFactory().openSession();
    IUserDao mapper = sqlSession.getMapper(IUserDao.class);
    User user1 = mapper.queryUserById(1);
    System.out.println(user1);

    //执行commit,将一级缓存清空
//      sqlSession.commit();
    sqlSession.clearCache();

    System.out.println("========================");
    User user2 = mapper.queryUserById(1);
    System.out.println(user2);
    //关闭资源
    sqlSession.close();
}

这里写图片描述

  • 3.3二级缓存
    这里写图片描述
1.开启二级缓存
<settings>
    <!-- 二级缓存总开关  默认是false -->
    <setting name="cacheEnabled" value="true"/>
</settings> 
2.在mapper映射文件中开启二级缓存
    <!-- 开启二级缓存,默认使用了PerpetualCache -->
    <cache/>
3.序列化

这里写图片描述

@Test
public void twoLevelCatch() throws Exception{
    SqlSession sqlSession = getSqlSessionFactory().openSession();
    SqlSession sqlSession2 = getSqlSessionFactory().openSession();
    SqlSession sqlSession3 = getSqlSessionFactory().openSession();

    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
    UserMapper mapper3 = sqlSession3.getMapper(UserMapper.class);

    User user1 = mapper.queryUserById(1);
    System.out.println(user1);
    //在colse的时候,才会将数据写入二级缓存中
    sqlSession.close();
    System.out.println("========================");

    User user2 = mapper2.queryUserById(1);
    System.out.println(user2);
    //在colse的时候,才会将数据写入二级缓存中
    sqlSession2.close();
    System.out.println("========================");

    sqlSession3.close();
}

这里写图片描述

@Test
public void twoLevelCatchTest() throws Exception{
    SqlSession sqlSession = getSqlSessionFactory().openSession();
    SqlSession sqlSession2 = getSqlSessionFactory().openSession();
    SqlSession sqlSession3 = getSqlSessionFactory().openSession();

    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
    UserMapper mapper3 = sqlSession3.getMapper(UserMapper.class);

    User user1 = mapper.queryUserById(1);
    System.out.println(user1);
    //在colse的时候,才会将数据写入二级缓存中
    sqlSession.close();
    System.out.println("========================");

    //执行commit,将一级缓存清空
//      sqlSession3.commit();
    sqlSession.clearCache();
    System.out.println("========================");

    User user2 = mapper2.queryUserById(1);
    System.out.println(user2);
    //在colse的时候,才会将数据写入二级缓存中
    sqlSession2.close();
    System.out.println("========================");

    sqlSession3.close();
}

这里写图片描述

  • 3.4禁用缓存
    这里写图片描述
  • 3.5刷新缓存
    这里写图片描述
  • 3.6整合ehcache
    Mybatis本身是一个持久层框架,它不是专门的缓存框架,所以它对缓存的实现不够好,不能支持分布式。Ehcache是一个分布式的缓存框架。
1.什么是分布式
系统为了提高性能,通常会对系统采用分布式部署(集群部署方式)

这里写图片描述

2.整合思路
Cache是一个接口,它的默认实现是mybatis的PerpetualCache。如果想整合mybatis的二级缓存,那么实现Cache接口即可。
2.1添加jar
ehcache-core-2.6.5.jar
mybatis-ehcache-1.0.2.jar
2.2设置映射文件中cache标签的type值为ehcache的实现类

这里写图片描述

3.添加ehcache的配置文件
在config下,创建ehcache.xml

这里写图片描述

4.测试ehcache的二级缓存

这里写图片描述

  • 3.7应用场景
    使用场景:对于访问响应速度要求高,但是实时性不高的查询,可以采用二级缓存技术。
    注意:在使用二级缓存的时候,要设置一下刷新间隔(cache标签中有一个flashInterval属性)来定时刷新二级缓存,这个刷新间隔根据具体需求来设置,比如设置30分钟、60分钟等,单位为毫秒。
  • 3.8局限性
    Mybatis二级缓存对细粒度的数据,缓存实现不好。
    场景:对商品信息进行缓存,由于商品信息查询访问量大,但是要求用户每次查询都是最新的商品信息,此时如果使用二级缓存,就无法实现当一个商品发生变化只刷新该商品的缓存信息而不刷新其他商品缓存信息,因为二级缓存是mapper级别的,当一个商品的信息发送更新,所有的商品信息缓存数据都会清空。
    解决此类问题,需要在业务层根据需要对数据有针对性的缓存。
    比如可以对经常变化的 数据操作单独放到另一个namespace的mapper中。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值