Mybatis如何实现关联属性懒加载

Mybatis 关联属性懒加载

延迟加载配置

mybatis默认没有开启延迟加载,需要在config.xml中setting配置。

lazyLoadingEnabled:true使用延迟加载,false禁用延迟加载,默认为false。

aggressiveLazyLoading:true启用时,当延迟加载开启时访问对象中一个懒对象属性时,将完全加载这个对象的所有懒对象属性。false,当延迟加载时,按需加载对象属性(即访问对象中一个对象属性时,不会加载对象中的引用属性)。默认为true。

修改延迟加载需要的select语句

延迟加载场景分析

查询订单时,需要关联查询订单明细表

假设有10 个订单,每个订单有5个明细信息。

如果用户仅仅需要查看订单信息,不需要订单明细信息,但是查询了10+50个对象到内存(只有10个是用户想要的)。显然是增加内存的负担。

如果用户的需求仅仅是查询订单信息,我仅仅订单信息用户,没有查询订单的详情,那么,内存中只有10对象(10个对象全是客户想的),这样做显然 节省了内存。

业务需求特点:需要的时候在进行查询,不需要的时候不尽兴查询,这种情况就是延迟加载,也叫作懒加载。

配置过程

去掉sql语句中对user表的查询语句

对resultMap orderAndUser 进行修改

注意:查询订单时要同时查询userid,避免查询user时没有凭据

修改orderMapper.xml文件select语句

<!-- 关联属性延迟加载 -->
	<select id="queryOrderAndUser" resultMap="orderAndUser">
		select
		o.id oids,o.orderid,o.createtime,o.note,o.userid
		from orders o,t_user u
		where o.userid=u.id
	</select>
	<resultMap type="orders" id="orderAndUser">
		<id property="id" column="oids"></id>
		<result property="orderId" column="orderid"></result>
		<result property="createtime" column="createtime" />
		<result property="note" column="note" />
		<association property="user" javaType="users"
		select="net.neuedu.mybatis.mapper.UserMapper.queryUserById" column="userid">
			<!--
				这里不查询用户的信息,而是将用的信息放在另外一个查询中
				column是order表中的userid字段
			 -->
		</association>
	</resultMap>

因为在assocication多了一个select元素,就要在UserMapper中多加一个方法。

 //根据ID查询用户
 public Users queryUserById(Integer id);

在UserMapper.xml中添加queryUserById的select

 <!-- public Users queryUserById(Integer id); -->
 <select id="queryUserById" parameterType="int" resultType="users">
  select * from t_user where id=#{id}
 </select>

Mybatis注解方式懒加载失效分析

本人在使用spring mvc + mybatis的后台结构的项目的时候,在使用mybatis的懒加载出现了一些问题:

明明懒加载的配置都正确了,但就是用debug断点调试的时候懒加载的属性总是提前加载,在经过几天的不断努力之后,终于发现了原因:

mybatis懒加载配置:

<!-- 查询时,关闭关联对象即时加载以提高性能 -->
<setting name="lazyLoadingEnabled" value="true" />
<!-- 设置关联对象加载的形态,此处为按需加载字段(加载字段由SQL指 定),不会加载关联表的所有字段,以提高性能 -->
<setting name="aggressiveLazyLoading" value="false" />

懒加载查询语句:

@Select("select * from user_main where username=#{username}")
 @Results({
       @Result(property = "roleNames", column = "id", many = @Many(fetchType=FetchType.LAZY,select = "getRoleNamesByUserId")),
       @Result(property = "permissionNames", column = "id", many = @Many(fetchType=FetchType.LAZY,select = "getPermissionsByUserId"))
   })
public UserPO getUserByUsername(@Param("username")String username);

测试用例:

 @Test
   public void testGetUserByUsername() throws Exception {
     SqlSession session = sqlSessionFactory.openSession();
     try {
       UserMapper userMapper = session.getMapper(UserMapper.class);
       UserPO userPo = userMapper.getUserByUsername("zhangsan");
       System.out.println(userPo);
       System.out.println(userPo.getPermissionNames());
//       List<String> map = userMapper.getRoleMain();
//       System.out.println(map);
       session.close();
     } finally {
       session.close();
     }
   }

结果调试测试用例的时候,使用debug却发现懒加载并没有生效,后来看了mybatis懒加载的源码,依然没有发现原因,感觉就是roleNames和permissionNames就是突然的加载了,断点也捕捉不到,也没有博捉到触发的事件,这时楼主我就很奇怪了,最后经过几天的努力终于发现了原因:

mybatis源码:configuration

protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" }));

默认这四个方法或触发懒加载,当然,触发懒加载的方法还有getRoleNames 和 getPermissions ,这两个(只针对当前实例)

然后通过sysout输出的时候,发现hashcode和equals方法被莫名的触发了,而且debug断点还捕捉不到,然后roleNames和permissionNames就被莫名的加载了

最后发现,这个问题和debug的原理有一定的关系,

原来,在显示debug的黄色框框时,debug会另起一个线程,然后重新调用一遍代码,然后显示userpo信息的时候,又会调用userpo的hashcode方法和tostring方法,

这样的话,就会导致这两个方法会在debug线程内触发懒加载,造成的效果就是懒加载失效,但是实际上懒加载是生效了的,只是在debug模式上被触发了,而且用断点还捕捉不到,就会形成一个奇怪的问题,如果想用debug来查看效果,也是很简单:

通过将触发默认的四个方法屏蔽来查看效果,但是不支持,因为这样的话,可能会影响实体的hash排序等问题,即使是用了这个方法,也是建议看完效果以后改成默认的

这种事代码方式的,还有一种是配置文件方式的(自己从网上找)

configuration.setLazyLoadTriggerMethods(new HashSet<String>());

还有一种更好的方式来查看懒加载的效果,那就是mybatis的默认日志,建议将日志级别调成debug(这个debug级别和debug模式不一样),然后执行的时候就可以通过sql语句的打印来判断懒加载是否生效。

其实这个问题的分析最后,还是在说debug断点调试的问题,它的这种原理需要注意,特别是会在另一个线程调用hashcode和tostring方法,相信在很多方面会影响到我们,希望大家能够注意吧~

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Mybatis懒加载的实现

    因为通过javassist和cglib代理实现的,所以说到底最主要的就是JavasisstProxyFactory类中的invoke方法和里面的load方法. 其实读一读,里面的逻辑就是跟配置中定义的规则一样的 因为github上的mybatis中文版中这部分注释比较少,所以从网上寻找博客,截取了代码注释片段记录下. JavasisstProxyFactory public class JavassistProxyFactory implements org.apache.ibatis.exec

  • mybatis 延迟加载的深入理解

    什么是延迟加载 延迟加载又叫懒加载,也叫按需加载,也就是说先加载主信息,需要的时候,再去加载从信息.代码中有查询语句,当执行到查询语句时,并不是马上去DB中查询,而是根据设置的延迟策略将查询向后推迟. 什么时候会执行延迟加载 配置之后在对关联对象进行查询时使用延迟加载. 延迟加载策略 直接加载 遇到代码中查询语句,马上到DB中执行select语句进行查询.(这种只能用于多表单独查询) 侵入式延迟加载 将关联对象的详情(具体数据,如id.name)侵入到主加载对象,作为主加载对象的详情的一部分出现

  • Mybatis中的延迟加载案例解析

    一.延迟加载 resultMap可以实现高级映射(使用association.collection实现一对一及一对多映射),association.collection具备延迟加载功能. 延迟加载:先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快. 在mybatis核心配置文件中配置: lazyLoadingEnabled.aggressiveLazyLoading 设置项 描述 允许值 默认值 lazyLoadingEnabled 全局性设置

  • Mybatis延迟加载的实现方式

    1.概念: MyBatis中的延迟加载,也称为懒加载,是指在进行表的关联查询时,按照设置延迟规则推迟对关联对象的select查询.例如在进行一对多查询的时候,只查询出一方,当程序中需要多方的数据时,mybatis再发出sql语句进行查询,这样子延迟加载就可以的减少数据库压力.MyBatis 的延迟加载只是对关联对象的查询有迟延设置,对于主加载对象都是直接执行查询语句的. 2.加载时机: 直接加载:执行完对主加载对象的 select 语句,马上执行对关联对象的 select 查询.侵入式延迟: 执

  • Mybatis如何实现关联属性懒加载

    Mybatis 关联属性懒加载 延迟加载配置 mybatis默认没有开启延迟加载,需要在config.xml中setting配置. lazyLoadingEnabled:true使用延迟加载,false禁用延迟加载,默认为false. aggressiveLazyLoading:true启用时,当延迟加载开启时访问对象中一个懒对象属性时,将完全加载这个对象的所有懒对象属性.false,当延迟加载时,按需加载对象属性(即访问对象中一个对象属性时,不会加载对象中的引用属性).默认为true. 修改延

  • JPA @Basic单表查询如何实现大字段懒加载

    JPA @Basic单表查询实现大字段懒加载 近期看了JPA@Basic注解的使用,看到该注解可以设置字段的懒加载. 1.以前碰到的懒加载: 我们知道,多表关联的时候,我们是可以配置懒加载的,比如一个Company类,里面可以关联员工表,办公设备表,当我们查看公司信息的时候,我们并不需要直接加载员工表,办公设备表,只需要在用户查看员工,查看设备的时候再加载,这样可以提高加载效率. 长话短说,大概是这样子的 @Entity @Table(name = "Company") public

  • MyBatis深入解读懒加载的实现

    懒加载 ,也称为嵌套查询 需要查询关联信息时,使用 Mybatis 懒加载特性可有效的减少数据库压力, 首次查询只查询主表信息,关联表的信息在用户获取时再加载. Mybatis 一对一关联的 association 和一对多的 collection 可以实现懒加载.懒加载时要 使用resultMap,不能使用 resultType . 这里我们以员工表和部门表为例 通过deptId 与 部门表 id 关联 我们这里首先需要开启一个设置 <settings> <!--指定哪些方法去触发延迟

  • mybatis源码解读之executor包懒加载功能 

    ProxyFactory是创建代理类的工厂接口,其中的setProperties方法用来对工厂进行属性设置,但是mybatis内置的两个实现类都没有实现该接口,所以不支持属性设置.createProxy方法用来创建一个代理对象 public interface ProxyFactory {   // 设置工厂属性   default void setProperties(Properties properties) {   }   // 创建代理对象   Object createProxy(O

  • MyBatis的注解使用、ORM层优化方式(懒加载和缓存)

    目录 Mybatis注解 Mybatis对ORM层的优化 缓存 懒加载 MyBatis缓存机制 二级缓存 Mybatis注解 查找 @Select( "SELECT * FROM tt_user WHERE username Like #{id};") User findUserByName(String name); <!--以map为输入参数查找--> @Select("select * from tt_user where username= #{name}

  • 解决JPA @OneToMany及懒加载无效的问题

    目录 JPA @OneToMany及懒加载无效 @OneToMany 小结一下吧 实现JPA的懒加载和无外键 例如 转换时使用 JPA @OneToMany及懒加载无效 @OneToOne @ManyToMany使用不做过多解释,重点解决"懒加载无效问题". 示例: @OneToMany teacher 和 student是一对多关系 只需要在studentList上使用@OneToMany注解,对应的参数为 懒加载.级联操作.子表外键 我为了验证懒加载是否生效,在debug模式下发现

  • 使用jpa的实体对象转json符串时懒加载的问题及解决

    目录 解决转json符串时懒加载问题方法(1) 解决转json符串时懒加载问题方法(2) Rest风格中关于JPA使用懒加载的坑 解决转json符串时懒加载问题方法(1) 1.导入hibernate5转json的Maven依赖: <dependency>     <groupId>com.fasterxml.jackson.datatype</groupId>     <artifactId>jackson-datatype-hibernate5</a

  • Hibernate懒加载之<class>标签上的lazy

    本文研究的主要是 lazy的概念:在真正使用某个对象的时候才去加载该对象. Hibernate的lazy策略可以使用在: 1.<class>标签上,可以取值:true/false. 2.<property>标签上,可以取值:true/false,需要类增强工具,对字节码进行修改(这个没多大意义). 3.<set>和<list>标签上,可以取值:true/false/extra. 4.<many-to-one>和<one-to-one>

  • 浅谈hibernate中懒加载禁用操作

    浅谈hibernate中懒加载禁用操作 懒加载的概念:懒加载就是hibernate中的延迟加载,在hibernate中的一对多,多对多关系中通过对象导航来查询对象时一般默认的就是懒加载.就是当我们查询一个对象的时候,在默认情况下,返回的只是该对象的代理对象,当用户去使用该对象的属性是,才会向数据库中再一次发出查询语句.懒加载在某些情况下确实可以减少不必要的sql语句,但是有的情况下,还是会抛出异常. 下面我将介绍懒加载禁用的方式 方式一: 在需要禁用懒加载的实体对象的配置文件中配置lazy="f

随机推荐