Mybatis工作流程源码分析
1.简介
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录《摘自mybatis官网》。
mybatis在我们开发中经常使用,所以搞清楚mybatis的工作流程还是比较重要的,下面就开始我们的分析。
2.Mybatis中的核心对象
2.1mybatis使用示例
public static void main(String[] args) throws IOException { //1.创建sqlSessionFactory对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml")); //2.创建会话 SqlSession session = sqlSessionFactory.openSession(); try { //3.获取mapper代理对象 BlogMapper mapper = session.getMapper(BlogMapper.class); //4.执行mapper接口方法 Blog blog = mapper.selectBlogById(1); System.out.println(blog); } finally { session.close(); } }
2.2核心对象
通过上面的示例可以看出mybatis里面的几个核心对象:SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession和Mapper对象,
1.SqlSessionFactoryBuilder:会话工厂构建者,用来构建SqlSessionFactory,在应用中,SqlSessionFactory作为单例对象存在,所以,创建SqlSessionFactory后,SqlSessionFactoryBuilder的任务也就完成了。所以他的生命周期为方法局部。
2.SqlSessionFactory:会话工厂类,用来创建会话,有了工厂,我们就可以创建SqlSession,而创建SqlSession只需要一个工厂就足够了,所以SqlSessionFactory为单例。 我们每次访问数据库都需要创建会话,这个过程贯穿应用的整个生命周期,所以SqlSessionFactory的生命周期为应用级别。
3.SqlSession:会话,内部持有与数据库的连接(connection),线程不安全,每次使用后需要及时关闭。生命周期为一次请求或一次事务。
4.Mapper:mapper对象实际是一个代理对象,从SqlSession中获取。BlogMapper mapper = session.getMapper(BlogMapper.class); 作用是发送sql语句操作数据,生命周期为SqlSession事务方法内。
3.工作流程
3.1 创建sqlSessionFactory对象
public class SqlSessionFactoryBuilder { public SqlSessionFactory build(InputStream inputStream) { return build(inputStream, null, null); } public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { //创建xml配置构建器 XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); //解析配置文件返回configuration对象,使用configuration对象创建会话工厂 return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { inputStream.close(); } catch (IOException e) { } } } public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); } }
调用了XMLConfigBuilder 的parse()方法
public class XMLConfigBuilder extends BaseBuilder { //... public Configuration parse() { if (parsed) { //配置文件只能解析一次 throw new BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; parseConfiguration(parser.evalNode("/configuration")); return configuration; } private void parseConfiguration(XNode root) { try { //解析properties标签,读取外部引入的配置文件,包括相对路径和绝对路径, //将解析结果defaults,最后将defaults设置到XPathParser和Configuration的properties属性 propertiesElement(root.evalNode("properties")); //解析别名,解析完成后将别名与class的映射保存到Configuration的typeAliasRegistry中 typeAliasesElement(root.evalNode("typeAliases")); //解析<plugins>标签,比如常用的分页插件,解析为Interceptor,设置到Configuration的 //interceptorChain(持有一个拦截器List)属性中 pluginElement(root.evalNode("plugins")); //解析<objectFactory> 和<objectWrapperFactory>标签,分别生成objectFactory和 //objectWrapperFactory,同样设置到Configuration的属性中,用来实例化对象使用。 objectFactoryElement(root.evalNode("objectFactory")); objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); reflectionFactoryElement(root.evalNode("reflectionFactory")); // settingsElement(root.evalNode("settings")); // read it after objectFactory and objectWrapperFactory issue #631 environmentsElement(root.evalNode("environments")); //解析 databaseIdProvider 标签,生成 DatabaseIdProvider对象,用来支持不同厂商的数据库 databaseIdProviderElement(root.evalNode("databaseIdProvider")); //解析类型处理器元素,用来保存JavaType和JdbcType的映射关系,设置到Configuration的typeHandlerRegistry typeHandlerElement(root.evalNode("typeHandlers")); //解析<mappers>标签,只有是接口才会解析,然后判断是否已经注册,单个Mapper重复注册会抛出异常 //将解析的mapper保存到Configuration的mapperRegistry中,并解析注解信息 mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } } //... }
通过parser.parse()拿到解析配置文件得到的Configuration对象,调用build()方法,创建默认的SqlSessionFactory对象并返回。
下面来看一下上面代码的运行时序图:
3.2 创建会话
调用DefaultSqlSessionFactory 的openSession()方法获取会话
public class DefaultSqlSessionFactory implements SqlSessionFactory { @Override public SqlSession openSession() { return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false); } private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { final Environment environment = configuration.getEnvironment(); //获取事务工厂 final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); //创建执行器,默认使用SimpleExecutor final Executor executor = configuration.newExecutor(tx, execType); //创建会话 return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { closeTransaction(tx); // may have fetched a connection so lets call close() throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } }
下面来看一下创建会话的运行时序图:
3.3获取mapper代理对象
调用session.getMapper方法获取mapper代理对象
public class DefaultSqlSession implements SqlSession { //... @Override public <T> T getMapper(Class<T> type) { return configuration.<T>getMapper(type, this); } //... }
继续调用Configuration的getMapper方法
public class Configuration { //... //Mapper注册器,所有的mapper在解析配置文件时保存到该对象中 protected MapperRegistry mapperRegistry = new MapperRegistry(this); public <T> T getMapper(Class<T> type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); } //... }
继续调用mapperRegistry的getMapper方法
public class MapperRegistry { private final Configuration config; //以接口的class为key,mapper代理工厂为value的map,MapperProxyFactory在加载配置文件扫描mapper所在包时创建,用来创建MapperProxy private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>(); public MapperRegistry(Configuration config) { this.config = config; } @SuppressWarnings("unchecked") 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 { //调用工厂方法创建mapper接口的代理对象 return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new BindingException("Error getting mapper instance. Cause: " + e, e); } } //... }
调用mapper代理工厂的newInstance方法为mapper创建代理对象
public class MapperProxyFactory<T> { //mapper接口对应class private final Class<T> mapperInterface; //方法缓存,为了提升性能,会在MapperProxy中使用,所有mapper的所有方法都将缓存在该map中 private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>(); public MapperProxyFactory(Class<T> mapperInterface) { this.mapperInterface = mapperInterface; } public Class<T> getMapperInterface() { return mapperInterface; } public Map<Method, MapperMethod> getMethodCache() { return methodCache; } @SuppressWarnings("unchecked") protected T newInstance(MapperProxy<T> mapperProxy) { //创建jdk动态代理,第三个参数是mapperProxy,所以对T对象的所有的所有调用都将调用mapperProxy的invoke方法 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); } }
上面使用jdk动态代理为mapper创建了代理对象,所以对mapper的所有调用将调用mapperProxy类的invoke方法,下面来看一下mapperProxy的代码
public class MapperProxy<T> implements InvocationHandler, Serializable { private static final long serialVersionUID = -6424540398559729838L; private final SqlSession sqlSession; private final Class<T> mapperInterface; private final Map<Method, MapperMethod> methodCache; public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) { this.sqlSession = sqlSession; this.mapperInterface = mapperInterface; this.methodCache = methodCache; } //所有对mapper的调用都将调用该方法 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (Object.class.equals(method.getDeclaringClass())) { try { return method.invoke(this, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } //获取缓存的mapper方法,如果不存在,则创建并缓存 final MapperMethod mapperMethod = cachedMapperMethod(method); //不像常规的动态代理,并没有调用method.invoke(target, args),因为mapper只是一个接口,并没有实现类 //这里动态代理的意义在于统一处理对mapper方法的调用 return mapperMethod.execute(sqlSession, args); } private MapperMethod cachedMapperMethod(Method method) { MapperMethod mapperMethod = methodCache.get(method); if (mapperMethod == null) { mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()); methodCache.put(method, mapperMethod); } return mapperMethod; } }
到这里为什么mapper接口不需要实现类就可以操作数据库就很清楚了,对mapper接口的调用实际会调用mapperMethod.execute()方法,在mapperMethod内部调用sqlSession的增删改查方法。
下面来看一下获取mapper代理对象的运行时序图:
3.4执行mapper接口方法
上面已经说到对mapper接口的方法调用都将调用MapperProxy的invoke方法,invoke方法又会调用mapperMethod的execute()方法,下面来看一下execute()方法:
public class MapperMethod { private final SqlCommand command; //方法签名 private final MethodSignature method; public Object execute(SqlSession sqlSession, Object[] args) { Object result; //判断sql类型,执行相应逻辑 if (SqlCommandType.INSERT == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.insert(command.getName(), param)); } else if (SqlCommandType.UPDATE == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.update(command.getName(), param)); } else if (SqlCommandType.DELETE == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.delete(command.getName(), param)); } else if (SqlCommandType.SELECT == command.getType()) { if (method.returnsVoid() && method.hasResultHandler()) { executeWithResultHandler(sqlSession, args); result = null; } else if (method.returnsMany()) { result = executeForMany(sqlSession, args); } else if (method.returnsMap()) { result = executeForMap(sqlSession, args); } else { Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); } } else if (SqlCommandType.FLUSH == command.getType()) { result = sqlSession.flushStatements(); } else { throw new BindingException("Unknown execution method for: " + command.getName()); } 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; } private <E> Object executeForMany(SqlSession sqlSession, Object[] args) { List<E> result; //转换参数 Object param = method.convertArgsToSqlCommandParam(args); //方法是否RowBounds(分页)参数 if (method.hasRowBounds()) { RowBounds rowBounds = method.extractRowBounds(args); result = sqlSession.<E>selectList(command.getName(), param, rowBounds); } else { result = sqlSession.<E>selectList(command.getName(), param); } // issue #510 Collections & arrays support if (!method.getReturnType().isAssignableFrom(result.getClass())) { if (method.getReturnType().isArray()) { return convertToArray(result); } else { return convertToDeclaredCollection(sqlSession.getConfiguration(), result); } return result; } public static class SqlCommand { //mapperInterface.getName() + "." + method.getName(); private final String name; //sql类型:UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH; private final SqlCommandType type; //... } public static class MethodSignature{ private boolean returnsMany; private boolean returnsMap; private boolean returnsVoid; private Class<?> returnType; //方法是否有MapKey.class注解,如果有就是注解的值 private String mapKey; //ResultHandler.class类型参数在方法参数中的索引 private Integer resultHandlerIndex; //RowBounds.class类型参数在方法参数中的索引 private rowBoundsIndex; //key为index,有Param注解的value为注解值,没有的也是index private SortedMap<Integer, String> params; //方法参数中是否有Param注解 private boolean hasNamedParameters //... } }
在execute()方法内部根据sql的类型执行不同的增删改查逻辑,这里以select为例,mapper方法有多个返回值的情况调用executeForMany(sqlSession, args)方法,内部继续调用sqlSession的selectList()方法:
public class DefaultSqlSession implements SqlSession { @Override public <E> List<E> selectList(String statement, Object parameter) { return this.selectList(statement, parameter, RowBounds.DEFAULT); } @Override public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { try { //获取configuration中缓存的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(); } } }
拿到MappedStatement,调用执行器进行查询:
public abstract class BaseExecutor implements Executor { @Override public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { BoundSql boundSql = ms.getBoundSql(parameter); CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql); return query(ms, parameter, rowBounds, resultHandler, key, boundSql); } @SuppressWarnings("unchecked") @Override 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; localCache.putObject(key, EXECUTION_PLACEHOLDER); try { //开始执行查询 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; } }
query()方法调用重载的query()方法,如果没有命中缓存,则调用queryFromDatabase()从数据库查询数据,继续调用SimpleExecutor的doQuery()方法:
public class SimpleExecutor extends BaseExecutor { @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(); //创建statement处理器,默认PreparedStatement StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); stmt = prepareStatement(handler, ms.getStatementLog()); return handler.<E>query(stmt, resultHandler); } finally { closeStatement(stmt); } } private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; Connection connection = getConnection(statementLog); stmt = handler.prepare(connection); handler.parameterize(stmt); return stmt; } }
创建Statement处理器:
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { //创建路由Statement处理器,根据statementType创建不同的statement处理器,委派模式的体现 StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); //创建statement处理器拦截器链,这里先不深入看,默认没有使用拦截器,后面的文章单独分析 statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); return statementHandler; }
在创建Statement处理器时返回的是RoutingStatementHandler对象,从名字就可以看出来,他不是真正工作的StatementHandler,其路由的功能,在内部会根据StatementType创建相应的StatementHandler,对RoutingStatementHandler 的所有调用都将委派给具体的StatementHandler(delegate)去处理:
public class RoutingStatementHandler implements StatementHandler { private final StatementHandler delegate; public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { //根据statementType创建不同的策略,委派给具体的statement处理器(策略模式与委派模式的体现) switch (ms.getStatementType()) { case STATEMENT: delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; case PREPARED: //默认使用PreparedStatementHandler 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()); } } @Override public Statement prepare(Connection connection) throws SQLException { return delegate.prepare(connection); } @Override public void parameterize(Statement statement) throws SQLException { delegate.parameterize(statement); } @Override public void batch(Statement statement) throws SQLException { delegate.batch(statement); } @Override public int update(Statement statement) throws SQLException { return delegate.update(statement); } @Override public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { return delegate.<E>query(statement, resultHandler); } @Override public BoundSql getBoundSql() { return delegate.getBoundSql(); } @Override public ParameterHandler getParameterHandler() { return delegate.getParameterHandler(); } }
回到doQuery()方法中,继续调用StatementHandler 的query(stmt, resultHandler)方法,query方法会调用到PreparedStatementHandler 的query()方法:
public class PreparedStatementHandler extends BaseStatementHandler { @Override public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; //执行PreparedStatement ps.execute(); //结果集处理器处理结果集并返回结果 return resultSetHandler.<E> handleResultSets(ps); } }
在PreparedStatementHandler 的query方法中执行PreparedStatement,并将PreparedStatement传给resultSetHandler处理结果集并返回。
上面的执行过程还是比较复杂的,下面来看一下时序图:
以上就是对mybatis工作流程的所有分析了。
因个人能力有限,如果有错误之处,还请指出,谢谢!