mybatis专栏 https://blog.csdn.net/worn_xiao/category_6530299.html?spm=1001.2014.3001.5482
一Mybaties关联查询与缓存
基本数据模型:
如上所示是我们电商系统的基本数据模型
1.1 一对一映射
模拟一个订单只能属于一个用户的,一对一映射。
SELECT
orders.`id`,
orders.`user_id`,
orders.`number`,
user.`username`,
user.`sex`
FROM
orders,
USER
WHERE orders.`user_id` = user.`id`
1.1.1 以resultType作为扩展类
创建可扩展的PO类,扩展订单实体:
publicclass OrdersExt extends Orders{
//添加用户属性
/*USER.username,
USER.address */
private String username;
private String address;
public String getUsername() {
returnusername;
}
publicvoid setUsername(String username) {
this.username = username;
}
public String getAddress() {
returnaddress;
}
publicvoid setAddress(String address) {
this.address = address;
}
}
创建mapper映射接口:
public List<OrdersExt> findOrdersUser();
创建映射文件:
<mapper namespace="cn.itcast.mybatis.mapper.OrdersMapper">
<!-- 定义查询订单表列名的SQL片段 -->
<sql id="select_orders">
Orders.id,
Orders.user_id,
orders.number,
orders.createtime,
orders.note
</sql>
<!-- 定义查询用户表列名的SQL片段 -->
<sql id="select_user">
user.username,
user.address
</sql>
<!-- 进行订单信息查询,包括用户的名称和地址信息 -->
<select id="findOrdersUser" resultType="OrdersExt">
Select
<include refid="select_orders"/>
<include refid="select_user"></include>
from orders,user
where orders.user_id = user.id
</select>
</mapper>
加载映射文件
<!-- 批量加载mapper文件,需要mapper接口文件和mapper映射文件名称相同且在同一个包下 -->
<package name="cn.itcast.mybatis.mapper"/>
测试代码
@Test
publicvoid testFindOrdersUser(){
// 创建sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 通过SqlSession构造usermapper的代理对象
OrdersMapper ordersMapper = sqlSession.getMapper(OrdersMapper.class);
// 调用usermapper的方法
List<OrdersExt> list = ordersMapper.findOrdersUser();
System.out.println(list);
// 释放SqlSession
sqlSession.close();
}
1.1.2 以resultMap作为扩展
vo实体类
publicclass Orders {
private Integer id;
private Integer userId;
private String number;
private Date createtime;
private String note;
private User user;
mapper接口:
public List<OrdersExt> findOrdersUserRstMap();
<!-- 进行订单信息查询,包括用户的名称和地址信息 (ResultMap)-->
<select id="findOrdersUserRstMap" resultMap="OrdersUserRstMap">
Select
<include refid="select_orders"/>
,
<include refid="select_user"></include>
from orders,user
where orders.user_id = user.id
xml映射文件
</select>
<!-- 定义orderUserResultMap -->
<resultMap type=" cn.itcast.mybatis.po.Orders" id="OrdersUserRstMap">
<id column="id"property="id" />
<result column="user_id"property="userId" />
<result column="number"property="number" />
<result column="createtime"property="createtime" />
<result column="note"property="note" />
<!-- 映射一对一关联关系的用户对象-->
<!--
property:指定关联对象要映射到Orders的哪个属性上
javaType:指定关联对象所要映射的java类型
-->
<!-- id标签:指定关联对象结果集的唯一标识,很重要,不写不会报错,但是会影响性能 -->
<association property="user"javaType="cn.itcast.mybatis.po.User">
<id column="user_id"property="id" />
<result column="username"property="username" />
<result column="address"property="address" />
</association>
</resultMap>
测试代码
@Test
publicvoid testFindOrdersUserRstMap() {
// 创建sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 通过SqlSession构造usermapper的代理对象
OrdersMapper ordersMapper = sqlSession.getMapper(OrdersMapper.class);
// 调用usermapper的方法
List<Orders> list = ordersMapper.findOrdersUserRstMap();
//此处我们采用debug模式来跟踪代码,然后验证结果集是否正确
System.out.println(list);
// 释放SqlSession
sqlSession.close
resultType:使用resultType实现较为简单,如果pojo中没有包括查询出来的列名,需要增加列名对应的属性,即可完成映射。
如果没有查询结果的特殊要求建议使用resultType。
resultMap:需要单独定义resultMap,实现有点麻烦,如果对查询结果有特殊的要求,使用resultMap可以完成将关联查询映射pojo的对象属性中。
resultMap可以实现延迟加载,resultType无法实现延迟加载。
1.2 一对多映射
业务场景描述:一个订单有多个订单明细。
Select
Orders.id,
Orders.user_id,
orders.number,
orders.createtime,
orders.note,
user.username,
user.address,
orderdetail.iddetail_id,
orderdetail.items_id,
orderdetail.items_num
from orders,user,orderdetail
where orders.user_id = user.id
andorders.id = orderdetail.orders_id
修改订单实体
在Orders类中添加以下属性,并提供get/set方法:
//订单明细
private List<Orderdetail> detailList;
mapper接口
// 查询订单信息及订单明细信息(一对多映射之使用resultMap)
public List<Orders> findOrdersAndOrderdetailRstMap();
编写映射文件Mapper.xml
<!-- 定义OrdersAndOrderdetailRstMap -->
<!-- extends:继承已有的ResultMap,值为继承的ResultMap的唯一标示 -->
<resultMap type="Orders"id="OrdersAndOrderdetailRstMap"
extends="OrdersUserRstMap">
<!-- 映射关联关系(一对多) -->
<!-- collection标签:定义一个一对多关系
ofType:指定该集合参数所映射的类型
-->
<collection property="detailList"ofType="Orderdetail">
<id column="detail_id"property="id" />
<result column="items_id"property="itemsId" />
<result column="items_num"property="itemsNum" />
</collection>
</resultMap>
<!-- 查询订单信息,包括用户名称、用户地址,订单商品信息(嵌套结果) -->
<select id="findOrdersAndOrderdetailRstMap"resultMap="OrdersAndOrderdetailRstMap">
Select
<include refid="select_orders"/>,
<include refid="select_user"/>,
orderdetail.id detail_id,
orderdetail.items_id,
orderdetail.items_num
from orders,user,orderdetail
where orders.user_id = user.id
and
orders.id = orderdetail.orders_id
</select>
resultMap的extends属性:可以用此属性来继承一个已有的resultmap。但是它继承的resultMap的type和它本身的type要保持一致。通过resultmap的继承,可以减少mapper的冗余映射。
测试代码
@Test
publicvoid testFindOrdersAndOrderdetailRstMap() {
// 创建sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 通过SqlSession构造usermapper的代理对象
OrdersMapper ordersMapper = sqlSession.getMapper(OrdersMapper.class);
// 调用usermapper的方法
List<Orders> list = ordersMapper.findOrdersAndOrderdetailRstMap();
//此处我们采用debug模式来跟踪代码,然后验证结果集是否正确
System.out.println(list);
// 释放SqlSession
sqlSession.close();
mybatis使用resultMap的collection对关联查询的多条记录映射到一个list集合属性中。
1.3 多对多映射
用户信息与商品信息的多对多关系
select
Orders.id,
Orders.user_id,
orders.number,
orders.createtime,
orders.note,
user.username,
user.address,
orderdetail.iddetail_id,
orderdetail.items_id,
orderdetail.items_num
items.name items_name,
items.detail items_detail
FROM
orders,
USER,
orderdetail,
items
WHERE user.`id` = orders.`user_id`
ANDorders.`id` = orderdetail.`orders_id`
ANDorderdetail.`items_id` = items.`id`
修改实体信息,分别在用户中添加订单信息,订单中添加订单明细,订单明细中添加订单项
在user类中添加List<Orders> ordersList 属性
// 订单信息
private List<Orders> ordersList;
在Orders类中添加List<Orderdetail>属性
//订单明细
private List<Orderdetail> detailList;
在Orderdetail类中添加Items属性
//商品信息
private Items items;
接口定义
//查询用户及用户购买商品信息(多对多映射之使用resultMap)
public List<User> findUserAndItemsRstMap();
编写映射文件
<!-- 定义UserAndItemsRstMap -->
<resultMap type="User" id="UserAndItemsRstMap">
<!-- 用户信息 -->
<!-- id:关联查询用户的唯一标示 -->
<id column="user_id"property="id" />
<result column="username"property="username" />
<result column="address"property="address" />
<!-- 订单信息(一个用户有多个订单) -->
<collection property="ordersList"ofType="orders">
<id column="id"property="id" />
<result column="user_id"property="userId" />
<result column="number"property="number" />
<result column="createtime"property="createtime" />
<result column="note"property="note" />
<!-- 订单明细信息(一个订单有多个订单明细) -->
<collection property="detailList"ofType="orderdetail">
<id column="detail_id"property="id" />
<result column="items_id"property="itemsId" />
<result column="items_num"property="itemsNum" />
<!-- 商品信息(一个订单明细对应一个商品) -->
<association property="items"javaType="cn.itcast.mybatis.po.Items">
<id column="items_id"property="id" />
<result column="items_name"property="name" />
<result column="items_detail"property="detail" />
</association>
</collection>
</collection>
</resultMap>
<!-- 查询用户及用户购买商品信息(多对多映射之使用resultMap) -->
<select id="findUserAndItemsRstMap" resultMap="UserAndItemsRstMap">
Select
<include refid="select_orders"/>
,
<include refid="select_user"/>
,
<include refid="select_orderdetail"></include>
,
items.name items_name,
items.detail items_detail
from
orders,user,orderdetail,items
where orders.user_id = user.id
and
orders.id = orderdetail.orders_id
and orderdetail.items_id = items.id
</select>
编写测试代码:
@Test
publicvoid testFindUserAndItemsRstMap() {
// 创建sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 通过SqlSession构造usermapper的代理对象
OrdersMapper ordersMapper = sqlSession.getMapper(OrdersMapper.class);
// 调用usermapper的方法
List<User> list = ordersMapper.findUserAndItemsRstMap();
// 此处我们采用debug模式来跟踪代码,然后验证结果集是否正确
System.out.println(list);
// 释放SqlSession
sqlSession.close();
}
1.3.1 关联映射总结
resultType:
作用:将查询结果按照sql列名pojo属性名一致性映射到pojo中。
场合:常见一些明细记录的展示,比如用户购买商品明细,将关联查询信息全部展示在页面时,此时可直接使用resultType将每一条记录映射到pojo中,在前端页面遍历list(list中是pojo)即可。
resultMap:使用association和collection完成一对一和一对多高级映射(对结果有特殊的映射要求)。
association:
作用:将关联查询信息映射到一个pojo对象中。
场合:为了方便查询关联信息可以使用association将关联订单信息映射为用户对象的pojo属性中,比如:查询订单及关联用户信息。
使用resultType无法将查询结果映射到pojo对象的pojo属性中,根据对结果集查询遍历的需要选择使用resultType还是resultMap。
collection:
作用:将关联查询信息映射到一个list集合中。
场合:为了方便查询遍历关联信息可以使用collection将关联信息映射到list集合中,比如:查询用户权限范围模块及模块下的菜单,可使用collection将模块映射到模块list中,将菜单列表映射到模块对象的菜单list属性中,这样的作的目的也是方便对查询结果集进行遍历查询。
如果使用resultType无法将查询结果映射到list集合中。
1.4 延迟加载
resultMap中的association和collection标签具有延迟加载的功能。
延迟加载的意思是说,在关联查询时,利用延迟加载,先加载主信息。需要关联信息时再去按需加载关联信息。这样会大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。
Mybatis默认是不开启延迟加载功能的,我们需要手动开启。
需要在SqlMapConfig.xml文件中,在<settings>标签中开启延迟加载功能。
lazyLoadingEnabled、aggressiveLazyLoading
<settings>
<!-- 开启延迟加载,默认是false -->
<setting name="lazyLoadingEnabled"value="true"/>
<!--积极的懒加载模式,默认是false-->
<setting name="aggressiveLazyLoading"value="false"/>
</settings>
<!-- 定义OrdersUserLazyLoadingRstMap -->
<resultMap type="cn.itcast.mybatis.po.Orders"id="OrdersUserLazyLoadingRstMap">
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="number" property="number"/>
<result column="createtime" property="createtime" />
<result column="note" property="note"/>
<!-- 延迟加载用户信息 -->
<!-- select:指定延迟加载需要执行的statement的id(是根据user_id查询用户信息的statement)
我们使用UserMapper.xml中的findUserById完成根据用户ID(user_id)查询用户信息
如果findUserById不在本mapper中,前边需要加namespace
-->
<!-- column:主信息表中需要关联查询的列,此处是user_id -->
<association property="user" select="cn.itcast.mybatis.mapper.UserMapper.findUserById"column="user_id"></association>
</resultMap>
<!-- 查询订单信息,延迟加载关联查询的用户信息 -->
<select id="findUserById" parameterType="int"
resultType="cn.itcast.mybatis.po.User">
SELECT * FROM user WHERE id = #{id}
</select>
如上图所示通过懒加载关联查询用户信息。
映射文件
<package name="cn.itcast.mybatis.mapper"/>
查询订单信息
// 查询订单信息,延迟加载关联查询的用户信息
public List<Orders> findOrdersUserLazyLoading();