zoukankan      html  css  js  c++  java
  • mybatis源代码分析:mybatis延迟加载机制改进

    在上一篇博客mybatis源代码分析:深入了解mybatis延迟加载机制》讲诉了mybatis延迟加载的具体机制及实现原理。

    可以看出,如果查询结果对象中有一个属性是需要延迟加载的,那整个结果对象就是一个代理对象,后面对这个对象的访问,都是通过代理对象去访问的,那是否有更好的方法呢?

    答案是有的。

    Student对象关联Teacher对象,需要对Teacher对象延迟加载。

    一种方式就是mybatis当前的这种做法,对Student进行代理,当访问到getTeacher时就进行延迟加载,设置其teacher对象;

    还有一种方式就是对teacher对象进行代理,当第一次加载之后,就将Student对象关联的teacher对象替换为真实的对象,而不再使用代理对象,这样后面对Student及Teacher的访问,就都是访问的实际对象,而不是代理对象了。

    修改之后的代码:http://download.csdn.net/detail/u014569459/7372195


    关键修改点说明:

    1.修改orgapacheibatisexecutor esultsetDefaultResultSetHandler.java类,注销掉里面生成代理对象的代码

      private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
        final List<Class<?>> constructorArgTypes = new ArrayList<Class<?>>();
        final List<Object> constructorArgs = new ArrayList<Object>();
        final Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
        //comment by jerry
    //        if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) {
    //      final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
    //      for (ResultMapping propertyMapping : propertyMappings) {
    //        if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) { // issue gcode #109 && issue #149
    //          return configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
    //        }
    //      }
    //    }
        return resultObject;
      }

    2.修改orgapacheibatisexecutorloadercglibCglibProxyFactory.java类

    1)在createProxy方法增加两个参数Object parentObj,String fieldNameInParent,用于访问父对象。

      public Object createProxy(Object parentObj,String fieldNameInParent,Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
        return EnhancedResultObjectProxyImpl.createProxy(parentObj,fieldNameInParent,target, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
      }

    2)在EnhancedResultObjectProxyImpl类中,增加parentObj和属性,并修改默认构造函数。

        private Object parentObj;
        private String fieldNameInParent;
    
        private EnhancedResultObjectProxyImpl(Object parentObj,String fieldNameInParent,Class<?> type, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
          this.type = type;
          this.lazyLoader = lazyLoader;
          this.aggressive = configuration.isAggressiveLazyLoading();
          this.lazyLoadTriggerMethods = configuration.getLazyLoadTriggerMethods();
          this.objectFactory = objectFactory;
          this.constructorArgTypes = constructorArgTypes;
          this.constructorArgs = constructorArgs;
          this.parentObj = parentObj;
          this.fieldNameInParent = fieldNameInParent;
        }

    3)修改intercept函数

            if(parentObj!=null)
            {
                Field field = parentObj.getClass().getDeclaredField(fieldNameInParent);
                Object obj = field.get(parentObj);
                return method.invoke(obj, args);
            }
            else
            {
            	return methodProxy.invokeSuper(enhanced, args);	
            }


    修改文件列表(共4个):

    1.CglibProxyFactory.java

    /*
     *    Copyright 2009-2013 the original author or authors.
     *
     *    Licensed under the Apache License, Version 2.0 (the "License");
     *    you may not use this file except in compliance with the License.
     *    You may obtain a copy of the License at
     *
     *       http://www.apache.org/licenses/LICENSE-2.0
     *
     *    Unless required by applicable law or agreed to in writing, software
     *    distributed under the License is distributed on an "AS IS" BASIS,
     *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     *    See the License for the specific language governing permissions and
     *    limitations under the License.
     */
    package org.apache.ibatis.executor.loader.cglib;
    
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.util.List;
    import java.util.Map;
    import java.util.Properties;
    import java.util.Set;
    
    import net.sf.cglib.proxy.Callback;
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    import org.apache.ibatis.executor.loader.AbstractEnhancedDeserializationProxy;
    import org.apache.ibatis.executor.loader.AbstractSerialStateHolder;
    import org.apache.ibatis.executor.loader.ProxyFactory;
    import org.apache.ibatis.executor.loader.ResultLoaderMap;
    import org.apache.ibatis.executor.loader.WriteReplaceInterface;
    import org.apache.ibatis.io.Resources;
    import org.apache.ibatis.logging.Log;
    import org.apache.ibatis.logging.LogFactory;
    import org.apache.ibatis.reflection.ExceptionUtil;
    import org.apache.ibatis.reflection.factory.ObjectFactory;
    import org.apache.ibatis.reflection.property.PropertyCopier;
    import org.apache.ibatis.reflection.property.PropertyNamer;
    import org.apache.ibatis.session.Configuration;
    
    /**
     * @author Clinton Begin
     */
    public class CglibProxyFactory implements ProxyFactory {
    
      private static final Log log = LogFactory.getLog(CglibProxyFactory.class);
      private static final String FINALIZE_METHOD = "finalize";
      private static final String WRITE_REPLACE_METHOD = "writeReplace";
    
      public CglibProxyFactory() {
        try {
          Resources.classForName("net.sf.cglib.proxy.Enhancer");
        } catch (Throwable e) {
          throw new IllegalStateException("Cannot enable lazy loading because CGLIB is not available. Add CGLIB to your classpath.", e);
        }
      }
    
      public Object createProxy(Object parentObj,String fieldNameInParent,Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
        return EnhancedResultObjectProxyImpl.createProxy(parentObj,fieldNameInParent,target, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
      }
      public Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
        return EnhancedResultObjectProxyImpl.createProxy(null,null,target, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
      }
    
      public Object createDeserializationProxy(Object target, Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
        return EnhancedDeserializationProxyImpl.createProxy(null,null,target, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
      }
    
      public void setProperties(Properties properties) {
      }
    
      private static Object crateProxy(Class<?> type, Callback callback, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
        Enhancer enhancer = new Enhancer();
        enhancer.setCallback(callback);
        enhancer.setSuperclass(type);
        try {
          type.getDeclaredMethod(WRITE_REPLACE_METHOD);
          // ObjectOutputStream will call writeReplace of objects returned by writeReplace
          log.debug(WRITE_REPLACE_METHOD + " method was found on bean " + type + ", make sure it returns this");
        } catch (NoSuchMethodException e) {
          enhancer.setInterfaces(new Class[]{WriteReplaceInterface.class});
        } catch (SecurityException e) {
          // nothing to do here
        }
        Object enhanced = null;
        if (constructorArgTypes.isEmpty()) {
          enhanced = enhancer.create();
        } else {
          Class<?>[] typesArray = constructorArgTypes.toArray(new Class[constructorArgTypes.size()]);
          Object[] valuesArray = constructorArgs.toArray(new Object[constructorArgs.size()]);
          enhanced = enhancer.create(typesArray, valuesArray);
        }
        return enhanced;
      }
    
      private static class EnhancedResultObjectProxyImpl implements MethodInterceptor {
    
        private Class<?> type;
        private ResultLoaderMap lazyLoader;
        private boolean aggressive;
        private Set<String> lazyLoadTriggerMethods;
        private ObjectFactory objectFactory;
        private List<Class<?>> constructorArgTypes;
        private List<Object> constructorArgs;
        private Object parentObj;
        private String fieldNameInParent;
    
        private EnhancedResultObjectProxyImpl(Object parentObj,String fieldNameInParent,Class<?> type, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
          this.type = type;
          this.lazyLoader = lazyLoader;
          this.aggressive = configuration.isAggressiveLazyLoading();
          this.lazyLoadTriggerMethods = configuration.getLazyLoadTriggerMethods();
          this.objectFactory = objectFactory;
          this.constructorArgTypes = constructorArgTypes;
          this.constructorArgs = constructorArgs;
          this.parentObj = parentObj;
          this.fieldNameInParent = fieldNameInParent;
        }
    
        public static Object createProxy(Object parentObj,String fieldNameInParent,Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
    
          final Class<?> type = target.getClass();
          EnhancedResultObjectProxyImpl callback = new EnhancedResultObjectProxyImpl(parentObj,fieldNameInParent,type, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
          Object enhanced = crateProxy(type, callback, constructorArgTypes, constructorArgs);
          PropertyCopier.copyBeanProperties(type, target, enhanced);
          return enhanced;
        }
    
        public Object intercept(Object enhanced, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
          final String methodName = method.getName();
          try {
            synchronized (lazyLoader) {
              if (WRITE_REPLACE_METHOD.equals(methodName)) {
                Object original = null;
                if (constructorArgTypes.isEmpty()) {
                  original = objectFactory.create(type);
                } else {
                  original = objectFactory.create(type, constructorArgTypes, constructorArgs);
                }
                PropertyCopier.copyBeanProperties(type, enhanced, original);
                if (lazyLoader.size() > 0) {
                  return new CglibSerialStateHolder(original, lazyLoader.getProperties(), objectFactory, constructorArgTypes, constructorArgs);
                } else {
                  return original;
                }
              } else {
                if (lazyLoader.size() > 0 && !FINALIZE_METHOD.equals(methodName)) {
                  if (aggressive || lazyLoadTriggerMethods.contains(methodName)) {
                    lazyLoader.loadAll();
                  } else if (PropertyNamer.isProperty(methodName)) {
                    final String property = PropertyNamer.methodToProperty(methodName);
                    if (lazyLoader.hasLoader(property)) {
                      lazyLoader.load(property);
                    }
                  }
                }
              }
            }
            
            if(parentObj!=null)
            {
                Field field = parentObj.getClass().getDeclaredField(fieldNameInParent);
                Object obj = field.get(parentObj);
                return method.invoke(obj, args);
            }
            else
            {
            	return methodProxy.invokeSuper(enhanced, args);	
            }
          } catch (Throwable t) {
            throw ExceptionUtil.unwrapThrowable(t);
          }
        }
      }
    
      private static class EnhancedDeserializationProxyImpl extends AbstractEnhancedDeserializationProxy implements MethodInterceptor {
    
        private EnhancedDeserializationProxyImpl(Object parentObj,String fieldNameInParent,Class<?> type, Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory,
                List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
          super(type, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
        }
    
        public static Object createProxy(Object parentObj,String fieldNameInParent,Object target, Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory,
                List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
          final Class<?> type = target.getClass();
          EnhancedDeserializationProxyImpl callback = new EnhancedDeserializationProxyImpl(parentObj,fieldNameInParent,type, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
          Object enhanced = crateProxy(type, callback, constructorArgTypes, constructorArgs);
          PropertyCopier.copyBeanProperties(type, target, enhanced);
          return enhanced;
        }
    
        @Override
        public Object intercept(Object enhanced, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
          final Object o = super.invoke(enhanced, method, args);
          return (o instanceof AbstractSerialStateHolder) ? o : methodProxy.invokeSuper(o, args);
        }
    
        @Override
        protected AbstractSerialStateHolder newSerialStateHolder(Object userBean, Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory,
                List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
          return new CglibSerialStateHolder(userBean, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
        }
      }
    }
    

    2.JavassistProxyFactory.java

    /*
     *    Copyright 2009-2012 the original author or authors.
     *
     *    Licensed under the Apache License, Version 2.0 (the "License");
     *    you may not use this file except in compliance with the License.
     *    You may obtain a copy of the License at
     *
     *       http://www.apache.org/licenses/LICENSE-2.0
     *
     *    Unless required by applicable law or agreed to in writing, software
     *    distributed under the License is distributed on an "AS IS" BASIS,
     *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     *    See the License for the specific language governing permissions and
     *    limitations under the License.
     */
    package org.apache.ibatis.executor.loader.javassist;
    
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.util.List;
    import java.util.Map;
    import java.util.Properties;
    import java.util.Set;
    
    import javassist.util.proxy.MethodHandler;
    import javassist.util.proxy.Proxy;
    import javassist.util.proxy.ProxyFactory;
    
    import org.apache.ibatis.executor.ExecutorException;
    import org.apache.ibatis.executor.loader.AbstractEnhancedDeserializationProxy;
    import org.apache.ibatis.executor.loader.AbstractSerialStateHolder;
    import org.apache.ibatis.executor.loader.ResultLoaderMap;
    import org.apache.ibatis.executor.loader.WriteReplaceInterface;
    import org.apache.ibatis.io.Resources;
    import org.apache.ibatis.logging.Log;
    import org.apache.ibatis.logging.LogFactory;
    import org.apache.ibatis.reflection.ExceptionUtil;
    import org.apache.ibatis.reflection.factory.ObjectFactory;
    import org.apache.ibatis.reflection.property.PropertyCopier;
    import org.apache.ibatis.reflection.property.PropertyNamer;
    import org.apache.ibatis.session.Configuration;
    
    /**
     * @author Eduardo Macarron
     */
    public class JavassistProxyFactory implements org.apache.ibatis.executor.loader.ProxyFactory {
    
      private static final Log log = LogFactory.getLog(JavassistProxyFactory.class);
      private static final String FINALIZE_METHOD = "finalize";
      private static final String WRITE_REPLACE_METHOD = "writeReplace";
    
      public JavassistProxyFactory() {
        try {
          Resources.classForName("javassist.util.proxy.ProxyFactory");
        } catch (Throwable e) {
          throw new IllegalStateException("Cannot enable lazy loading because Javassist is not available. Add Javassist to your classpath.", e);
        }
      }
    
      public Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
        return EnhancedResultObjectProxyImpl.createProxy(null,null,target, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
      }
      public Object createProxy(Object parentObj, String fieldNameInParent,Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
    	    return EnhancedResultObjectProxyImpl.createProxy(parentObj,fieldNameInParent,target, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
      }
    
      public Object createDeserializationProxy(Object target, Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
        return EnhancedDeserializationProxyImpl.createProxy(target, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
      }
    
      public void setProperties(Properties properties) {
      }
    
      private static Object crateProxy(Class<?> type, MethodHandler callback, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
    
        ProxyFactory enhancer = new ProxyFactory();
        enhancer.setSuperclass(type);
    
        try {
          type.getDeclaredMethod(WRITE_REPLACE_METHOD);
          // ObjectOutputStream will call writeReplace of objects returned by writeReplace
          log.debug(WRITE_REPLACE_METHOD + " method was found on bean " + type + ", make sure it returns this");
        } catch (NoSuchMethodException e) {
          enhancer.setInterfaces(new Class[]{WriteReplaceInterface.class});
        } catch (SecurityException e) {
          // nothing to do here
        }
    
        Object enhanced = null;
        Class<?>[] typesArray = constructorArgTypes.toArray(new Class[constructorArgTypes.size()]);
        Object[] valuesArray = constructorArgs.toArray(new Object[constructorArgs.size()]);
        try {
          enhanced = enhancer.create(typesArray, valuesArray);
        } catch (Exception e) {
          throw new ExecutorException("Error creating lazy proxy.  Cause: " + e, e);
        }
        ((Proxy) enhanced).setHandler(callback);
        return enhanced;
      }
    
      private static class EnhancedResultObjectProxyImpl implements MethodHandler {
    
        private Class<?> type;
        private ResultLoaderMap lazyLoader;
        private boolean aggressive;
        private Set<String> lazyLoadTriggerMethods;
        private ObjectFactory objectFactory;
        private List<Class<?>> constructorArgTypes;
        private List<Object> constructorArgs;
        private Object parentObj;
        private String fieldNameInParent;
        
        private EnhancedResultObjectProxyImpl(Object parentObj, String fieldNameInParent,Class<?> type, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
          this.type = type;
          this.lazyLoader = lazyLoader;
          this.aggressive = configuration.isAggressiveLazyLoading();
          this.lazyLoadTriggerMethods = configuration.getLazyLoadTriggerMethods();
          this.objectFactory = objectFactory;
          this.constructorArgTypes = constructorArgTypes;
          this.constructorArgs = constructorArgs;
          this.parentObj = parentObj;
          this.fieldNameInParent = fieldNameInParent;
        }
    
        public static Object createProxy(Object parentObj, String fieldNameInParent,Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
          final Class<?> type = target.getClass();
          EnhancedResultObjectProxyImpl callback = new EnhancedResultObjectProxyImpl(parentObj, fieldNameInParent,type, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
          Object enhanced = crateProxy(type, callback, constructorArgTypes, constructorArgs);
          PropertyCopier.copyBeanProperties(type, target, enhanced);
          return enhanced;
        }
    
        public Object invoke(Object enhanced, Method method, Method methodProxy, Object[] args) throws Throwable {
          final String methodName = method.getName();
          try {
            synchronized (lazyLoader) {
              if (WRITE_REPLACE_METHOD.equals(methodName)) {
                Object original = null;
                if (constructorArgTypes.isEmpty()) {
                  original = objectFactory.create(type);
                } else {
                  original = objectFactory.create(type, constructorArgTypes, constructorArgs);
                }
                PropertyCopier.copyBeanProperties(type, enhanced, original);
                if (lazyLoader.size() > 0) {
                  return new JavassistSerialStateHolder(original, lazyLoader.getProperties(), objectFactory, constructorArgTypes, constructorArgs);
                } else {
                  return original;
                }
              } else {
                if (lazyLoader.size() > 0 && !FINALIZE_METHOD.equals(methodName)) {
                  if (aggressive || lazyLoadTriggerMethods.contains(methodName)) {
                    lazyLoader.loadAll();
                  } else if (PropertyNamer.isProperty(methodName)) {
                    final String property = PropertyNamer.methodToProperty(methodName);
                    if (lazyLoader.hasLoader(property)) {
                      lazyLoader.load(property);
                    }
                  }
                }
              }
            }
            if(parentObj!=null)
            {
                Field field = parentObj.getClass().getDeclaredField(fieldNameInParent);
                Object obj = field.get(parentObj);
                return method.invoke(obj, args);
            }
            else
            {
            	return methodProxy.invoke(enhanced, args);	
            }
          } catch (Throwable t) {
            throw ExceptionUtil.unwrapThrowable(t);
          }
        }
      }
    
      private static class EnhancedDeserializationProxyImpl extends AbstractEnhancedDeserializationProxy implements MethodHandler {
    
        private EnhancedDeserializationProxyImpl(Class<?> type, Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory,
                List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
          super(type, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
        }
    
        public static Object createProxy(Object target, Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory,
                List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
          final Class<?> type = target.getClass();
          EnhancedDeserializationProxyImpl callback = new EnhancedDeserializationProxyImpl(type, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
          Object enhanced = crateProxy(type, callback, constructorArgTypes, constructorArgs);
          PropertyCopier.copyBeanProperties(type, target, enhanced);
          return enhanced;
        }
    
        @Override
        public Object invoke(Object enhanced, Method method, Method methodProxy, Object[] args) throws Throwable {
          final Object o = super.invoke(enhanced, method, args);
          return (o instanceof AbstractSerialStateHolder) ? o : methodProxy.invoke(o, args);
        }
    
        @Override
        protected AbstractSerialStateHolder newSerialStateHolder(Object userBean, Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory,
                List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
          return new JavassistSerialStateHolder(userBean, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
        }
      }
    
    }
    

    3.ProxyFactory.java

    /*
     *    Copyright 2012 the original author or authors.
     *
     *    Licensed under the Apache License, Version 2.0 (the "License");
     *    you may not use this file except in compliance with the License.
     *    You may obtain a copy of the License at
     *
     *       http://www.apache.org/licenses/LICENSE-2.0
     *
     *    Unless required by applicable law or agreed to in writing, software
     *    distributed under the License is distributed on an "AS IS" BASIS,
     *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     *    See the License for the specific language governing permissions and
     *    limitations under the License.
     */
    package org.apache.ibatis.executor.loader;
    
    import java.util.List;
    import java.util.Properties;
    
    import org.apache.ibatis.reflection.factory.ObjectFactory;
    import org.apache.ibatis.session.Configuration;
    
    /**
     * @author Eduardo Macarron
     */
    public interface ProxyFactory {
    
      void setProperties(Properties properties);
    
      Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs);
      Object createProxy(Object parentObj,String fieldNameInParent,Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs);
      
    }
    

    4.DefaultResultSetHandler.java

    /*
     *    Copyright 2009-2014 the original author or authors.
     *
     *    Licensed under the Apache License, Version 2.0 (the "License");
     *    you may not use this file except in compliance with the License.
     *    You may obtain a copy of the License at
     *
     *       http://www.apache.org/licenses/LICENSE-2.0
     *
     *    Unless required by applicable law or agreed to in writing, software
     *    distributed under the License is distributed on an "AS IS" BASIS,
     *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     *    See the License for the specific language governing permissions and
     *    limitations under the License.
     */
    package org.apache.ibatis.executor.resultset;
    
    import java.sql.CallableStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.List;
    import java.util.Locale;
    import java.util.Map;
    import java.util.Set;
    
    import org.apache.ibatis.cache.CacheKey;
    import org.apache.ibatis.executor.Executor;
    import org.apache.ibatis.executor.ExecutorException;
    import org.apache.ibatis.executor.loader.ResultLoader;
    import org.apache.ibatis.executor.loader.ResultLoaderMap;
    import org.apache.ibatis.executor.parameter.ParameterHandler;
    import org.apache.ibatis.executor.result.DefaultResultContext;
    import org.apache.ibatis.executor.result.DefaultResultHandler;
    import org.apache.ibatis.mapping.BoundSql;
    import org.apache.ibatis.mapping.Discriminator;
    import org.apache.ibatis.mapping.MappedStatement;
    import org.apache.ibatis.mapping.ParameterMapping;
    import org.apache.ibatis.mapping.ParameterMode;
    import org.apache.ibatis.mapping.ResultMap;
    import org.apache.ibatis.mapping.ResultMapping;
    import org.apache.ibatis.reflection.MetaClass;
    import org.apache.ibatis.reflection.MetaObject;
    import org.apache.ibatis.reflection.factory.ObjectFactory;
    import org.apache.ibatis.session.AutoMappingBehavior;
    import org.apache.ibatis.session.Configuration;
    import org.apache.ibatis.session.ResultContext;
    import org.apache.ibatis.session.ResultHandler;
    import org.apache.ibatis.session.RowBounds;
    import org.apache.ibatis.type.TypeHandler;
    import org.apache.ibatis.type.TypeHandlerRegistry;
    
    /**
     * @author Clinton Begin
     * @author Eduardo Macarron
     */
    public class DefaultResultSetHandler implements ResultSetHandler {
    
      private static final Object NO_VALUE = new Object();
    
      private final Executor executor;
      private final Configuration configuration;
      private final MappedStatement mappedStatement;
      private final RowBounds rowBounds;
      private final ParameterHandler parameterHandler;
      private final ResultHandler resultHandler;
      private final BoundSql boundSql;
      private final TypeHandlerRegistry typeHandlerRegistry;
      private final ObjectFactory objectFactory;
    
      // nested resultmaps
      private final Map<CacheKey, Object> nestedResultObjects = new HashMap<CacheKey, Object>();
      private final Map<CacheKey, Object> ancestorObjects = new HashMap<CacheKey, Object>();
      private final Map<String, String> ancestorColumnPrefix = new HashMap<String, String>();
    
      // multiple resultsets
      private final Map<String, ResultMapping> nextResultMaps = new HashMap<String, ResultMapping>();
      private final Map<CacheKey, PendingRelation> pendingRelations = new HashMap<CacheKey, PendingRelation>();
      
      private static class PendingRelation {
        public MetaObject metaObject;
        public ResultMapping propertyMapping;
      }
      
      public DefaultResultSetHandler(Executor executor, MappedStatement mappedStatement, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql,
          RowBounds rowBounds) {
        this.executor = executor;
        this.configuration = mappedStatement.getConfiguration();
        this.mappedStatement = mappedStatement;
        this.rowBounds = rowBounds;
        this.parameterHandler = parameterHandler;
        this.boundSql = boundSql;
        this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
        this.objectFactory = configuration.getObjectFactory();
        this.resultHandler = resultHandler;
      }
    
      //
      // HANDLE OUTPUT PARAMETER
      //
    
      public void handleOutputParameters(CallableStatement cs) throws SQLException {
        final Object parameterObject = parameterHandler.getParameterObject();
        final MetaObject metaParam = configuration.newMetaObject(parameterObject);
        final List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        for (int i = 0; i < parameterMappings.size(); i++) {
          final ParameterMapping parameterMapping = parameterMappings.get(i);
          if (parameterMapping.getMode() == ParameterMode.OUT || parameterMapping.getMode() == ParameterMode.INOUT) {
            if (ResultSet.class.equals(parameterMapping.getJavaType())) {
              handleRefCursorOutputParameter((ResultSet) cs.getObject(i + 1), parameterMapping, metaParam);
            } else {
              final TypeHandler<?> typeHandler = parameterMapping.getTypeHandler();
              metaParam.setValue(parameterMapping.getProperty(), typeHandler.getResult(cs, i + 1));
            }
          }
        }
      }
    
      private void handleRefCursorOutputParameter(ResultSet rs, ParameterMapping parameterMapping, MetaObject metaParam) throws SQLException {
        try {
          final String resultMapId = parameterMapping.getResultMapId();
          final ResultMap resultMap = configuration.getResultMap(resultMapId);
          final DefaultResultHandler resultHandler = new DefaultResultHandler(objectFactory);
          final ResultSetWrapper rsw = new ResultSetWrapper(rs, configuration);
          handleRowValues(rsw, resultMap, resultHandler, new RowBounds(), null);
          metaParam.setValue(parameterMapping.getProperty(), resultHandler.getResultList());
        } finally {
          closeResultSet(rs); // issue #228 (close resultsets)
        }
      }
    
      //
      // HANDLE RESULT SETS
      //
    
      public List<Object> handleResultSets(Statement stmt) throws SQLException {
        final List<Object> multipleResults = new ArrayList<Object>();
    
        int resultSetCount = 0;
        ResultSetWrapper rsw = getFirstResultSet(stmt);
    
        List<ResultMap> resultMaps = mappedStatement.getResultMaps();
        int resultMapCount = resultMaps.size();
        validateResultMapsCount(rsw, resultMapCount);
        while (rsw != null && resultMapCount > resultSetCount) {
          ResultMap resultMap = resultMaps.get(resultSetCount);
          handleResultSet(rsw, resultMap, multipleResults, null);
          rsw = getNextResultSet(stmt);
          cleanUpAfterHandlingResultSet();
          resultSetCount++;
        }
    
        String[] resultSets = mappedStatement.getResulSets();
        if (resultSets != null) {
          while (rsw != null && resultSetCount < resultSets.length) {
            ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
            if (parentMapping != null) {
              String nestedResultMapId = parentMapping.getNestedResultMapId();
              ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
              handleResultSet(rsw, resultMap, null, parentMapping);
            }
            rsw = getNextResultSet(stmt);
            cleanUpAfterHandlingResultSet();
            resultSetCount++;
          }
        }
    
        return collapseSingleResultList(multipleResults);
      }
    
      private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
        ResultSet rs = stmt.getResultSet();
        while (rs == null) {
          // move forward to get the first resultset in case the driver
          // doesn't return the resultset as the first result (HSQLDB 2.1)
          if (stmt.getMoreResults()) {
            rs = stmt.getResultSet();
          } else {
            if (stmt.getUpdateCount() == -1) {
              // no more results. Must be no resultset
              break;
            }
          }
        }
        return rs != null ? new ResultSetWrapper(rs, configuration) : null;
      }
    
      private ResultSetWrapper getNextResultSet(Statement stmt) throws SQLException {
        // Making this method tolerant of bad JDBC drivers
        try {
          if (stmt.getConnection().getMetaData().supportsMultipleResultSets()) {
            // Crazy Standard JDBC way of determining if there are more results
            if (!((!stmt.getMoreResults()) && (stmt.getUpdateCount() == -1))) {
              ResultSet rs = stmt.getResultSet();
              return rs != null ? new ResultSetWrapper(rs, configuration) : null;
            }
          }
        } catch (Exception e) {
          // Intentionally ignored.
        }
        return null;
      }
    
      private void closeResultSet(ResultSet rs) {
        try {
          if (rs != null) {
            rs.close();
          }
        } catch (SQLException e) {
          // ignore
        }
      }
    
      private void cleanUpAfterHandlingResultSet() {
        nestedResultObjects.clear();
        ancestorColumnPrefix.clear();
      }
    
      private void validateResultMapsCount(ResultSetWrapper rsw, int resultMapCount) {
        if (rsw != null && resultMapCount < 1) {
          throw new ExecutorException("A query was run and no Result Maps were found for the Mapped Statement '" + mappedStatement.getId()
              + "'.  It's likely that neither a Result Type nor a Result Map was specified.");
        }
      }
    
      private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
        try {
          if (parentMapping != null) {
            handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
          } else {
            if (resultHandler == null) {
              DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
              handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
              multipleResults.add(defaultResultHandler.getResultList());
            } else {
              handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
            }
          }
        } finally {
          closeResultSet(rsw.getResultSet()); // issue #228 (close resultsets)
        }
      }
    
      private List<Object> collapseSingleResultList(List<Object> multipleResults) {
        if (multipleResults.size() == 1) {
          @SuppressWarnings("unchecked")
          List<Object> returned = (List<Object>) multipleResults.get(0);
          return returned;
        }
        return multipleResults;
      }
    
      //
      // HANDLE ROWS FOR SIMPLE RESULTMAP
      //
    
      private void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
        if (resultMap.hasNestedResultMaps()) {
          ensureNoRowBounds();
          checkResultHandler();
          handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
        } else {
          handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
        }
      }  
    
      private void ensureNoRowBounds() {
        if (configuration.isSafeRowBoundsEnabled() && rowBounds != null && (rowBounds.getLimit() < RowBounds.NO_ROW_LIMIT || rowBounds.getOffset() > RowBounds.NO_ROW_OFFSET)) {
          throw new ExecutorException("Mapped Statements with nested result mappings cannot be safely constrained by RowBounds. "
              + "Use safeRowBoundsEnabled=false setting to bypass this check.");
        }
      }
    
      protected void checkResultHandler() {
        if (resultHandler != null && configuration.isSafeResultHandlerEnabled() && !mappedStatement.isResultOrdered()) {
          throw new ExecutorException("Mapped Statements with nested result mappings cannot be safely used with a custom ResultHandler. "
              + "Use safeResultHandlerEnabled=false setting to bypass this check " 
              + "or ensure your statement returns ordered data and set resultOrdered=true on it.");
        }
      } 
    
      private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
          throws SQLException {
        DefaultResultContext resultContext = new DefaultResultContext();
        skipRows(rsw.getResultSet(), rowBounds);
        while (shouldProcessMoreRows(rsw.getResultSet(), resultContext, rowBounds)) {
          ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
          Object rowValue = getRowValue(rsw, discriminatedResultMap);
          storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
        }
      }
    
      private void storeObject(ResultHandler resultHandler, DefaultResultContext resultContext, Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException {
        if (parentMapping != null) {
          linkToParent(rs, parentMapping, rowValue);
        } else {
          callResultHandler(resultHandler, resultContext, rowValue);
        }
      }
    
      private void callResultHandler(ResultHandler resultHandler, DefaultResultContext resultContext, Object rowValue) {
        resultContext.nextResultObject(rowValue);
        resultHandler.handleResult(resultContext);
      }
    
      private boolean shouldProcessMoreRows(ResultSet rs, ResultContext context, RowBounds rowBounds) throws SQLException {
        return !context.isStopped() && rs.next() && context.getResultCount() < rowBounds.getLimit();
      }
    
      private void skipRows(ResultSet rs, RowBounds rowBounds) throws SQLException {
        if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY) {
          if (rowBounds.getOffset() != RowBounds.NO_ROW_OFFSET) {
            rs.absolute(rowBounds.getOffset());
          }
        } else {
          for (int i = 0; i < rowBounds.getOffset(); i++) {
            rs.next();
          }
        }
      }
    
      //
      // GET VALUE FROM ROW FOR SIMPLE RESULT MAP
      //
    
      private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
        final ResultLoaderMap lazyLoader = new ResultLoaderMap();
        Object resultObject = createResultObject(rsw, resultMap, lazyLoader, null);
        if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) {
          final MetaObject metaObject = configuration.newMetaObject(resultObject);
          boolean foundValues = resultMap.getConstructorResultMappings().size() > 0;
          if (shouldApplyAutomaticMappings(resultMap, !AutoMappingBehavior.NONE.equals(configuration.getAutoMappingBehavior()))) {        
            foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
          }
          foundValues = applyPropertyMappings(resultObject,rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
          foundValues = lazyLoader.size() > 0 || foundValues;
          resultObject = foundValues ? resultObject : null;
          return resultObject;
        }
        return resultObject;
      }
    
      private boolean shouldApplyAutomaticMappings(ResultMap resultMap, boolean def) {
        return resultMap.getAutoMapping() != null ? resultMap.getAutoMapping() : def;
      }
    
      //
      // PROPERTY MAPPINGS
      //
    
      private boolean applyPropertyMappings(Object resultObject,ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
          throws SQLException {
        final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
        boolean foundValues = false;
        final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
        for (ResultMapping propertyMapping : propertyMappings) {
          final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
          if (propertyMapping.isCompositeResult() 
              || (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH))) 
              || propertyMapping.getResultSet() != null) {
            Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
            //added by jerry
            if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) { 
            	value = new dao.domain.Teacher();
            	try {
    				value = propertyMapping.getJavaType().newInstance();
    	        	value = configuration.getProxyFactory().createProxy(resultObject,propertyMapping.getProperty(),value, lazyLoader, configuration, objectFactory, new ArrayList(), new ArrayList());
    			} catch (Exception e) {
    				e.printStackTrace();
    			} 
            }
            	
            final String property = propertyMapping.getProperty(); // issue #541 make property optional
            if (value != NO_VALUE && property != null && (value != null || configuration.isCallSettersOnNulls())) { // issue #377, call setter on nulls
              if (value != null || !metaObject.getSetterType(property).isPrimitive()) {
                metaObject.setValue(property, value);
              }
              foundValues = true;
            }
          }
        }
        
        return foundValues;
      }
    
      private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)
          throws SQLException {
        if (propertyMapping.getNestedQueryId() != null) {
          return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
        } else if (propertyMapping.getResultSet() != null) {
          addPendingChildRelation(rs, metaResultObject, propertyMapping);
          return NO_VALUE;
        } else if (propertyMapping.getNestedResultMapId() != null) {
          // the user added a column attribute to a nested result map, ignore it
          return NO_VALUE;
        } else {
          final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
          final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
          return typeHandler.getResult(rs, column);
        }
      }
    
      private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
        final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
        boolean foundValues = false;
        for (String columnName : unmappedColumnNames) {
          String propertyName = columnName;
          if (columnPrefix != null && columnPrefix.length() > 0) {
            // When columnPrefix is specified,
            // ignore columns without the prefix.
            if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {
              propertyName = columnName.substring(columnPrefix.length());
            } else {
              continue;
            }
          }
          final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
          if (property != null && metaObject.hasSetter(property)) {
            final Class<?> propertyType = metaObject.getSetterType(property);
            if (typeHandlerRegistry.hasTypeHandler(propertyType)) {
              final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
              final Object value = typeHandler.getResult(rsw.getResultSet(), columnName);
              if (value != null || configuration.isCallSettersOnNulls()) { // issue #377, call setter on nulls
                if (value != null || !propertyType.isPrimitive()) {
                  metaObject.setValue(property, value);
                }
                foundValues = true;
              }
            }
          }
        }
        return foundValues;
      }
    
      // MULTIPLE RESULT SETS
    
      private void linkToParent(ResultSet rs, ResultMapping parentMapping, Object rowValue) throws SQLException {
        CacheKey parentKey = createKeyForMultipleResults(rs, parentMapping, parentMapping.getColumn(), parentMapping.getForeignColumn());
        PendingRelation parent = pendingRelations.get(parentKey);
        if (parent != null) {
          final Object collectionProperty = instantiateCollectionPropertyIfAppropriate(parent.propertyMapping, parent.metaObject);
          if (rowValue != null) {
            if (collectionProperty != null) {
              final MetaObject targetMetaObject = configuration.newMetaObject(collectionProperty);
              targetMetaObject.add(rowValue);
            } else {
              parent.metaObject.setValue(parent.propertyMapping.getProperty(), rowValue);
            }
          }
        }
      }
    
      private Object instantiateCollectionPropertyIfAppropriate(ResultMapping resultMapping, MetaObject metaObject) {
        final String propertyName = resultMapping.getProperty();
        Object propertyValue = metaObject.getValue(propertyName);
        if (propertyValue == null) {
          Class<?> type = resultMapping.getJavaType();
          if (type == null) {
            type = metaObject.getSetterType(propertyName);
          }
          try {
            if (objectFactory.isCollection(type)) {
              propertyValue = objectFactory.create(type);
              metaObject.setValue(propertyName, propertyValue);
              return propertyValue;
            }
          } catch (Exception e) {
            throw new ExecutorException("Error instantiating collection property for result '" + resultMapping.getProperty() + "'.  Cause: " + e, e);
          }
        } else if (objectFactory.isCollection(propertyValue.getClass())) {
          return propertyValue;
        }
        return null;
      }
    
      private void addPendingChildRelation(ResultSet rs, MetaObject metaResultObject, ResultMapping parentMapping) throws SQLException {
        CacheKey cacheKey = createKeyForMultipleResults(rs, parentMapping, parentMapping.getColumn(), parentMapping.getColumn());
        PendingRelation deferLoad = new PendingRelation();
        deferLoad.metaObject = metaResultObject;
        deferLoad.propertyMapping = parentMapping;
        pendingRelations.put(cacheKey, deferLoad);
        ResultMapping previous = nextResultMaps.get(parentMapping.getResultSet());
        if (previous == null) {
          nextResultMaps.put(parentMapping.getResultSet(), parentMapping);
        } else {
          if (!previous.equals(parentMapping)) {
            throw new ExecutorException("Two different properties are mapped to the same resultSet");
          }
        }
      }
    
      private CacheKey createKeyForMultipleResults(ResultSet rs, ResultMapping resultMapping, String names, String columns) throws SQLException {
        CacheKey cacheKey = new CacheKey();
        cacheKey.update(resultMapping);
        if (columns != null && names != null) {
          String[] columnsArray = columns.split(",");
          String[] namesArray = names.split(",");
          for (int i = 0 ; i < columnsArray.length ; i++) {
            Object value = rs.getString(columnsArray[i]);
            if (value != null) {
              cacheKey.update(namesArray[i]);
              cacheKey.update(value);
            }
          }
        }
        return cacheKey;
      }
    
      //
      // INSTANTIATION & CONSTRUCTOR MAPPING
      //
    
      private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
        final List<Class<?>> constructorArgTypes = new ArrayList<Class<?>>();
        final List<Object> constructorArgs = new ArrayList<Object>();
        final Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
        //comment by jerry
    //        if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) {
    //      final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
    //      for (ResultMapping propertyMapping : propertyMappings) {
    //        if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) { // issue gcode #109 && issue #149
    //          return configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
    //        }
    //      }
    //    }
        return resultObject;
      }
    
      private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
          throws SQLException {
        final Class<?> resultType = resultMap.getType();
        final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
        if (typeHandlerRegistry.hasTypeHandler(resultType)) {
          return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
        } else if (constructorMappings.size() > 0) {
          return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
        } else {
          return objectFactory.create(resultType);
        }
      }
    
      private Object createParameterizedResultObject(ResultSetWrapper rsw, Class<?> resultType, List<ResultMapping> constructorMappings, List<Class<?>> constructorArgTypes,
          List<Object> constructorArgs, String columnPrefix) throws SQLException {
        boolean foundValues = false;
        for (ResultMapping constructorMapping : constructorMappings) {
          final Class<?> parameterType = constructorMapping.getJavaType();
          final String column = constructorMapping.getColumn();
          final Object value;
          if (constructorMapping.getNestedQueryId() != null) {
            value = getNestedQueryConstructorValue(rsw.getResultSet(), constructorMapping, columnPrefix);
          } else if (constructorMapping.getNestedResultMapId() != null) {
            final ResultMap resultMap = configuration.getResultMap(constructorMapping.getNestedResultMapId());
            value = getRowValue(rsw, resultMap);
          } else {
            final TypeHandler<?> typeHandler = constructorMapping.getTypeHandler();
            value = typeHandler.getResult(rsw.getResultSet(), prependPrefix(column, columnPrefix));
          }
          constructorArgTypes.add(parameterType);
          constructorArgs.add(value);
          foundValues = value != null || foundValues;
        }
        return foundValues ? objectFactory.create(resultType, constructorArgTypes, constructorArgs) : null;
      }
    
      private Object createPrimitiveResultObject(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
        final Class<?> resultType = resultMap.getType();
        final String columnName;
        if (resultMap.getResultMappings().size() > 0) {
          final List<ResultMapping> resultMappingList = resultMap.getResultMappings();
          final ResultMapping mapping = resultMappingList.get(0);
          columnName = prependPrefix(mapping.getColumn(), columnPrefix);
        } else {
          columnName = rsw.getColumnNames().get(0);
        }
        final TypeHandler<?> typeHandler = rsw.getTypeHandler(resultType, columnName);
        return typeHandler.getResult(rsw.getResultSet(), columnName);
      }
    
      //
      // NESTED QUERY
      //
    
      private Object getNestedQueryConstructorValue(ResultSet rs, ResultMapping constructorMapping, String columnPrefix) throws SQLException {
        final String nestedQueryId = constructorMapping.getNestedQueryId();
        final MappedStatement nestedQuery = configuration.getMappedStatement(nestedQueryId);
        final Class<?> nestedQueryParameterType = nestedQuery.getParameterMap().getType();
        final Object nestedQueryParameterObject = prepareParameterForNestedQuery(rs, constructorMapping, nestedQueryParameterType, columnPrefix);
        Object value = null;
        if (nestedQueryParameterObject != null) {
          final BoundSql nestedBoundSql = nestedQuery.getBoundSql(nestedQueryParameterObject);
          final CacheKey key = executor.createCacheKey(nestedQuery, nestedQueryParameterObject, RowBounds.DEFAULT, nestedBoundSql);
          final Class<?> targetType = constructorMapping.getJavaType();
          final ResultLoader resultLoader = new ResultLoader(configuration, executor, nestedQuery, nestedQueryParameterObject, targetType, key, nestedBoundSql);
          value = resultLoader.loadResult();
        }
        return value;
      }
    
      private Object getNestedQueryMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)
          throws SQLException {
        final String nestedQueryId = propertyMapping.getNestedQueryId();
        final String property = propertyMapping.getProperty();
        final MappedStatement nestedQuery = configuration.getMappedStatement(nestedQueryId);
        final Class<?> nestedQueryParameterType = nestedQuery.getParameterMap().getType();
        final Object nestedQueryParameterObject = prepareParameterForNestedQuery(rs, propertyMapping, nestedQueryParameterType, columnPrefix);
        Object value = NO_VALUE;
        if (nestedQueryParameterObject != null) {
          final BoundSql nestedBoundSql = nestedQuery.getBoundSql(nestedQueryParameterObject);
          final CacheKey key = executor.createCacheKey(nestedQuery, nestedQueryParameterObject, RowBounds.DEFAULT, nestedBoundSql);
          final Class<?> targetType = propertyMapping.getJavaType();
          if (executor.isCached(nestedQuery, key)) {
            executor.deferLoad(nestedQuery, metaResultObject, property, key, targetType);
          } else {
            final ResultLoader resultLoader = new ResultLoader(configuration, executor, nestedQuery, nestedQueryParameterObject, targetType, key, nestedBoundSql);
            if (propertyMapping.isLazy()) {
              lazyLoader.addLoader(property, metaResultObject, resultLoader);
            } else {
              value = resultLoader.loadResult();
            }
          }
        }
        return value;
      }
    
      private Object prepareParameterForNestedQuery(ResultSet rs, ResultMapping resultMapping, Class<?> parameterType, String columnPrefix) throws SQLException {
        if (resultMapping.isCompositeResult()) {
          return prepareCompositeKeyParameter(rs, resultMapping, parameterType, columnPrefix);
        } else {
          return prepareSimpleKeyParameter(rs, resultMapping, parameterType, columnPrefix);
        }
      }
    
      private Object prepareSimpleKeyParameter(ResultSet rs, ResultMapping resultMapping, Class<?> parameterType, String columnPrefix) throws SQLException {
        final TypeHandler<?> typeHandler;
        if (typeHandlerRegistry.hasTypeHandler(parameterType)) {
          typeHandler = typeHandlerRegistry.getTypeHandler(parameterType);
        } else {
          typeHandler = typeHandlerRegistry.getUnknownTypeHandler();
        }
        return typeHandler.getResult(rs, prependPrefix(resultMapping.getColumn(), columnPrefix));
      }
    
      private Object prepareCompositeKeyParameter(ResultSet rs, ResultMapping resultMapping, Class<?> parameterType, String columnPrefix) throws SQLException {
        final Object parameterObject = instantiateParameterObject(parameterType);
        final MetaObject metaObject = configuration.newMetaObject(parameterObject);
        boolean foundValues = false;
        for (ResultMapping innerResultMapping : resultMapping.getComposites()) {
          final Class<?> propType = metaObject.getSetterType(innerResultMapping.getProperty());
          final TypeHandler<?> typeHandler = typeHandlerRegistry.getTypeHandler(propType);
          final Object propValue = typeHandler.getResult(rs, prependPrefix(innerResultMapping.getColumn(), columnPrefix));
          if (propValue != null) { // issue #353 & #560 do not execute nested query if key is null
            metaObject.setValue(innerResultMapping.getProperty(), propValue);
            foundValues = true;
          }
        }
        return foundValues ? parameterObject : null;
      }
    
      private Object instantiateParameterObject(Class<?> parameterType) {
        if (parameterType == null) {
          return new HashMap<Object, Object>();
        } else {
          return objectFactory.create(parameterType);
        }
      }
    
      //
      // DISCRIMINATOR
      //
    
      public ResultMap resolveDiscriminatedResultMap(ResultSet rs, ResultMap resultMap, String columnPrefix) throws SQLException {
        Set<String> pastDiscriminators = new HashSet<String>();
        Discriminator discriminator = resultMap.getDiscriminator();
        while (discriminator != null) {
          final Object value = getDiscriminatorValue(rs, discriminator, columnPrefix);
          final String discriminatedMapId = discriminator.getMapIdFor(String.valueOf(value));
          if (configuration.hasResultMap(discriminatedMapId)) {
            resultMap = configuration.getResultMap(discriminatedMapId);
            Discriminator lastDiscriminator = discriminator;
            discriminator = resultMap.getDiscriminator();
            if (discriminator == lastDiscriminator || !pastDiscriminators.add(discriminatedMapId)) {
              break;
            }
          } else {
            break;
          }
        }
        return resultMap;
      }
    
      private Object getDiscriminatorValue(ResultSet rs, Discriminator discriminator, String columnPrefix) throws SQLException {
        final ResultMapping resultMapping = discriminator.getResultMapping();
        final TypeHandler<?> typeHandler = resultMapping.getTypeHandler();
        return typeHandler.getResult(rs, prependPrefix(resultMapping.getColumn(), columnPrefix));
      }
    
      private String prependPrefix(String columnName, String prefix) {
        if (columnName == null || columnName.length() == 0 || prefix == null || prefix.length() == 0) {
          return columnName;
        }
        return prefix + columnName;
      }
    
      //
      // HANDLE NESTED RESULT MAPS
      //
    
      private void handleRowValuesForNestedResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
        final DefaultResultContext resultContext = new DefaultResultContext();
        skipRows(rsw.getResultSet(), rowBounds);
        Object rowValue = null;
        while (shouldProcessMoreRows(rsw.getResultSet(), resultContext, rowBounds)) {
          final ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
          final CacheKey rowKey = createRowKey(discriminatedResultMap, rsw, null);
          Object partialObject = nestedResultObjects.get(rowKey);
          if (mappedStatement.isResultOrdered()) { // issue #577 && #542
            if (partialObject == null && rowValue != null) {
              nestedResultObjects.clear();
              storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
            }
            rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, rowKey, null, partialObject);
          } else {
            rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, rowKey, null, partialObject);
            if (partialObject == null) {
              storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
            }
          }
        }
        if (rowValue != null && mappedStatement.isResultOrdered()) {
          storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
        }
      }
      
      //
      // GET VALUE FROM ROW FOR NESTED RESULT MAP
      //
    
      private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, CacheKey combinedKey, CacheKey absoluteKey, String columnPrefix, Object partialObject) throws SQLException {
        final String resultMapId = resultMap.getId();
        Object resultObject = partialObject;
        if (resultObject != null) {
          final MetaObject metaObject = configuration.newMetaObject(resultObject);
          putAncestor(absoluteKey, resultObject, resultMapId, columnPrefix);
          applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, false);
          ancestorObjects.remove(absoluteKey);
        } else {
          final ResultLoaderMap lazyLoader = new ResultLoaderMap();
          resultObject = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
          if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) {
            final MetaObject metaObject = configuration.newMetaObject(resultObject);
            boolean foundValues = resultMap.getConstructorResultMappings().size() > 0;
            if (shouldApplyAutomaticMappings(resultMap, AutoMappingBehavior.FULL.equals(configuration.getAutoMappingBehavior()))) {
              foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
            }        
            foundValues = applyPropertyMappings(resultObject,rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
            putAncestor(absoluteKey, resultObject, resultMapId, columnPrefix);
            foundValues = applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, true) || foundValues;
            ancestorObjects.remove(absoluteKey);
            foundValues = lazyLoader.size() > 0 || foundValues;
            resultObject = foundValues ? resultObject : null;
          }
          if (combinedKey != CacheKey.NULL_CACHE_KEY) nestedResultObjects.put(combinedKey, resultObject);
        }
        return resultObject;
      }
    
      private void putAncestor(CacheKey rowKey, Object resultObject, String resultMapId, String columnPrefix) {
        if (!ancestorColumnPrefix.containsKey(resultMapId)) {
          ancestorColumnPrefix.put(resultMapId, columnPrefix);
        }
        ancestorObjects.put(rowKey, resultObject);
      }
    
      //
      // NESTED RESULT MAP (JOIN MAPPING)
      //
    
      private boolean applyNestedResultMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String parentPrefix, CacheKey parentRowKey, boolean newObject) {
        boolean foundValues = false;
        for (ResultMapping resultMapping : resultMap.getPropertyResultMappings()) {
          final String nestedResultMapId = resultMapping.getNestedResultMapId();
          if (nestedResultMapId != null && resultMapping.getResultSet() == null) {
            try {
              final String columnPrefix = getColumnPrefix(parentPrefix, resultMapping);
              final ResultMap nestedResultMap = getNestedResultMap(rsw.getResultSet(), nestedResultMapId, columnPrefix);
              CacheKey rowKey = null;
              Object ancestorObject = null;
              if (ancestorColumnPrefix.containsKey(nestedResultMapId)) {
                rowKey = createRowKey(nestedResultMap, rsw, ancestorColumnPrefix.get(nestedResultMapId));
                ancestorObject = ancestorObjects.get(rowKey);
              }
              if (ancestorObject != null) { 
                if (newObject) metaObject.setValue(resultMapping.getProperty(), ancestorObject);
              } else {
                rowKey = createRowKey(nestedResultMap, rsw, columnPrefix);
                final CacheKey combinedKey = combineKeys(rowKey, parentRowKey);            
                Object rowValue = nestedResultObjects.get(combinedKey);
                boolean knownValue = (rowValue != null);
                final Object collectionProperty = instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject);            
                if (anyNotNullColumnHasValue(resultMapping, columnPrefix, rsw.getResultSet())) {
                  rowValue = getRowValue(rsw, nestedResultMap, combinedKey, rowKey, columnPrefix, rowValue);
                  if (rowValue != null && !knownValue) {
                    if (collectionProperty != null) {
                      final MetaObject targetMetaObject = configuration.newMetaObject(collectionProperty);
                      targetMetaObject.add(rowValue);
                    } else {
                      metaObject.setValue(resultMapping.getProperty(), rowValue);
                    }
                    foundValues = true;
                  }
                }
              }
            } catch (SQLException e) {
              throw new ExecutorException("Error getting nested result map values for '" + resultMapping.getProperty() + "'.  Cause: " + e, e);
            }
          }
        }
        return foundValues;
      }
    
      private String getColumnPrefix(String parentPrefix, ResultMapping resultMapping) {
        final StringBuilder columnPrefixBuilder = new StringBuilder();
        if (parentPrefix != null) columnPrefixBuilder.append(parentPrefix);
        if (resultMapping.getColumnPrefix() != null) columnPrefixBuilder.append(resultMapping.getColumnPrefix());
        final String columnPrefix = columnPrefixBuilder.length() == 0 ? null : columnPrefixBuilder.toString().toUpperCase(Locale.ENGLISH);
        return columnPrefix;
      }
    
      private boolean anyNotNullColumnHasValue(ResultMapping resultMapping, String columnPrefix, ResultSet rs) throws SQLException {
        Set<String> notNullColumns = resultMapping.getNotNullColumns();
        boolean anyNotNullColumnHasValue = true;
        if (notNullColumns != null && !notNullColumns.isEmpty()) {
          anyNotNullColumnHasValue = false;
          for (String column: notNullColumns) {
            rs.getObject(prependPrefix(column, columnPrefix));
            if (!rs.wasNull()) {
              anyNotNullColumnHasValue = true;
              break;
            }
          }
        }
        return anyNotNullColumnHasValue;
      }
    
      private ResultMap getNestedResultMap(ResultSet rs, String nestedResultMapId, String columnPrefix) throws SQLException {
        ResultMap nestedResultMap = configuration.getResultMap(nestedResultMapId);
        nestedResultMap = resolveDiscriminatedResultMap(rs, nestedResultMap, columnPrefix);
        return nestedResultMap;
      }
    
      //
      // UNIQUE RESULT KEY
      //
    
      private CacheKey createRowKey(ResultMap resultMap, ResultSetWrapper rsw, String columnPrefix) throws SQLException {
        final CacheKey cacheKey = new CacheKey();
        cacheKey.update(resultMap.getId());
        List<ResultMapping> resultMappings = getResultMappingsForRowKey(resultMap);
        if (resultMappings.size() == 0) {
          if (Map.class.isAssignableFrom(resultMap.getType())) {
            createRowKeyForMap(rsw, cacheKey);
          } else {
            createRowKeyForUnmappedProperties(resultMap, rsw, cacheKey, columnPrefix);
          }
        } else {
          createRowKeyForMappedProperties(resultMap, rsw, cacheKey, resultMappings, columnPrefix);
        }
        return cacheKey;
      }
    
      private CacheKey combineKeys(CacheKey rowKey, CacheKey parentRowKey) {
        if (rowKey.getUpdateCount() > 1 && parentRowKey.getUpdateCount() > 1) {
          CacheKey combinedKey;
          try {
            combinedKey = rowKey.clone();
          } catch (CloneNotSupportedException e) {
            throw new ExecutorException("Error cloning cache key.  Cause: " + e, e);
          }
          combinedKey.update(parentRowKey);
          return combinedKey;
        }
        return CacheKey.NULL_CACHE_KEY;
      }
    
      private List<ResultMapping> getResultMappingsForRowKey(ResultMap resultMap) {
        List<ResultMapping> resultMappings = resultMap.getIdResultMappings();
        if (resultMappings.size() == 0) {
          resultMappings = resultMap.getPropertyResultMappings();
        }
        return resultMappings;
      }
    
      private void createRowKeyForMappedProperties(ResultMap resultMap, ResultSetWrapper rsw, CacheKey cacheKey, List<ResultMapping> resultMappings, String columnPrefix) throws SQLException {
        for (ResultMapping resultMapping : resultMappings) {
          if (resultMapping.getNestedResultMapId() != null && resultMapping.getResultSet() == null) { // Issue #392
            final ResultMap nestedResultMap = configuration.getResultMap(resultMapping.getNestedResultMapId());
            createRowKeyForMappedProperties(nestedResultMap, rsw, cacheKey, nestedResultMap.getConstructorResultMappings(),
                prependPrefix(resultMapping.getColumnPrefix(), columnPrefix));
          } else if (resultMapping.getNestedQueryId() == null) {
            final String column = prependPrefix(resultMapping.getColumn(), columnPrefix);
            final TypeHandler<?> th = resultMapping.getTypeHandler();
            List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
            if (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH))) { // Issue #114
              final Object value = th.getResult(rsw.getResultSet(), column);
              if (value != null) {
                cacheKey.update(column);
                cacheKey.update(value);
              }
            }
          }
        }
      }
    
      private void createRowKeyForUnmappedProperties(ResultMap resultMap, ResultSetWrapper rsw, CacheKey cacheKey, String columnPrefix) throws SQLException {
        final MetaClass metaType = MetaClass.forClass(resultMap.getType());
        List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
        for (String column : unmappedColumnNames) {
          String property = column;
          if (columnPrefix != null && columnPrefix.length() > 0) {
            // When columnPrefix is specified,
            // ignore columns without the prefix.
            if (column.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {
              property = column.substring(columnPrefix.length());
            } else {
              continue;
            }
          }
          if (metaType.findProperty(property, configuration.isMapUnderscoreToCamelCase()) != null) {
            String value = rsw.getResultSet().getString(column);
            if (value != null) {
              cacheKey.update(column);
              cacheKey.update(value);
            }
          }
        }
      }
    
      private void createRowKeyForMap(ResultSetWrapper rsw, CacheKey cacheKey) throws SQLException {
        List<String> columnNames = rsw.getColumnNames();
        for (String columnName : columnNames) {
          final String value = rsw.getResultSet().getString(columnName);
          if (value != null) {
            cacheKey.update(columnName);
            cacheKey.update(value);
          }
        }
      }
    
    }
    

    

    

  • 相关阅读:
    Linux进程实践(5) --守护进程
    Linux进程实践(4) --wait避免僵尸进程
    Linux进程实践(3) --进程终止与exec函数族
    Linux进程实践(2) --僵尸进程与文件共享
    ORACLE清除某一字段重复的数据(选取重复数据中另一个字段时期最大值)
    ORACLE查询某一字段重复的数据
    mysql字符集设置
    项目中填写全局常量
    mybatis的insert简单使用
    window.open()读取本地图片简单使用总结
  • 原文地址:https://www.cnblogs.com/jerry1999/p/3740149.html
Copyright © 2011-2022 走看看