zoukankan      html  css  js  c++  java
  • 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();
    
    	}
    
  • 相关阅读:
    SwiftUI:看我展示52张扑克牌,“很快啊!”
    不会吧,这也行?iOS后台锁屏监听摇一摇
    分布式锁
    布隆过滤器原理
    redis缓存穿透
    布隆过滤器应用DEMO
    线程的声明周期
    分布式事务
    滑动窗口协议
    代理
  • 原文地址:https://www.cnblogs.com/wushenjiang/p/12681712.html
Copyright © 2011-2022 走看看