写在前面
本文接https://www.cnblogs.com/wushenjiang/p/12506992.html,至此mybatis的学习已经基本完成。近一个月会进行android的冲刺学习,所以将SSM的学习暂且滞后。
高级映射
高级映射主要分为一对一,一对多,多对多,延迟加载等。以下分别进行解释:
一对一查询(resultType实现)
需求
首先我们要提出一个需求,以便我们开展学习。需求:查询订单信息,关联查询创建订单的用户信息。
po的编写
这里我们直接继承原订单类,并添加新的属性:
public class OrdersCustom extends Orders{
//添加用户属性
/*
* User.username
* User.sex,
* User.address
*/
private String username;
private String sex;
private String address;
mapper.xml的编写
首先来看代码:
<!-- 查询订单关联查询用户信息 使用resultType -->
<select id="findOrdersUser"
resultType="mybatis01.po.OrdersCustom">
select orders.*,user.username,user.sex,user.address
from
orders,user
where orders.user_id = user.id
</select>
在这里我们使用了resultType,即直接映射。因为这里的需求比较简单,所以没有使用resultMap的方式。
mapper.java的编写
这里我们要写一个接口:
//查询订单关联查询用户信息
public List<OrdersCustom> findOrdersUser() throws Exception;
这样我们就可以开始写测试了。
测试
@Test
public void testFindOrdersUser() throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
//创建代理对象
OrdersMapperCustom ordersMapperCustom = sqlSession.getMapper(OrdersMapperCustom.class);
//调用mapper的方法
List<OrdersCustom> list = ordersMapperCustom.findOrdersUser();
System.out.println(list);
sqlSession.close();
}
具体的测试代码可见上一篇入门博客。
一对一查询(resultMap实现)
上面是使用了resultType进行实现,这里我们使用resultMap来实现以学习resultMap的使用
mapper.xml
<!-- 查询订单关联查询用户信息 使用resultMap -->
<select id="findOrdersUserResultMap"
resultMap="OrdersUserResultMap">
select orders.*,user.username,user.sex,user.address
from
orders,user
where orders.user_id = user.id
</select>
以上是statement的编写,下面是resultMap的编写:
<!--订单查询关联用户的resultMap 映射到orders中 -->
<resultMap type="mybatis01.po.Orders"
id="OrdersUserResultMap">
<!-- 配置映射的订单信息 -->
<!-- 指定查询列中的唯一标识,订单里的.如果有多个列组成唯一标识,就配置多个id column:订单信息的唯一标识列 property:订单信息的唯一标识列映射到哪个属性中 -->
<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" />
<!-- 配置映射的关联的用户信息 -->
<!-- association:用于映射关联查询单个对象的信息 property:要将关联查询的用户信息映射到user里 -->
<association property="user" javaType="mybatis01.po.User">
<!-- id:关联查询用户的唯一标识 column:用于唯一标识用户信息的列 javaType:映射到user的哪个属性 -->
<id column="user_id" property="id" />
<result column="username" property="username" />
<result column="sex" property="sex" />
<result column="address" property="address" />
</association>
</resultMap>
mapper.java编写
//查询订单关联查询用户使用resultMap
public List<Orders> findOrdersUserResultMap() throws Exception;
这里写一个接口,就可以开始测试了。
测试
@Test
public void testFindOrdersUserResultMap() throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
//创建代理对象
OrdersMapperCustom ordersMapperCustom = sqlSession.getMapper(OrdersMapperCustom.class);
//调用mapper的方法
List<Orders> list = ordersMapperCustom.findOrdersUserResultMap();
System.out.println(list);
sqlSession.close();
}
一对多
需求
需求:查询订单及订单明细的信息。
改写po
由于需求,我们需要在orders里添加一个list来存储订单详细信息。
//订单明细
private List<Orderdetail> orderdetails;
mapper.xml 编写
首先是statement:
<!-- 查询订单关联查询用户及订单明细 使用resultMap -->
<select id="findOrdersAndOrderDetailResultMap"
resultMap="OrdersAndOrderDetailResultMap">
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
</select>
然后是resultMap:
<!-- 订单及订单明细的resultMap 使用extends继承 不用在重配置订单信息和用户信息 -->
<resultMap type="mybatis01.po.Orders"
id="OrdersAndOrderDetailResultMap" extends="OrdersUserResultMap">
<!-- 订单明细信息 一个订单关联查询出了多条明细,要使用collection进行映射 collection:对关联查询到的多条记录映射到集合对象中
property:将关联查询的多条记录映射到Orders的哪条属性 ofType:指定映射到集合属性中pojo的类型 -->
<collection property="orderdetails"
ofType="mybatis01.po.Orderdetail">
<id column="orderdetail_id" property="id" />
<result column="items_id" property="itemsId" />
<result column="items_num" property="itemsNum" />
<result column="orders_id" property="ordersId" />
</collection>
<!-- id:订单明细唯一标识 property:要将订单明细的唯一标识映射到orderdetail的哪个属性 -->
</resultMap>
mapper.java
这里写一下接口即可:
//查询订单(关联用户)及订单明细
public List<Orders> findOrdersAndOrderDetailResultMap() throws Exception;
测试
@Test
public void testFindOrdersAndOrderDetailResultMap() throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
//创建代理对象
OrdersMapperCustom ordersMapperCustom = sqlSession.getMapper(OrdersMapperCustom.class);
//调用mapper的方法
List<Orders> list = ordersMapperCustom.findOrdersAndOrderDetailResultMap();
System.out.println(list);
sqlSession.close();
}
多对多
需求
查询用户及用户购买商品信息。
mapper.xml编写
statement:
<!-- 查询用户及购买的商品信息 使用resultMap -->
<select id="findUserAndItemsResultMap"
resultMap="UserAndItemsResultMap">
select orders.*,user.username,user.sex,user.address,
orderdetail.id orderdetail_id,
orderdetail.items_id,
orderdetail.items_num,
orderdetail.orders_id,
items.id items_id,
items.name items_name,
items.detail items_detail,
items.price items_price
from orders,user,orderdetail,items
where orders.user_id = user.id
and orderdetail.orders_id = orders.id
and orderdetail.items_id = items.id
</select>
resultMap:
<!-- 查询用户及购买的商品 -->
<resultMap type="mybatis01.po.User"
id="UserAndItemsResultMap">
<!-- 用户信息 -->
<id column="user_id" property="id" />
<result column="username" property="username" />
<result column="sex" property="sex" />
<result column="address" property="address" />
<!-- 订单信息 一个用户对应多个订单,使用collection映射 -->
<collection property="ordersList"
ofType="mybatis01.po.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="orderdetails"
ofType="mybatis01.po.Orderdetail">
<id column="orderdetail_id" property="id" />
<result column="items_id" property="itemsId" />
<result column="items_num" property="itemsNum" />
<result column="orders_id" property="ordersId" />
<!-- 商品信息 一个订单明细对应一个商品 -->
<association property="items"
javaType="mybatis01.po.Items">
<id column="items_id" property="id" />
<result column="items_name" property="name" />
<result column="items_detail" property="detail" />
<result column="items_price" property="price" />
</association>
</collection>
</collection>
</resultMap>
mapper.java
//查询用户购买商品的信息
public List<User> findUserAndItemsResultMap() throws Exception;
测试
@Test
public void testFindUserAndItemsResultMap() throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
//创建代理对象
OrdersMapperCustom ordersMapperCustom = sqlSession.getMapper(OrdersMapperCustom.class);
//调用mapper的方法
List<User> list = ordersMapperCustom.findUserAndItemsResultMap();
System.out.println(list);
sqlSession.close();
}
延迟加载
延迟加载,又称懒加载。指先从单表查询、需要时再从关联表去关联查询,大大提高 数据库性能,因为查询单表要比关联查询多张表速度要快。
我们可以通过mabatis的自带参数实现延迟加载的功能
需求
查询订单并且关联查询用户信息
mapper.xml
statement
<!-- 查询订单关联查询用户,用户信息需要延迟加载 -->
<select id="findOrdersUserLazyLoading" resultMap="OrdersUserLazyLoadingResultMap">
select * from orders
</select>
resultMap
<!-- 延迟加载的resultMap -->
<resultMap type="mybatis01.po.Orders"
id="OrdersUserLazyLoadingResultMap">
<!-- 对订单信息进行映射配置 -->
<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:指定延迟加载所需要执行的sql语句(statement的id,即根据user_id查询用户信息的statement)
要使用UserMapper.xml中的findUserById完成根据用户id的查询,如果不在本mapper中,需要在前面加namespace
column:订单信息中关联用户信息查询的列:是user_id
关联查询的sql可以理解为子查询里的sql
-->
<association property="user" javaType="mybatis01.po.User" select="mybatis01.mapper.UserMapper.findUserById" column="user_id">
</association>
</resultMap>
mapper.java
//查询订单关联查询用户,用户信息需要延迟加载
public List<Orders> findOrdersUserLazyLoading() throws Exception;
测试
//查询订单关联查询用户,用户信息使用延迟加载
@Test
public void testFindOrdersUserLazyLoading() throws Exception{
//查询订单信息(单表)
SqlSession sqlSession = sqlSessionFactory.openSession();
OrdersMapperCustom ordersMapperCustom = sqlSession.getMapper(OrdersMapperCustom.class);
List<Orders> list = ordersMapperCustom.findOrdersUserLazyLoading();
//遍历上边的订单列表
for(Orders orders:list) {
//执行getUser()去查询用户信息,这里实现按需加载
User user = orders.getUser();
System.out.println(user);
}
}
查询缓存
mybatis提供查询缓存,用于减轻数据压力,提高数据库性能。
mybaits提供一级缓存,和二级缓存。
一级缓存是SqlSession级别的缓存。在操作数据库时需要构造 sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。
二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
一级缓存
一级缓存原理
第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从数据库查询用户信息。
得到用户信息,将用户信息存储到一级缓存中。
如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。
如图示例:
一级缓存测试
@Test
public void testCache1() throws Exception{
SqlSession sqlSession = sqlSessionFactory.openSession();//创建代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//下边查询使用一个SqlSession
//第一次发起请求,查询id为1的用户
User user1 = userMapper.findUserById(1);
System.out.println(user1);
// 如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
//更新user1的信息
user1.setUsername("测试用户22");
userMapper.updateUser(user1);
//执行commit操作去清空缓存
sqlSession.commit();
//第二次发起请求,查询id为1的用户
User user2 = userMapper.findUserById(1);
System.out.println(user2);
sqlSession.close();
}
二级缓存
二级缓存原理
首先开启mybatis的二级缓存。
sqlSession1去查询用户id为1的用户信息,查询到用户信息会将查询数据存储到二级缓存中。
如果SqlSession3去执行相同 mapper下sql,执行commit提交,清空该 mapper下的二级缓存区域的数据。
sqlSession2去查询用户id为1的用户信息,去缓存中找是否存在数据,如果存在直接从缓存中取出数据。
二级缓存与一级缓存区别,二级缓存的范围更大,多个sqlSession可以共享一个UserMapper的二级缓存区域。
UserMapper有一个二级缓存区域(按namespace分) ,其它mapper也有自己的二级缓存区域(按namespace分)。
每一个namespace的mapper都有一个二缓存区域,两个mapper的namespace如果相同,这两个mapper执行sql查询到数据将存在相同 的二级缓存区域中。
如图示例:
开启二级缓存
在核心配置文件SqlMapConfig.xml中加入
在UserMapper.xml中开启二缓存,UserMapper.xml下的sql执行完成会存储到它的缓存区域(HashMap)。
对pojo类实现序列化接口
为了将缓存数据取出执行反序列化操作,因为二级缓存数据存储介质多种多样,不一定在内存。
二级缓存测试
// 二级缓存测试
@Test
public void testCache2() throws Exception {
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
SqlSession sqlSession3 = sqlSessionFactory.openSession();
// 创建代理对象
UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
// 第一次发起请求,查询id为1的用户
User user1 = userMapper1.findUserById(1);
System.out.println(user1);
//这里执行关闭操作,将sqlsession中的数据写到二级缓存区域
sqlSession1.close();
//使用sqlSession3执行commit()操作
UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class);
User user = userMapper3.findUserById(1);
user.setUsername("张明明");
userMapper3.updateUser(user);
//执行提交,清空UserMapper下边的二级缓存
sqlSession3.commit();
sqlSession3.close();
UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
// 第二次发起请求,查询id为1的用户
User user2 = userMapper2.findUserById(1);
System.out.println(user2);
sqlSession2.close();
}