MyBatis高级结果映射(一对一、一对多、多对多的映射),延迟加载,查询缓存(一级缓存),二级缓存的学习记录;
1、学习中所使用到的例子,数据库基础分析
2、高级结果映射
3、延迟加载
4、一级缓存
5、二级缓存
1、学习中所使用到的例子,数据库基础分析
2、高级结果映射
resultType与resultMap:
- resultType来进行结果映射,数据库中查询几条记录,那么在resultType就会映射成几条记录;resultType映射是一个平铺式的映射;
- resultMap比较繁琐一些,是一种层级式映射,在企业中如果没有特殊要求,一般使用的都是resultType来完成一对一的映射;
- resultType实现查询机制,需要自定义pojo,pojo属性名和sql查询的列名是一致的;企业开发中的resultType简单方便便于使用
- resultMap可以将sql查询结果信息中的部分属性映射到一个pojo中;需要的是映射配置;并且如果有查询到其他表中的内容首先要进行的是实体类关系的分析,以及映射关系的分析,并且分清一对一,一对多,多对多的关系;
下面的例子中会使用上述的购物车的数据库结构创建表,并且生成实体po类,进行操作(代码只会贴出关键代码块);
例子1:订单信息与用户信息
使用实体PO类:
OrderCustom.java
package com.mybatis.demo.po;
/**
* 继承一个需要信息最多的类
* @author YQ
*
*/
public class OrderCustom extends Orders {
// 用户名称
private String username;
// 用户地址
private String address;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
resultType的使用
<!-- 需要使用resultType实现订单查询,关联用户信息 -->
<!--
resultType必须要包含所有的查询结果类
自定义POJO类
-->
<select id="findOrderUserList" resultType="com.mybatis.demo.po.OrderCustom">
SELECT orders.* , user.username , user.address
FROM orders , USER WHERE orders.user_id = user.id
</select>
使用resultMap完成例子一中的需求:
<!-- 定义订单信息与用户信息的resultMap -->
<!-- 将结果做一个映射 -->
<resultMap type="orders" id="ordersUserResultMap">
<!--
id:订单信息的唯一约束
如果存在多个字段决定唯一记录,id标签需要定义多个
-->
<id column="id" property="id"/>
<result column="order_number" property="order_number"/>
<result column="user_id" property="user_id"/>
<!--
配置用户映射信息
将sql查询的用户信息映射到orders中的user属性中
association:用于单个关联对象的映射
property:将关联信息映射到orders的哪一个属性
javaType:映射属性的类型
-->
<association property="user" javaType="com.mybatis.demo.po.User">
<!--
id:关联用户信息的唯一约束
property:id指定的列映射到关联com.mybatis.demo.po.User
-->
<id column="user_id" property="id"/>
<result column="username" property="username"/>
<result column="address" property="address"/>
</association>
</resultMap>
使用resultMap
<select id="findOrderUserListResultMap" resultMap="ordersUserResultMap">
SELECT orders.* , user.username , user.address
FROM orders , USER WHERE orders.user_id = user.id
</select>
例子2:订单与订单明细查询
resultMap定义如下:
<!--
定义订单以及订单明细的resultMap
如果是跨namespace就可以在前面加上namespace
-->
<resultMap type="orders" id="ordersUserDetailResultMap" extends="ordersUserResultMap">
<!-- 订单以及用户信息,继承于ordersUserResultMap -->
<!--
映射订单明细信息
colllection:映射集合对象(表示的是多的关系)
property:将明细信息映射到哪个集合属性中
ofType:集合中对象的类型
-->
<collection property="orderdetails" ofType="com.mybatis.demo.po.Orderdetail">
<!--
id:订单明细的唯一约束
-->
<id column="orderdetail_id" property="id"/>
<result column="item_id" property="item_id"/>
<result column="item_num" property="item_num"/>
<result column="item_price" property="item_price"/>
</collection>
</resultMap>
在Orders类中需要定义一个订单明细的集合对象:
//订单明细,需要给出该对象的get与set方法
private List<Orderdetail> orderdetails;
使用定义的resultMap
<!-- 查询用户与订单明细信息 -->
<select id="findOrdersUserDetailList" resultMap="ordersUserDetailResultMap">
SELECT
orders.*,
user.username,
user.address,
orderdetail.item_id,
orderdetail.item_num,
orderdetail.item_price
FROM
orders,
USER,
orderdetail
WHERE
orders.user_id = user.id AND orders.id = orderdetail.orders_id
</select>
例子3:订单明细与商品信息
<!-- 定义订单明细以及商品信息 -->
<resultMap type="orders" id="ordersUserDetailItemResultMap"
extends="ordersUserResultMap">
<!-- 订单与用户信息,继承自ordersUserResultMap -->
<!--
映射订单信息collection:映射集合对象property;将明细信息映射到集合属性中!
ofType:集合对象的类型
-->
<collection property="orderdetails" ofType="com.mybatis.demo.po.Orderdetail">
<!--
id:订单明细的唯一约束
-->
<id column="orderdetail_id" property="id"/>
<result column="item_id" property="item_id"/>
<result column="item_num" property="item_num"/>
<result column="item_price" property="item_price"/>
<!--
映射商品信息
映射商品属性
-->
<association property="items" javaType="com.mybatis.demo.po.Items">
<id column="item_id" property="id"/>
<result column="item_name" property="item_name"/>
<result column="item_price" property="item_price"/>
<result column="item_detail" property="item_detail"/>
</association>
</collection>
</resultMap>
使用上述定义的resultMap
<!-- 查询订单以及订单明细信息以及商品信息 -->
<select id="findOrderUserDetailItemList"
resultMap="ordersUserDetailItemResultMap">
SELECT
orders.*,
user.username,
user.address,
orderdetail.id orderdetail_id,
orderdetail.item_id,
orderdetail.item_num,
orderdetail.item_price ,
items.item_detail,
items.item_name,
items.item_price
FROM
orders,
USER,
orderdetail,
items
WHERE
orders.user_id = user.id
AND
orders.id = orderdetail.orders_id
AND
orderdetail.item_id = items.id
</select>
PO类中的定义:
在OrderDerail类中需要添加以下对象:
//商品信息
private Items items;//明细对应的商品信息
3、延迟加载
- 延迟加载对于上述例子中的需求:首次只查询订单信息,当需要关联查询用户信息时,再查询用户信息。
- 延迟加载相对于之前学习的hibernate的概念就是懒加载,先去查询主表信息,如果用到从表的数据的话,再去查询从表的信息。Mybatis中resultMap可以实现延迟加载
在sqlMapConfig.xml中进行配置与开启:
<!-- 全局参数的配置,开启延迟加载 -->
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
<!-- 开启二级缓存总开关 -->
<setting name="cacheEnabled" value="true"/>
</settings>
在mapper中使用延迟加载:
<!-- 订单与用户的resultMap,实现延迟加载 -->
<resultMap type="orders" id="ordersUserResultMapLazy">
<!-- 配置订单信息的映射 -->
<id column="id" property="id"/>
<result column="user_id" property="user_id"/>
<result column="order_number" property="order_number"/>
<!--
配置延迟加载,加载用户信息
select:延迟加载时候调用的statement
colum:将哪一列的值传到延迟加载的statement中
-->
<association property="user" javaType="com.mybatis.demo.po.User"
select="com.mybatis.demo.mapper.UserMapper.findUserById" column="user_id">
<id column="id" property="id"/>
</association>
</resultMap>
<!-- 订单信息查询,需要使用延迟加载 -->
<select id="findOrderList" resultMap="ordersUserResultMapLazy">
select * from orders
</select>
调试效果如下:
首先调试测试方法:
//测试延迟加载
@Test
public void findOrderList() throws Exception {
SqlSession session = sqlSessionFactory.openSession();
OrdersMapperCustom orderMapperCustom = session.getMapper(OrdersMapperCustom.class);
List<Orders> list = orderMapperCustom.findOrderList();
for (Orders orders : list) {
@SuppressWarnings("unused")
int order_id = orders.getId(); //去订单ID;断点1
@SuppressWarnings("unused")
User user = orders.getUser(); //查询用户信息;断点2
}
session.close();
}
4、一级缓存
首先先看一下一级缓存与二级缓存在mybatis中的关系:
- 一级缓存是SqlSession级别的缓存。在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。
- 二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
一级缓存原理:
第一次查询id为X的用户,此时先去一级缓存查找,如果查找不到,则去数据库查询,把查询后的 结果存储到一级缓存中;
第二次查询id为X的用户,此时先去一级缓存查找,如果查找到,则直接从一级缓存中把数据取出,不去查询数据库;
只要中间发生增删改操作,那么一级缓存就清空;
5、二级缓存
- 原理
- 第一次查询id为1 的用户,此时先去二级缓存查找,如果查找不到,则去数据库查询,把查询后的 结果存储到二级缓存中。
- 第二次查询id为1 的用户,此时先去二级缓存查找,如果查找到,则直接从二级缓存中把数据取出,不去查询数据库。
- 只要中间发生增删改操作,那么二级缓存就清空。
- 二级缓存默认是不开启的。
步骤一:开启二级缓存:
在sqlMapConfig.xml中开启二级缓存总开关:
<!-- 开启二级缓存总开关 -->
<setting name="cacheEnabled" value="true"/>
步骤二:开启二级缓存:
<!-- 开启二级缓存 -->
<cache></cache>
步骤三:对象序列化(必须)
implements Serializable
步骤四:测试即可
如何刷新缓存:
- 在映射文件的statement中设置flushCache=true可以刷新当前的二级缓存,默认情况下如果是select语句,那么flushCache是false。如果是insert、update、delete语句,那么flushCache是true;
- 如果查询语句设置成true,那么每次查询都是去数据库查询,即意味着该查询的二级缓存失效;
- 如果查询语句设置成false,即使用二级缓存,那么如果在数据库中修改了数据,而缓存数据还是原来的,这个时候就会出现脏读。
禁用缓存:
<select id="findUserById" parameterType="int" resultType="user" useCache="false">
SELECT * FROM USER WHERE id = #{id}
</select>