TABLE OF CONTENTS Mybatis包结构 Mybatis使用流程 一些关键逻辑和亮点 通过SqlSessionFactory工厂获取SqlSession 加载Mybatis配置文件 配置项的默认值 SqlSession接口 获取Mapper代理类 执行Mapper代理方法 SqlSession, SqlSessionFactory, SqlSessionFactoryBuilder的关系 SqlSession的使用 SqlSession的执行原理 Excutor 总结
Mybatis包结构
Mybatis使用流程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 @Slf4j public class MyBatisBootStrap { @Test public void test () { try { InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml" ); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder ().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); CategoryMapper categoryMapper = sqlSession.getMapper(CategoryMapper.class); Category category = categoryMapper.queryCategoryByCategoryId(1 ); log.info("category = " + JsonUtil.toJson(category)); sqlSession.commit(); sqlSession.close(); inputStream.close(); } catch (Exception e) { log.error(e.getMessage(), e); } } }
如果对于Mybatis启动不清晰,可以通过上面这段代码找到入口,阅读源码。
一些关键逻辑和亮点 通过SqlSessionFactory工厂获取SqlSession 工厂方法模式+建造者设计模式的应用
SqlSessionFactory通过工厂方法openSession创建SqlSession实例,具体的工厂类实现了工厂接口,负责创建特定类型的对象,使SqlSession的实例化延迟到SqlSessionFactory子类。客户端通过SqlSessionFactory接口和工厂子类来获取SqlSession,而不需要知道具体的对象创建过程。而工厂本身是一个复杂的对象,因此可以通过建造者模式SqlSessionFactoryBuilder来构建。
源码位置:org.apache.ibatis.session.SqlSessionFactory
两个子类:org.apache.ibatis.session.defaults.DefaultSqlSessionFactory
org.apache.ibatis.session.SqlSessionManager
建造者类SqlSessionFactoryBuilderorg.apache.ibatis.session.SqlSessionFactoryBuilder#build(java.io.InputStream, java.lang.String, java.util.Properties)
加载Mybatis配置文件 1 2 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder ().build(inputStream);
上面这段代码,XMLConfigBuilder会解析inputStream,根据得到Configuration默认实例化一个工厂子类DefaultSqlSessionFactory。
源码位置:org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 private void parseConfiguration (XNode root) { try { propertiesElement(root.evalNode("properties" )); Properties settings = settingsAsProperties(root.evalNode("settings" )); loadCustomVfs(settings); loadCustomLogImpl(settings); typeAliasesElement(root.evalNode("typeAliases" )); pluginElement(root.evalNode("plugins" )); objectFactoryElement(root.evalNode("objectFactory" )); objectWrapperFactoryElement(root.evalNode("objectWrapperFactory" )); reflectorFactoryElement(root.evalNode("reflectorFactory" )); settingsElement(settings); environmentsElement(root.evalNode("environments" )); databaseIdProviderElement(root.evalNode("databaseIdProvider" )); typeHandlerElement(root.evalNode("typeHandlers" )); mapperElement(root.evalNode("mappers" )); } catch (Exception e) { throw new BuilderException ("Error parsing SQL Mapper Configuration. Cause: " + e, e); } }
配置项的默认值 org.apache.ibatis.builder.xml.XMLConfigBuilder#settingsElement
SqlSession接口 sqlSession是操作数据库的高级接口,获取sqlSession有两种方式: 一种是从数据源中获取的,还有一种是从连接中获取。获取到的都是DefaultSqlSession对象,也就是sqlSession的默认实现。
SqlSession接口源码,下面是其主要方法,已过滤一些重载方法。org.apache.ibatis.session.SqlSession
statement:Mybatis语句ID,Mapper接口的方法名称
RowBounds:分页对象,主要拼接sql中的start,limit条件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public interface SqlSession extends Closeable { <T> T selectOne (String statement, Object parameter) ; <E> List<E> selectList (String statement, Object parameter, RowBounds rowBounds) ; <K, V> Map<K, V> selectMap (String statement, Object parameter, String mapKey, RowBounds rowBounds) ; <T> Cursor<T> selectCursor (String statement, Object parameter, RowBounds rowBounds) ; void select (String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) ; int insert (String statement, Object parameter) ; int update (String statement, Object parameter) ; int delete (String statement, Object parameter) ; void commit (boolean force) ; void rollback (boolean force) ; List<BatchResult> flushStatements () ; @Override void close () ; void clearCache () ; Configuration getConfiguration () ; <T> T getMapper (Class<T> type) ; Connection getConnection () ; }
获取sqlSession源码位置:org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSessionFromDataSource
org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSessionFromConnection
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 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); final Executor executor = configuration.newExecutor(tx, execType); return new DefaultSqlSession (configuration, executor, autoCommit); } catch (Exception e) { closeTransaction(tx); throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } private SqlSession openSessionFromConnection (ExecutorType execType, Connection connection) { try { boolean autoCommit; try { autoCommit = connection.getAutoCommit(); } catch (SQLException e) { autoCommit = true ; } final Environment environment = configuration.getEnvironment(); final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); final Transaction tx = transactionFactory.newTransaction(connection); final Executor executor = configuration.newExecutor(tx, execType); return new DefaultSqlSession (configuration, executor, autoCommit); } catch (Exception e) { throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
获取Mapper代理类 1 2 CategoryMapper categoryMapper = sqlSession.getMapper(CategoryMapper.class);
因为默认的SqlSession实现是DefaultSqlSession,进入源码可以得知,CategoryMapper是通过代理工厂MapperProxyFactory反射得到Mapper代理类。这段代码逻辑可以通过顺藤摸瓜,找到Mapper代理是通过Map<Class<?>, MapperProxyFactory<?>> knownMappers
获取的。
源码入口:org.apache.ibatis.session.defaults.DefaultSqlSession#getMapper
1 2 3 public <T> T getMapper (Class<T> type) { return this .configuration.getMapper(type, this ); }
org.apache.ibatis.session.Configuration#getMapper
1 2 3 4 public <T> T getMapper (Class<T> type, SqlSession sqlSession) { return this .mapperRegistry.getMapper(type, sqlSession); }
org.apache.ibatis.binding.MapperRegistry#getMapper
找到这段代码可以得知: 1、代理工厂来自knownMappers 2、mapperProxyFactory.newInstance(sqlSession),Mapper每次都通过Mapper代理工厂生成,是一个代理类。
1 2 3 4 5 6 7 8 9 10 11 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); } }
knownMappers是如何初始化?
org.apache.ibatis.binding.MapperRegistry#addMapper
==> org.apache.ibatis.builder.xml.XMLConfigBuilder#mapperElement
可以发现是在XMLConfigBuilder解析mybatis-config.xml时通过parseConfiguration方法进行的。仔细观察下图,还提供了一个Spring下的FacotryBean实现进行配置。
执行Mapper代理方法 从上面分析得知,获取得到的Mapper是通过MapperProxyFactory工厂类创建的MapperProxy代理类。因此,在执行Mapper接口的方法时,会调用MapperProxy的invoke方法。 通过Debug也可以得到Mapper的执行逻辑。
源码入口:org.apache.ibatis.binding.MapperProxy#invoke
cachedInvoker(method)
返回MapperMethodInvoker对象,执行invoke方法(具体通过哪个实现可以实际debug,这里是MapperMethodInvoker的子类PlainMethodInvoker的invoke方法)org.apache.ibatis.binding.MapperMethod#execute
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 public Object execute (SqlSession sqlSession, Object[] args) { Object result; switch (command.getType()) { case INSERT: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.insert(command.getName(), param)); break ; } case UPDATE: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.update(command.getName(), param)); break ; } case DELETE: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.delete(command.getName(), param)); break ; } case SELECT: 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 if (method.returnsCursor()) { result = executeForCursor(sqlSession, args); } else { Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); if (method.returnsOptional() && (result == null || !method.getReturnType().equals(result.getClass()))) { result = Optional.ofNullable(result); } } break ; case FLUSH: result = sqlSession.flushStatements(); break ; default : 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; }
SqlSession, SqlSessionFactory, SqlSessionFactoryBuilder的关系 SqlSession的使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public void testSelectOne () { try { InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml" ); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder ().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); Category category = sqlSession.selectOne("moe.tsukasa.demo.dao.CategoryMapper.queryCategoryByCategoryId" , 1 ); log.info("category = [{}]" , JsonUtil.toJson(category)); Category category2 = sqlSession.selectOne("queryCategoryByCategoryId" , 1 ); log.info("category2 = [{}]" , JsonUtil.toJson(category2)); sqlSession.close(); inputStream.close(); } catch (Exception e){ log.error("errMsg = [{}]" , e.getMessage(), e); } }
SqlSession的执行原理 Excutor Excutor是MyBatis持久层框架中的一个关键组件,负责SQL语句的执行,缓存的处理,事务管理,参数处理。
Executor接口有两个实现,一个是BaseExecutor抽象类,一个是CachingExecutor实现类。BaseExecutor抽象类有四个实现SimpleExecutor,BathExecutor, ReuseExecutor, ClosedExecutor。
Executor继承关系:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public interface Executor { ResultHandler NO_RESULT_HANDLER = null ; int update (MappedStatement ms, Object parameter) throws SQLException; <E> List<E> query (MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException; <E> List<E> query (MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException; <E> Cursor<E> queryCursor (MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException; List<BatchResult> flushStatements () throws SQLException; void commit (boolean required) throws SQLException; void rollback (boolean required) throws SQLException; CacheKey createCacheKey (MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) ; boolean isCached (MappedStatement ms, CacheKey key) ; void clearLocalCache () ; void deferLoad (MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType) ; Transaction getTransaction () ; void close (boolean forceRollback) ; boolean isClosed () ; void setExecutorWrapper (Executor executor) ; }
Excutor是模板方法设计模式的应用,Executor接口有个抽象子类BaseExecutor,定义了一些模板方法,通过子类来实现。
SimpleExecutor是默认的Executor实现,适用于大多数情况,但不支持高级功能如二级缓存。我们只需要定义好myabtis-config.xml,MyBatis框架会负责调用适当的Executor来执行数据库操作。
总结
声明:本站所有文章均为原创或翻译,遵循署名 - 非商业性使用 - 禁止演绎 4.0 国际许可协议 ,如需转载请确保您对该协议有足够了解,并附上作者名 (Tsukasa) 及原文地址