解析xml讲完之后,我们开始讲调用了。因为mybatis实际使用了动态代理(阉割版),不过这里的动态代理没有实现类。有关这块设计模式可以找一下看看。
public class Test { public static void main(String[] args) throws IOException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //获取代理 PageArticlesMapper pageArticles = sqlSessionFactory.openSession().getMapper(PageArticlesMapper.class); //查询 pageArticles.selectBlog(101); } }
大家在这里注意到了proxy代理了吧,因为在之前解析xml的时候已经对每个xxMapper接口都包装了一个MapperProxyFactory,当从sqlSessionFactory.openSession().getMapper(PageArticlesMapper.class);获取对应的xxMapper时,会进行代理实例化。来看一下
DefaultSqlSession#getMapper
public <T> T getMapper(Class<T> type) { return configuration.<T>getMapper(type, this); }
MapperRegistry#getMapper
//在这里获取对应xxxMapper的代理对象 public <T> T getMapper(Class<T> type, SqlSession sqlSession) { //获取的类包装了一层代理工厂 final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type); if (mapperProxyFactory == null) { throw new BindingException("Type " + type + " is not known to the MapperRegistry."); } try { //实例化 return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new BindingException("Error getting mapper instance. Cause: " + e, e); } }
正好找到了之前对bean封装的MapperProxyFactory类
public <T> void addMapper(Class<T> type) { if (type.isInterface()) { //如果已经存在则报错 interface com.telecom.BlogMapper if (hasMapper(type)) { throw new BindingException("Type " + type + " is already known to the MapperRegistry."); } boolean loadCompleted = false; try { //放入map中,并对类型包装一个代理工厂 knownMappers.put(type, new MapperProxyFactory(type)); // It's important that the type is added before the parser is run // otherwise the binding may automatically be attempted by the // mapper parser. If the type is already known, it won't try. MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } }
继续MapperProxyFactory#newInstance
protected T newInstance(MapperProxy<T> mapperProxy) { //通过代理进行实例化 return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } public T newInstance(SqlSession sqlSession) { //包装代理 final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); }
然后进入 java.lang.reflect.Proxy#newProxyInstance,大家可以滤过下面这个代码
public static Object newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h); final Class>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * Look up or generate the designated proxy class. */ Class> cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } final Constructor> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction() { public Void run() { cons.setAccessible(true); return null; } }); } return cons.newInstance(new Object[]{h}); } catch (IllegalAccessException|InstantiationException e) { throw new InternalError(e.toString(), e); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString(), t); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } }
继续到Test.java中
MapperProxy实现了InvocationHandler,那当调用这个接口的时候就会触发invoke方法。
MapperProxy#invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { if (Object.class.equals(method.getDeclaringClass())) { //如果是从Object继承的方法,直接执行 return method.invoke(this, args); //判断是否是默认方法,默认方法是Java8的新特性,判断逻辑与Java8 Method类的isDefault方法一样 } else if (isDefaultMethod(method)) { //调用默认方法,这里面用到了Java7的API,有兴趣的读者可以自行了解 return invokeDefaultMethod(proxy, method, args); } } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } /*构建并缓存MapperMethod*/ final MapperMethod mapperMethod = cachedMapperMethod(method); /*执行*/ return mapperMethod.execute(sqlSession, args); }
private MapperMethod cachedMapperMethod(Method method) { //从缓存中获取信息 public abstract com.telecom.Blog com.telecom.BlogMapper.selectBlog(int) MapperMethod mapperMethod = methodCache.get(method); if (mapperMethod == null) { //加入缓存 mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()); methodCache.put(method, mapperMethod); } return mapperMethod; }
MapperMethod#execute
//根据type类型执行 public Object execute(SqlSession sqlSession, Object[] args) { Object result; switch (command.getType()) { case INSERT: { /*将参数转换为sql命令的参数*/ Object param = method.convertArgsToSqlCommandParam(args); /*插入,返回影响行数的结果*/ result = rowCountResult(sqlSession.insert(command.getName(), param)); break; } case UPDATE: { /*将参数转换为sql命令的参数*/ Object param = method.convertArgsToSqlCommandParam(args); /*更新,返回影响行数的结果*/ result = rowCountResult(sqlSession.update(command.getName(), param)); break; } case DELETE: { /*将参数转换为sql命令的参数*/ Object param = method.convertArgsToSqlCommandParam(args); /*删除,返回影响行数的结果*/ result = rowCountResult(sqlSession.delete(command.getName(), param)); break; } case SELECT: //下面几个分支判断的条件,我们在分析构造MapperMethod的过程时看到了这些波尔条件的赋值过程 if (method.returnsVoid() && method.hasResultHandler()) { /*返回void并且方法包含ResultHandler的查询的执行*/ executeWithResultHandler(sqlSession, args); result = null; } else if (method.returnsMany()) { /*多个返回值的查询的执行*/ result = executeForMany(sqlSession, args); } else if (method.returnsMap()) { /*@MapKey注解的Map类型的返回值的查询的执行*/ result = executeForMap(sqlSession, args); } else if (method.returnsCursor()) { /*Cursor游标类型的返回值的查询的执行*/ result = executeForCursor(sqlSession, args); } else { /*将参数转换为sql命令的参数*/ Object param = method.convertArgsToSqlCommandParam(args); /*单个返回值的查询的执行*/ result = sqlSession.selectOne(command.getName(), param); } break; case FLUSH: result = sqlSession.flushStatements(); break; default: throw new BindingException("Unknown execution method for: " + command.getName()); } //命令执行结果为null,并且方法返回值是基本类型抛出异常 if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) { throw new BindingException("Mapper method '" + command.getName() + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ")."); } return result; }
到这里,大家看到了增删改查的判断了吧。测试方法走select.
public Object getNamedParams(Object[] args) { final int paramCount = names.size(); if (args == null || paramCount == 0) { return null; //解析的参数名称为空,返回null } else if (!hasParamAnnotation && paramCount == 1) { //没有注解@Param的参数并且解析的参数名称只有一个,根据下标返回参数 return args[names.firstKey()]; } else { final Map, Object> param = new ParamMap
通过selectOne一路追到DefaultSqlSession#selectList类中
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { try { //根据statementId获取MappedStatement MappedStatement ms = configuration.getMappedStatement(statement); return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
//对集合类型参数的包装,就是如果参数是集合类型,会根据参数类型的不同为参数添加不同的key private Object wrapCollection(final Object object) { if (object instanceof Collection) { StrictMap
这里的executor在默认配置的情况下的类型是CachingExecutor:
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { /*获取解析过动态标签的sql*/ BoundSql boundSql = ms.getBoundSql(parameterObject); //创建缓存key CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql); /*调用另一个重载的查询方法进行查询*/ return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }
public BoundSql getBoundSql(Object parameterObject) { /*获取解析过动态标签的sql*/ BoundSql boundSql = sqlSource.getBoundSql(parameterObject); ListparameterMappings = boundSql.getParameterMappings() ; if (parameterMappings == null || parameterMappings.isEmpty()) { boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject); } //检查参数映射中是否存在嵌套的resultMap并设置boolean标记 for (ParameterMapping pm : boundSql.getParameterMappings()) { String rmId = pm.getResultMapId(); if (rmId != null) { ResultMap rm = configuration.getResultMap(rmId); if (rm != null) { hasNestedResultMaps |= rm.hasNestedResultMaps(); } } } return boundSql; }
DynamicSqlSource#getBoundSql
public BoundSql getBoundSql(Object parameterObject) { DynamicContext context = new DynamicContext(configuration, parameterObject); //这里会根据动态标签的不同,如、 等,解析这些节点拼接到sql中,会涉及到OGNL表达式解析的内容,有兴趣的读者可以自行了解 rootSqlNode.apply(context); SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration); Class> parameterType = parameterObject == null ? Object.class : parameterObject.getClass(); //将#{}替换成?号占位符,并构建每个占位符对应参数属性的映射 SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings()); //将解析内容封装到BoundSql对象中返回 BoundSql boundSql = sqlSource.getBoundSql(parameterObject); for (Map.Entry , Object> entry : context.getBindings().entrySet()) { boundSql.setAdditionalParameter(entry.getKey(), entry.getValue()); } return boundSql; }
BaseExecutor#createCacheKey
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) { if (closed) { throw new ExecutorException("Executor was closed."); } //创建缓存 //621272139:1884823062:com.telecom.BlogMapper.selectBlog:0:2147483647:select * from Blog where id = ? CacheKey cacheKey = new CacheKey(); cacheKey.update(ms.getId()); cacheKey.update(rowBounds.getOffset()); cacheKey.update(rowBounds.getLimit()); cacheKey.update(boundSql.getSql()); ListparameterMappings = boundSql.getParameterMappings(); TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry(); // mimic DefaultParameterHandler logic for (ParameterMapping parameterMapping : parameterMappings) { if (parameterMapping.getMode() != ParameterMode.OUT) { Object value; //获取属性名称 String propertyName = parameterMapping.getProperty(); if (boundSql.hasAdditionalParameter(propertyName)) { value = boundSql.getAdditionalParameter(propertyName); } else if (parameterObject == null) { value = null; } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { value = parameterObject; } else { MetaObject metaObject = configuration.newMetaObject(parameterObject); value = metaObject.getValue(propertyName); } //1512233168:1884823163:com.telecom.BlogMapper.selectBlog:0:2147483647:select * from Blog where id = ?:101 cacheKey.update(value); } } if (configuration.getEnvironment() != null) { // 将环境的id更新进去 cacheKey.update(configuration.getEnvironment().getId()); } //-1230830222:1660009398:com.telecom.BlogMapper.selectBlog:0:2147483647:select * from Blog where id = ?:101:development return cacheKey; }
这里的cacheKey会在一级缓存(Map)代码中作为key。查询结果为value。
CachingExecutor#query
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { //获取缓存信息,不过第一次调用没有 Cache cache = ms.getCache(); //不为空,判断从缓存获取 if (cache != null) { flushCacheIfRequired(ms); if (ms.isUseCache() && resultHandler == null) { ensureNoOutParams(ms, boundSql); @SuppressWarnings("unchecked") List<E> list = (List<E>) tcm.getObject(cache, key); if (list == null) { list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); tcm.putObject(cache, key, list); // issue #578 and #116 } return list; } } //缓存为空,则从数据库查询 return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }
BaseExecutor#query,这里的key是从cacheKey创建获得的
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId()); if (closed) { throw new ExecutorException("Executor was closed."); } if (queryStack == 0 && ms.isFlushCacheRequired()) { clearLocalCache(); } List<E> list; try { //计数器 queryStack++; //从缓存拿去 list = resultHandler == null ? (List<E>) localCache.getObject(key) : null; if (list != null) { handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); } else { //从数据库读取 list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); } } finally { queryStack--; } if (queryStack == 0) { for (DeferredLoad deferredLoad : deferredLoads) { deferredLoad.load(); } // issue #601 deferredLoads.clear(); if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { // issue #482 clearLocalCache(); } } return list; }
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { List<E> list; //设置缓存,占位符。防止其他连接调用击穿db localCache.putObject(key, EXECUTION_PLACEHOLDER); try { //执行 org.apache.ibatis.executor.SimpleExecutor.doQuery /*子类实现具体的查询逻辑*/ list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); } finally { localCache.removeObject(key); } localCache.putObject(key, list); if (ms.getStatementType() == StatementType.CALLABLE) { localOutputParameterCache.putObject(key, parameter); } return list; }
SimpleExecutor#doQuery
/*子类实现具体的查询逻辑*/ @Override public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); //包装StatementHandler StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); /*准备Statement*/ stmt = prepareStatement(handler, ms.getStatementLog()); //查询 return handler.<E>query(stmt, resultHandler); } finally { closeStatement(stmt); } }
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { /*构造RoutingStatementHandler*/ StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); //拦截器执行 statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); return statementHandler; }
RoutingStatementHandler
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { //根据statementType配置选择不同的处理器,在标签解析时我们看到默认为PREPARED switch (ms.getStatementType()) { case STATEMENT: delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; case PREPARED: delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; case CALLABLE: delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; default: throw new ExecutorException("Unknown statement type: " + ms.getStatementType()); } }SimpleExecutor
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; //获取数据库连接 Connection connection = getConnection(statementLog); /*准备Statement*/ stmt = handler.prepare(connection, transaction.getTimeout()); /*为Statement设置参数*/ handler.parameterize(stmt); return stmt; }
BaseStatementHandler
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException { ErrorContext.instance().sql(boundSql.getSql()); Statement statement = null; try { /*实例化Statement*/ statement = instantiateStatement(connection); /*设置超时时间*/ setStatementTimeout(statement, transactionTimeout); /*设置FetchSize*/ setFetchSize(statement); return statement; } catch (SQLException e) { closeStatement(statement);//异常关闭Statement throw e; } catch (Exception e) { closeStatement(statement);//异常关闭Statement throw new ExecutorException("Error preparing statement. Cause: " + e, e); } }
instantiateStatement实例化Statement就是根据配置不同调用Connection的不同重载方法来创建PreparedStatement,具体每个重载的方法的作用,请参考Java API。
protected void setStatementTimeout(Statement stmt, Integer transactionTimeout) throws SQLException { Integer queryTimeout = null; //标签上配置的timeout优先级最高 if (mappedStatement.getTimeout() != null) { queryTimeout = mappedStatement.getTimeout(); } else if (configuration.getDefaultStatementTimeout() != null) { //标签没有配置则应用全局配置的timeout queryTimeout = configuration.getDefaultStatementTimeout(); } if (queryTimeout != null) { stmt.setQueryTimeout(queryTimeout); } /*应用事务超时时间*/ StatementUtil.applyTransactionTimeout(stmt, queryTimeout, transactionTimeout); }
public static void applyTransactionTimeout(Statement statement, Integer queryTimeout, Integer transactionTimeout) throws SQLException { if (transactionTimeout == null){ return; } Integer timeToLiveOfQuery = null; //没有配置查询超时时间则应用事务配置的超时时间 if (queryTimeout == null || queryTimeout == 0) { timeToLiveOfQuery = transactionTimeout; } else if (transactionTimeout < queryTimeout) { //如果事务配置的超时时间小于配置的查询超时时间,则应用事务配置的超时时间 timeToLiveOfQuery = transactionTimeout; } if (timeToLiveOfQuery != null) { statement.setQueryTimeout(timeToLiveOfQuery); } }
BaseStatementHandler#setFetchSize
protected void setFetchSize(Statement stmt) throws SQLException { //标签上配置的优先 Integer fetchSize = mappedStatement.getFetchSize(); if (fetchSize != null) { stmt.setFetchSize(fetchSize); return; } Integer defaultFetchSize = configuration.getDefaultFetchSize(); if (defaultFetchSize != null) { //标签上没有配置则使用全局配置 stmt.setFetchSize(defaultFetchSize); } }
回到SimpleExecutor#prepareStatement并继续跟踪到PreparedStatementHandler#parameterize,直到DefaultParameterHandler#setParameters
public void setParameters(PreparedStatement ps) { ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId()); ListparameterMappings = boundSql.getParameterMappings(); if (parameterMappings != null) { //遍历参数绑定映射列表 for (int i = 0; i < parameterMappings.size(); i++) { ParameterMapping parameterMapping = parameterMappings.get(i); if (parameterMapping.getMode() != ParameterMode.OUT) { Object value; //获取参数绑定的属性名称 String propertyName = parameterMapping.getProperty(); if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params value = boundSql.getAdditionalParameter(propertyName); } else if (parameterObject == null) { value = null; } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { value = parameterObject; } else { MetaObject metaObject = configuration.newMetaObject(parameterObject); //根据属性名称从参数对象中反射获取对应的值 value = metaObject.getValue(propertyName); } //获取类型处理器 TypeHandler typeHandler = parameterMapping.getTypeHandler(); //获取jdbcType类型 JdbcType jdbcType = parameterMapping.getJdbcType(); if (value == null && jdbcType == null) { jdbcType = configuration.getJdbcTypeForNull(); } try { /*为PreparedStatement设置值*/ typeHandler. setParameter(ps, i + 1, value, jdbcType); } catch (TypeException e) { throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e); } catch (SQLException e) { throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e); } } } } }
public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException { if (parameter == null) { if (jdbcType == null) { throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters."); } try { //设置空值 ps.setNull(i, jdbcType.TYPE_CODE); } catch (SQLException e) { throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " + "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " + "Cause: " + e, e); } } else { try { //子类实现具体的非空值设置 setNonNullParameter(ps, i, parameter, jdbcType); } catch (Exception e) { throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . " + "Try setting a different JdbcType for this parameter or a different configuration property. " + "Cause: " + e, e); } } }根据构建ParameterMapping时设置的TypeHandler来为PreparedStatement设置对应类型的值。
RoutingStatementHandler:
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { //org.apache.ibatis.executor.statement.PreparedStatementHandler.query return delegate.<E>query(statement, resultHandler); }
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; ps.execute(); //执行sql命令 返回com.mysql.jdbc.JDBC42PreparedStatement@1a677343: select * from Blog where id = 101 /*处理结果集*/ return resultSetHandler.<E> handleResultSets(ps); }
到这里就要进入mysql驱动程序对接数据库了。然后查询结果handleResultSets在这里不讲,有兴趣的可以查看阅读以下,否则整个章节太长,难以消化。
上一篇: Mybatis之Xml解析说明
下一篇: Spring-Mybatis 注解连接器 MapperScannerRegistrar