Ibatis源码学习(二):延迟加载的实现
Ibatis获取查询的结果是从ResultMap.java类里面的getResults获取
?
/** * Read a row from a resultset and map results to an array. * * @param statementScope scope of the request * @param rs ResultSet to read from * * @return row read as an array of column values. * * @throws java.sql.SQLException */ public Object[] getResults(StatementScope statementScope, ResultSet rs) throws SQLException { ... for (int i = 0; i < getResultMappings().length; i++) { ... columnValues[i] = getNestedSelectMappingValue(statementScope, rs, mapping, javaType); ... }}
?getNestedSelectMappingValue方法的调用如下:
?
protected Object getNestedSelectMappingValue(StatementScope statementScope, ResultSet rs, ResultMapping mapping, Class targetType) throws SQLException { try { ... result = ResultLoader.loadResult(client, statementName, parameterObject, targetType); ... return result; } catch (InstantiationException e) { throw new NestedSQLException("Error setting nested bean property. Cause: " + e, e); } catch (IllegalAccessException e) { throw new NestedSQLException("Error setting nested bean property. Cause: " + e, e); } }
?
?这时调用的是ResultLoader.loadResult方法:
?
/** * Loads a result lazily * * @param client - the client creating the object * @param statementName - the name of the statement to be used * @param parameterObject - the parameters for the statement * @param targetType - the target type of the result * @return the loaded result * @throws SQLException */ public static Object loadResult(SqlMapClientImpl client, String statementName, Object parameterObject, Class targetType) throws SQLException { Object value = null; if (client.isLazyLoadingEnabled()) { if (client.isEnhancementEnabled()) { EnhancedLazyResultLoader lazy = new EnhancedLazyResultLoader(client, statementName, parameterObject, targetType); value = lazy.loadResult(); } else { LazyResultLoader lazy = new LazyResultLoader(client, statementName, parameterObject, targetType); value = lazy.loadResult(); } } else { value = getResult(client, statementName, parameterObject, targetType); } return value; }
?
?注意下面的调用:
(调用CGLIB的方式代理这里暂不分析,先分析利用java反射API进行的动态代理方式)
?
LazyResultLoader lazy = new LazyResultLoader(client, statementName, parameterObject, targetType);
? ? ? ? value = lazy.loadResult();
LazyResultLoader?是实现InvocationHandler的代理动态类,里面的方法如下:
?
/** * Loads the result * * @return the results - a list or object * * @throws SQLException if there is a problem */ public Object loadResult() throws SQLException { if (Collection.class.isAssignableFrom(targetType)) { InvocationHandler handler = new LazyResultLoader(client, statementName, parameterObject, targetType); ClassLoader cl = targetType.getClassLoader(); if (Set.class.isAssignableFrom(targetType)) { return Proxy.newProxyInstance(cl, SET_INTERFACES, handler); } else { return Proxy.newProxyInstance(cl, LIST_INTERFACES, handler); } } else { return ResultLoader.getResult(client, statementName, parameterObject, targetType); } }?
方法里面对所有的collection作为父接口的类都采用了创建代理对象的方式进行了延迟加载,对所有set的采用的set的方式,所有非set的采用list的方式(也即是对所有的子查询进行了延迟加载)。所有的非collectin子类都采用非延迟加载的方式加载。
?
?
这里先创建一个代理对象,待真正用到对象的数据时,将执行动态代理的invoke方法,然后在invoke方法里面才真正的加载数据。
public Object invoke(Object o, Method method, Object[] objects) throws Throwable { if ("finalize".hashCode() == method.getName().hashCode() && "finalize".equals(method.getName())) { 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); } } }?实际的加载数据是通过ResultLoader.getResult(client, statementName, parameterObject, targetType);里面的实际加载委托为SqlMapClientImpl的数据加载:
protected static Object getResult(SqlMapClientImpl client, String statementName, Object parameterObject, Class targetType) throws SQLException { Object value = null; if (DomCollectionTypeMarker.class.isAssignableFrom(targetType)) { value = client.queryForList(statementName, parameterObject); } else if (Set.class.isAssignableFrom(targetType)) { value = new HashSet(client.queryForList(statementName, parameterObject)); } else if (Collection.class.isAssignableFrom(targetType)) { value = client.queryForList(statementName, parameterObject); } else if (targetType.isArray()) { List list = client.queryForList(statementName, parameterObject); value = listToArray(list, targetType.getComponentType()); } else { value = client.queryForObject(statementName, parameterObject); } return value; }?
?
?
备注:初稿,为写完,后续继续。