Mybatis 延迟加载的实现原理详细解析
(1)代理对象机制的深入探讨
代理对象的生成:Mybatis 使用代理对象来实现延迟加载是基于 Java 的代理机制。当开启延迟加载并且配置正确后,对于需要延迟加载的关联对象,Mybatis 会创建一个代理对象。这个代理对象是在运行时动态生成的,它的生成过程涉及到 Java 的反射机制。以 Java 动态代理为例,Mybatis 会实现一个InvocationHandler接口,在这个接口的invoke方法中,会处理对代理对象方法的调用。
方法拦截原理:当外部代码调用代理对象的方法时,实际上是调用了InvocationHandler接口的invoke方法。在invoke方法中,代理对象会首先检查关联对象是否已经被加载。这个检查过程是通过判断一个标志位或者查看缓存来实现的。如果关联对象还没有被加载,代理对象就会触发加载过程。例如,假设代理对象是一个List类型的关联对象的代理(如前面提到的商品图片集合),当调用list.size()方法时,invoke方法会检测到如果关联对象(真实的商品图片列表)还没有被加载,就会执行加载操作。
代理对象与真实对象的替换:在关联对象加载完成后,代理对象会将自己替换为真实的对象。这个替换过程需要考虑到对象的类型兼容性和引用一致性。例如,在 Java 中,如果代理对象是ArrayList的代理,加载完成后的真实对象也是ArrayList,那么代理对象会将自己在内存中的引用替换为真实的ArrayList对象的引用。这样,在后续的方法调用中,就可以直接使用真实对象,而不会再经过代理对象的拦截。
(2)加载过程的详细步骤
查询语句的构建与执行:当代理对象检测到需要加载关联对象时,它会根据在映射文件中配置的信息构建查询语句。这个过程涉及到解析select属性指定的查询方法以及column属性指定的参数。以之前的商品和商品图片的例子来说,代理对象会从ProductImageMapper.getProductImagesByProductId这个方法签名和product_id这个参数构建出一个完整的 SQL 查询语句,然后通过 Mybatis 的SqlSession对象执行这个查询语句。在执行查询语句时,Mybatis 会使用配置好的数据源、数据库驱动等组件,将 SQL 语句发送到数据库服务器进行查询。
数据的映射与填充:数据库返回查询结果后,Mybatis 会根据结果集和实体类的映射关系(通常在resultMap中定义)将数据填充到关联对象中。这个映射过程类似于普通的查询结果映射,但在延迟加载场景下,需要将数据填充到之前未加载的关联对象中。例如,对于商品图片的查询结果,Mybatis 会根据ProductImage实体类的属性和结果集的列名、列值的对应关系,将数据逐一填充到ProductImage对象中,然后将这些对象添加到关联对象(如productImages集合)中。
加载过程中的异常处理:在加载过程中,可能会出现各种异常情况,如数据库连接失败、SQL 语法错误、查询结果为空等。Mybatis 会对这些异常进行处理。如果是数据库连接失败等严重错误,会将异常向上抛出,可能导致整个操作失败。如果是查询结果为空,会根据具体的配置和业务需求进行处理,可能是返回一个空的关联对象(如空集合),也可能是抛出一个轻微的警告信息。
(3)缓存机制在延迟加载中的作用
一级缓存的影响:Mybatis 的一级缓存是基于SqlSession的缓存。在延迟加载场景下,当一个SqlSession内首次加载关联对象后,数据可能会被缓存到一级缓存中。如果在同一个SqlSession内再次访问相同的关联对象,就可以直接从缓存中获取数据,而不需要再次执行查询语句。例如,在一个事务处理过程中,第一次加载了商品的图片关联对象,之后在同一事务(同一个SqlSession)中再次访问商品图片时,就可以利用一级缓存提高性能。
二级缓存的作用(如果启用):二级缓存是基于Mapper的缓存,范围比一级缓存更广。如果在配置中启用了二级缓存,并且关联对象的查询符合二级缓存的规则,那么在不同的SqlSession之间也可以共享缓存数据。这对于频繁访问的关联对象来说,可以大大减少数据库查询次数。不过,在使用二级缓存时,需要注意缓存的一致性问题,因为不同的SqlSession可能会对数据进行修改,导致缓存数据与数据库中的实际数据不一致。在延迟加载场景下,二级缓存的更新策略和缓存清除机制需要谨慎设计,以确保缓存数据的准确性和及时性。
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/aaaa_1111111/article/details/144152482