Mybatis框架学习03

写在前面

本文接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();

	}

猜你喜欢

转载自www.cnblogs.com/wushenjiang/p/12681712.html