所谓的延迟加载,就是将暂时不需要的对象不真正的载人内存,直到真正需要的时候再去执行加载动作。在iBatis中,实现延迟加载的思路:如果一个对象的某个属性需要被延迟加载,那么首先创建这个属性对应的代理对象并返回;当真正使用时,调用这个代理对象的相关方法,invoke方法得以执行,此时,在 invoke方法里面,去执行真正的加载操作。
总结起来,对于延迟加载,首先创建一个被延迟加载对象的代理对象,当使用代理对象时,就是真正需要加载的时刻,在执行invoke方法时执行加载。
在iBatis中,延迟加载相关的类主要有:ResultLoader,LazyResultLoader,EnhancedLazyResultLoader。其中ResultLoader是执行数据加载的类,LazyResultLoader和EnhancedResultLoader是延迟加载的的实现类,LazyResultLoader使用的是java的代理模型,EnhancedResultLoader使用的是cglib。
那么,在iBatis中,延迟加载是如何实现的呢?
在iBatis中,在RowMap中,如果某个result属性中指定了select属性,那么此处就有可能会发生延迟加载。在处理ResultMap 时,ResultMap类中的getNestedSelectMappingValue方法会触发ResultLoader的loadResult方法。下面,就先看一下loadResult是如何实现的。
view plaincopy to clipboardprint?
1. public static Object loadResult(SqlMapClientImpl client, String statementName, Object parameterObject, Class targetType)
2. throws SQLException {
3. Object value = null;
4.
5. //如果需要延迟加载,在SqlMapConfig中的setting中指定
6. if (client.isLazyLoadingEnabled()) {
7. //如果使用基于cglib的增强性能的延迟加载,需要SqlMapConfig中的setting中指定
8. if (client.isEnhancementEnabled()) {
9. EnhancedLazyResultLoader lazy = new EnhancedLazyResultLoader(client, statementName, parameterObject, targetType);
10. value = lazy.loadResult();
11. } else {
12. //使用基于jdk反射的延迟加载
13. LazyResultLoader lazy = new LazyResultLoader(client, statementName, parameterObject, targetType);
14. value = lazy.loadResult();
15. }
16. } else {
17. //不使用延迟加载
18. value = getResult(client, statementName, parameterObject, targetType);
19. }
20.
21. return value;
22. }
public static Object loadResult(SqlMapClientImpl client, String statementName, Object parameterObject, Class targetType) throws SQLException { Object value = null; //如果需要延迟加载,在SqlMapConfig中的setting中指定 if (client.isLazyLoadingEnabled()) { //如果使用基于cglib的增强性能的延迟加载,需要SqlMapConfig中的setting中指定 if (client.isEnhancementEnabled()) { EnhancedLazyResultLoader lazy = new EnhancedLazyResultLoader(client, statementName, parameterObject, targetType); value = lazy.loadResult(); } else { //使用基于jdk反射的延迟加载 LazyResultLoader lazy = new LazyResultLoader(client, statementName, parameterObject, targetType); value = lazy.loadResult(); } } else { //不使用延迟加载 value = getResult(client, statementName, parameterObject, targetType); } return value; }
在上述代码中,根据setting中指定的enhancementEnabled和lazyLoadingEnabled属性来决定是否使用延迟加载以及使用延迟加载的策略,分别创建了LazyResultLoader和EnhancedLazyResultLoader对象,并且调用了 loadResult方法,下面就分别看一下这两个类中的loadResult是如何实现的。
首先看一下LazyResultLoader的loadResult实现
view plaincopy to clipboardprint?
1. public Object loadResult() throws SQLException {
2. //注意,只代理Collection。也就是说非Collection类型的属性不会被延迟加载
3. if (Collection.class.isAssignableFrom(targetType)) {
4. InvocationHandler handler = new LazyResultLoader(client, statementName, parameterObject, targetType);
5. ClassLoader cl = targetType.getClassLoader();
6. if (Set.class.isAssignableFrom(targetType)) {
7. //创建Set类型的代理对象
8. return Proxy.newProxyInstance(cl, SET_INTERFACES, handler);
9. } else {
10. //创建List类型的代理对象
11. return Proxy.newProxyInstance(cl, LIST_INTERFACES, handler);
12. }
13. } else {
14. //不执行延迟加载,直接加载数据
15. return ResultLoader.getResult(client, statementName, parameterObject, targetType);
16. }
17. }
public Object loadResult() throws SQLException { //注意,只代理Collection。也就是说非Collection类型的属性不会被延迟加载 if (Collection.class.isAssignableFrom(targetType)) { InvocationHandler handler = new LazyResultLoader(client, statementName, parameterObject, targetType); ClassLoader cl = targetType.getClassLoader(); if (Set.class.isAssignableFrom(targetType)) { //创建Set类型的代理对象 return Proxy.newProxyInstance(cl, SET_INTERFACES, handler); } else { //创建List类型的代理对象 return Proxy.newProxyInstance(cl, LIST_INTERFACES, handler); } } else { //不执行延迟加载,直接加载数据 return ResultLoader.getResult(client, statementName, parameterObject, targetType); } }
根据上述代码可知,使用基于java反射的延迟加载实现,只会延迟加载那些类型为Collection的属性,其他属性即使配置为延迟加载也会被立即载入内存的。
然后看一下EnhancedLazyResultLoader的loadResult实现。
view plaincopy to clipboardprint?
1. public Object loadResult() throws SQLException {
2.
3. if (DomTypeMarker.class.isAssignableFrom(targetType)) {
4. //这个DomTypeMarker不太清楚是干什么的
5. return ResultLoader.getResult(client, statementName, parameterObject, targetType);
6. } else if (Collection.class.isAssignableFrom(targetType)) {
7. //延迟加载集合类
8. if (Set.class.isAssignableFrom(targetType)) {
9. return Enhancer.create(Object.class, SET_INTERFACES, this);
10. } else {
11. return Enhancer.create(Object.class, LIST_INTERFACES, this);
12. }
13. } else if (targetType.isArray() || ClassInfo.isKnownType(targetType)) {
14. //数组类型和其他的集合类型,以及一些iBATIS定义的简单类型不延迟加载
15. return ResultLoader.getResult(client, statementName, parameterObject, targetType);
16. } else {
17. //其他情况下延迟加载
18. return Enhancer.create(targetType, this);
19. }
20. }
public Object loadResult() throws SQLException { if (DomTypeMarker.class.isAssignableFrom(targetType)) { //这个DomTypeMarker不太清楚是干什么的 return ResultLoader.getResult(client, statementName, parameterObject, targetType); } else if (Collection.class.isAssignableFrom(targetType)) { //延迟加载集合类 if (Set.class.isAssignableFrom(targetType)) { return Enhancer.create(Object.class, SET_INTERFACES, this); } else { return Enhancer.create(Object.class, LIST_INTERFACES, this); } } else if (targetType.isArray() || ClassInfo.isKnownType(targetType)) { //数组类型和其他的集合类型,以及一些iBATIS定义的简单类型不延迟加载 return ResultLoader.getResult(client, statementName, parameterObject, targetType); } else { //其他情况下延迟加载 return Enhancer.create(targetType, this); } }
根据上述代码可知,采用了cglib创建代理对象的技术,而且被延迟加载的属性类型与LazyResultLoader中不同。如自定义类型的属性,在LazyResultLoader中不会被延迟加载,在EnhancedLazyResultLoader中会被延迟加载。究其根本原因在于:Java反射代理只能代理接口对象,而cglib的代理对象是基于asm的字节码增强技术,可以代理接口和对象。
在创建完了代理对象后,就是当请求真正发生时,代理对象如何处理?如何加载所需数据?下面就来看一下代理对象的invoke方法。
view plaincopy to clipboardprint?
1. public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
2.
3. if ("finalize".hashCode() == method.getName().hashCode()
4. && "finalize".equals(method.getName())) {
5. //处理finalize方法
6. return null;
7. } else {
8. //执行真正的加载数据操作
9. loadObject();
10. if (resultObject != null) {
11. try {
12. //客户对象向代理对象发出请求,代理对象将请求转发给已被加载出来的对象
13. return method.invoke(resultObject, objects);
14. } catch (Throwable t) {
15. throw ClassInfo.unwrapThrowable(t);
16. }
17. } else {
18. return null;
19. }
20. }
21. }
22.
23. private synchronized void loadObject() {
24. if (!loaded) {
25. try {
26. loaded = true;
27. resultObject = ResultLoader.getResult(client, statementName, parameterObject, targetType);
28. } catch (SQLException e) {
29. throw new RuntimeException("Error lazy loading result. Cause: " + e, e);
30. }
31. }
32. }
public Object invoke(Object o, Method method, Object[] objects) throws Throwable { if ("finalize".hashCode() == method.getName().hashCode() && "finalize".equals(method.getName())) { //处理finalize方法 return null; } else { //执行真正的加载数据操作 loadObject(); if (resultObject != null) { try { //客户对象向代理对象发出请求,代理对象将请求转发给已被加载出来的对象 return method.invoke(resultObject, objects); } catch (Throwable t) { throw ClassInfo.unwrapThrowable(t); } } else { return null; } } } private synchronized void loadObject() { if (!loaded) { try { loaded = true; resultObject = ResultLoader.getResult(client, statementName, parameterObject, targetType); } catch (SQLException e) { throw new RuntimeException("Error lazy loading result. Cause: " + e, e); } } }
经过上述的讲解,结合代码,可以比较深入的理解iBATIS延迟加载的实现机制。下面对以上内容做一个简短的总结:
延迟加载的核心思想是动态代理模式。首先并不加载数据,而是创建一个代理对象,需要数据的时候由代理对象去加载所需数据。iBatis中有两种延迟加载的方法:基于java反射的LazyResultLoader和基于cglib的EnhancedLazyResultLoader。由于二者实现代理的机制不同,所以对延迟加载的支持也不相同。LazyResultLoader只能延迟加载Collection类型的属性,而 EnhancedLazyResultLoader可以延迟加载Collection类型的属性,以及自定义类型的属性,而且性能方面要由于 LazyResultLoader。
分享到:
相关推荐
延迟加载(Lazy Loading)是iBatis的一种优化策略,只在真正需要时才加载关联的对象,减少了不必要的数据库交互。一对多或多对一关系的处理是对象关系映射(ORM)中的常见问题,iBatis提供了优雅的方式来管理这些...
`ibatis 延迟加载、对多关系、缓存的测试.mht`则深入探讨了iBatis的高级特性。延迟加载(Lazy Loading)允许在需要时才加载关联对象,可以有效提高性能。对多关系(One-to-Many, Many-to-One)的处理是ORM(对象关系...
在 MyBatis 中,延迟加载(Lazy Loading)和缓存是两个非常重要的特性,它们对于提升应用程序性能和减少数据库访问有着显著作用。 **延迟加载(Lazy Loading)** 延迟加载是一种设计模式,它在对象被首次请求时才...
此外,ibatis还支持延迟加载,即只有在真正需要时才加载关联对象,从而避免了不必要的数据加载和内存消耗。 ### ibatis高级特性 #### 动态SQL ibatis的一个强大特性是动态SQL。它允许开发者在运行时根据不同的...
iBatis提供了延迟加载(Lazy Loading)功能,允许在真正需要关联数据时才执行对应的SQL。在`resultMap`中开启延迟加载: ```xml ``` #### 动态映射 动态SQL让开发者可以根据条件动态构造SQL语句,使用`if`, `...
- **延迟加载**:对于一对多和一对一关联,可以启用延迟加载,仅在真正需要关联对象时才加载,提高效率。 #### ibatis高级特性 - **动态映射**:ibatis允许在SQL语句中使用动态参数,如`if`、`choose`、`foreach`...
2. **settings**:提供了一系列的开关,用于开启或关闭某些功能,如缓存的启用、延迟加载的开启等。 3. **typeAliases**:类型别名的配置,用于简化Java类型的引用。 4. **typeHandlers**:类型处理器,用于处理...
- **延迟加载**:利用 `<association>` 元素的 `fetchType` 属性实现延迟加载,以提高性能。 - **动态映射**:使用 `<if>`、`<choose>`、`<foreach>` 等元素构建动态 SQL 语句,提高代码复用性和灵活性。 #### ...
接着,书中还会介绍iBatis的高级特性,如结果映射、关联查询、延迟加载等。结果映射允许开发者将数据库查询结果自动映射到Java对象,简化了数据处理的过程。而关联查询则能处理一对一、一对多、多对多的关系,使得...
- **延迟加载**:延迟加载是在需要时才加载关联对象的数据,从而减少初始查询时的数据量。 - **动态映射**:支持在运行时动态构建SQL语句,提高了代码的灵活性。 - **事务管理**: - **基于JDBC的事务管理机制**:...
4. **性能优化**:通过延迟加载(Lazy Loading)和预加载(Eager Loading)策略,优化数据加载性能。 5. **日志集成**:允许开发者集成各种日志框架,以便记录数据库操作信息,便于调试和监控。 **总结** IBatis...
为了进一步提升性能,ibatis还支持延迟加载(Lazy Loading)。这意味着只有在真正需要时才会加载关联的数据,避免了不必要的数据传输和内存占用,特别是在处理大量关联数据时,这一特性尤为重要。 #### 动态映射 ...
延迟加载(Lazy Loading)功能可以在需要时才加载关联的数据,以提高性能。动态映射允许你在运行时根据 Java 对象的状态动态生成 SQL 语句,增强了 SQL 语句的可扩展性。 在事务管理方面,Ibatis 支持基于 JDBC 和 ...
1. **Settings**:用于定义ibatis运行时的行为参数,如缓存管理、字节码增强、延迟加载等。例如,`cacheModelsEnabled`用于控制是否启用缓存机制,这对于提高查询效率尤为重要;`lazyLoadingEnabled`则决定了关联...
8. **高级话题**:涵盖与iBATIS相关的高级主题,如批处理、延迟加载、自定义类型处理器、插件扩展等,提升开发者对iBATIS框架的深度理解。 9. **最佳实践**:提供实用的项目开发建议,如如何设计合理的映射文件结构...
同时,Ibatis也支持结果集的分页和延迟加载,进一步提升性能。 9. **插件扩展**: Ibatis允许开发者自定义插件,通过拦截器模式增强SqlSession或Executor的行为,例如日志记录、性能分析等。 这个自己编写的...