由於使用的是SpringBoot方式,因此此處隱藏了對於MapperScan的處理配置。通常不須要分庫分表時,能夠直接使用DruidDataSource做爲數據源
詳情見 【@Mapper與@MapperScan如何正確使用】解析。java
/*事務管理*/ @Bean private DataSourceTransactionManager transactionManager(TDataSource mysqlTddlDataSource) { DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(); transactionManager.setDataSource(mysqlTddlDataSource); return transactionManager; } @Bean private SqlSessionFactoryBean dataSqlSessionFactory(TDataSource mysqlTddlDataSource) throws IOException { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(mysqlTddlDataSource); sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver() .getResources("classpath*:mapper/*Mapper.xml")); sqlSessionFactoryBean.setPlugins(new Interceptor[] { new SqlStatementInterceptor(ServiceNameConstant.GIANT) }); // 自定義在sql時的攔截切面處理 return sqlSessionFactoryBean; } @Bean(name = "mysqlTddlDataSource", initMethod = "init") private TDataSource dataSource() { TDataSource datasource = new TDataSource(); datasource.setAppName(appname); datasource.setDynamicRule(true); return datasource; }
mybatis在構建DefaultSqlSessionFactory時,會初始化該關鍵對象。 他的活動週期貫穿整個mybatis的執行鏈路!不少處理都須要用到它!mysql
構建DefaultSqlSessionFactory的工廠類, 因此咱們直接看getObject()方法的核心buildSqlSessionFactory方法spring
public SqlSessionFactory getObject() throws Exception { if (this.sqlSessionFactory == null) { afterPropertiesSet(); } return this.sqlSessionFactory; } @Override public void afterPropertiesSet() throws Exception { notNull(dataSource, "Property 'dataSource' is required"); notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required"); this.sqlSessionFactory = buildSqlSessionFactory(); }
內部構建了一個關鍵對象Configuration !不少處理都須要用到它sql
MapperProxy的工廠類。數據庫
在【mybatis的MapperScan到底作了什麼】的文章中分析:ClassPathMapperScanner指定用MapperFactoryBean來處理Mapper接口 。文章中提到過bean的定義信息BeanDefinition存儲在了DefaultListableBeanFactory中。mybatis
那是如何構建bean的呢?app
AbstractBeanFactory.doGetBean:
根據beanName去DefaultListableBeanFactory的beanDefinitionMap中獲取對象定義並構建實例ide
DefaultListableBeanFactory是AbstractBeanFactory的實現類。函數
繼承sqlSession接口,SqlSession對象徹底包含以數據庫爲背景的全部執行SQL操做的方法。ui
SqlSession的update/insert/delete會調用BaseExecutor的update方法;
SqlSession的selectList會調用BaseExecutor的query方法;
經過源碼能夠看出SqlSessionTemplate中有一個代理對象sqlSessionProxy,實際的handler是SqlSessionInterceptor!
首先:
斷定在事務管理器中當前線程是否有SqlSessionHolder,有則referenceCount++並返回SqlSession;無則SqlSessionFactory.opeanSession開啓一個SqlSession, 並斷定是否有事務;有則將SqlSession包裝成SqlSessionHolder,與當前線程綁定在TransactionSynchronizationManager。
因此: 若是沒開啓事務SqlSession必定會真正的建立,有事務則視spring的事務傳播級別來複用!
斷定事務的狀況下當前線程綁定 DefaultSqlSession與DefaultSqlSessionFactory的關係:
建立一個實例由SpringManagedTransactionFactory構建的SpringManagedTransaction(此實例含Connection內部變量,此時的值爲null)。 建立一個類型由傳入的ExecutorType(默認是「SIMPLE」)指定的Executor,被包裝成invocationHandler爲Plugin(包裝了Interceptor)的代理對象, 並返回DefaultSqlSession。
對於每個Mapper的BeanWrapper指定MapperFactoryBean後的getObject()處理:
此處的getSqlSession() 返回的是SqlSessionTemplate對象,mapperInterface是實際的mapper接口
接下來的執行過程爲:
SqlSessionTemplate的getMapper方法--->Configuration的getMapper方法 ---> MapperRegistry的getMapper方法----> MapperProxyFactory的newInstance方法
因此: 每個Mapper其實是一個InvocationHandler爲MapperProxy的代理對象!
MapperProxy的invoke會進入MapperMethod的execute方法。
將會斷定是什麼類型的sql命令,該用什麼樣的對象來處理返回結果,若是參數含有rowBounds則調用分頁的查詢。
command.getName()是xml配置文件中的sql id, args是sql的入參。
最終的執行! 實例能夠參見SimpleStatementHandler
public interface StatementHandler { //從鏈接中獲取一個Statement Statement prepare(Connection connection) throws SQLException; //設置statement執行裏所需的參數 void parameterize(Statement statement) throws SQLException; //批量 void batch(Statement statement) throws SQLException; //更新:update/insert/delete語句 int update(Statement statement) throws SQLException; //執行查詢 <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException; BoundSql getBoundSql(); ParameterHandler getParameterHandler(); }
當實際調用mapper中的方法時 -> MapperProxy.inovke -> MapperMethod.execute ,最終回到了SqlSessionTemplate內部對象sqlSessionProxy執行。
上文也說過sqlSessionProxy的handler是SqlSessionInterceptor,其會執行進入DefaultSqlSession(implements SqlSession)。
在上文中有講過: Executor 其實是一個InvocationHandler爲Plugin的代理類。
因此:實際上Executor 在執行方法時,將會執行對象代理的Plugin的invoke方法(interceptor.intercept()方法) 再執行Executor實例的方法。
以update方法爲例:
在Executor實例BaseExecutor的update方法中跳轉入SimpleExecutor的doUpdate方法。 內部在執行prepareStatement時,會獲取connection。
public class SimpleExecutor extends BaseExecutor { public SimpleExecutor(Configuration configuration, Transaction transaction) { super(configuration, transaction); } @Override public int doUpdate(MappedStatement ms, Object parameter) throws SQLException { Statement stmt = null; try { //得到配置文件對象 Configuration configuration = ms.getConfiguration(); //得到statementHandler裏面有statement,來處理 StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null); stmt = prepareStatement(handler, ms.getStatementLog()); //最終是一個statement進行處理 return handler.update(stmt); } finally { closeStatement(stmt); } } @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裏面有statement,來處理 StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); //得到statement stmt = prepareStatement(handler, ms.getStatementLog()); //最終是一個statement進行處理 return handler.<E>query(stmt, resultHandler); } finally { closeStatement(stmt); } } @Override public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException { return Collections.emptyList(); } private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; Connection connection = getConnection(statementLog); // 獲取鏈接 stmt = handler.prepare(connection); //將Statement轉爲PrepareStatement handler.parameterize(stmt); return stmt; } }
獲取connection是從BaseExecutor中的transcation中獲取; 又回到了org.mybatis.spring.transaction.SpringManagedTransaction(管理JDBC鏈接的open/commit/rollback/close)
最終來到org.springframework.jdbc.datasource.DataSourceUtils:
首先從事務管理器TransactionSynchronizationManager的ThreadLocal中獲取當前datasource對應的ConnectionHolder,若是爲空或者沒有鏈接,則構建新的鏈接。若是是在spring事務內,由於在開啓事務過程當中會綁定一個已經提早開啓的鏈接,因此此處會返回該鏈接(事務是線程內獨佔共享的), 若是是非事務的則會直接新增鏈接。固然: 若是是在開啓事務的線程中也會將新增的Connection包裝成ConnectionHolder並綁定在線程上下文中。