MyBatis在Spring环境下的事务管理
- 2019 年 10 月 3 日
- 笔记
MyBatis???????????????JDBC??????????????SQL???????????????????????????????????????????——????????JFinal?????Hibernate??????????MyBatis???????????????????????????????????????——????????????????
- ????????DAO????????????????????????????????????????
- ??????DAO?????????????????? 2? ?
???????????????????????????????MyBatis?????????DAO??????????Connection????????????????????????????????????????????????????????????????root cause???????????
- ???????
- MyBatis?Spring????????
- MyBatis?Spring????????
- ????
????
????????????????????????????????????????Spring+MyBatis????????
Spring 3.2.9/4.3.5 + Mybatis 3.2.6 + mybatis-spring 1.2.2 + mysql connector 5.1.20 + commons-dbcp 1.4
?MyBatis???????????
//??1 <!-- bean#1--> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <!-- ?????????--> <!-- ??DBCP????? --> //??????????? <property name="defaultAutoCommit" value="${dbcp.defaultAutoCommit}" /> </bean> <!-- bean#2--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="mapperLocations" value="classpath*:path/to/mapper/**/*.xml" /> </bean> <!-- bean#3 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- bean#4--> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value=".path.to.mapper" /> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> </bean> <!-- bean5 --> <tx:annotation-driven transaction-manager="transactionManager" />
???????
?????????????????????????6?10? ms
?????20 ms
??????????????????????????????????????DAO?????????????bean#1?? defaultAutoCommit
????? false
????????? true
?????????
??????MyBatis?????????????????????????????????????API?????????????????????????Spring?????????????Spring??????????????????MyBatis???????DAO??????????????????
????
?????Service?????????mapper????????2?? insertModelList()
????????????? delModels()
????????????????
//??2 //@Transactional public void testIS(){ List<Model> models= new ArrayList<>(); //??????????? modelMapper.insertModelList(50001l, models); modelMapper.delModels(50001); if (CollectionUtils.isNotEmpty(models)) modelMapper.insertModelList(50001, models); modelMapper.delModels(50001); } public void testOther(){ System.out.println("????"); System.out.println(modelMapper.getClass().getClassLoader()); modelMapper.delModels(50001); }
???????cat????????????????cat????????AOP?????????
//??3 public class DaoTimeAdvice { private long time = 0; private long num = 0; public Object calcTime(ProceedingJoinPoint joinPoint) throws Throwable { long then = System.nanoTime(); Object object = joinPoint.proceed(); long now = System.nanoTime(); setTime(getTime() + (now-then)); setNum(getNum() + 1); return object; } //??getter & setter??? public void printInfo() { System.out.println("?????" + num); System.out.println("?????" + time); System.out.println("?????" + time / num); } }
?????
//??4 public static void test(){ System.out.println(new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss]").format(new Date()) + " ????!"); for (int i = 0; i < TEST_NUM; i++) { ItemStrategyServiceTest ist = (ItemStrategyServiceTest) context.getBean("isTS"); ist.testIS(); if (i % 1000 == 0) { System.out.println("1000?"); } } DaoTimeAdvice ad = (DaoTimeAdvice) context.getBean("daoTimeAdvice"); ad.printInfo(); ItemStrategyServiceTest ist = (ItemStrategyServiceTest) context.getBean("isTS"); ist.testOther(); System.exit(1); }
?????
defaultAutoCommit |
???? | ?????(ns) | ????(ns) |
---|---|---|---|
true | 40000 | 17831088316 | 445777 |
true | 40000 | 17881589992 | 447039 |
false | 40000 | 27280458229 | 682011 |
false | 40000 | 27237413893 | 680935 |
defaultAutoCommit? false
??????? true
??1.5???????2???????????cat??????AOP?????????????????? false
? true
??????
MyBatis?Spring????????
??????????????MyBatis?DAO?bean??????????
-
???BasicDataSource????????bean?bean#1??????
dataSource
???bean?????????????Spring??????
-
??
dataSource
???sqlSessionFactory
?bean#2)???bean??????MyBatis????????????MyBatis???????????????SqlSession?????????SqlSession???SQLSessionFactory???????
org.mybatis.spring.SqlSessionFactoryBean
???FactoryBean????????????????????????Spring????bean???????SQLSessionFactory???????????????????DefaultSqlSessionFactory???? -
??
sqlSessionFactory
????????mapper????bean#4???????DAO???????????????????????????DAO??????Spring?????????bean???MyBatis???????????mapper?????????SQL??????????XML?????????????MyBatis???????????????????????
org.mybatis.spring.mapper.MapperScannerConfigurer
?????org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor
???????Spring?????bean?????????????????????????Spring?????mapper????????????????????//??5 @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { if (this.processPropertyPlaceHolders) { processPropertyPlaceHolders(); } ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); //?????? scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS)); } /** * Perform a scan within the specified base packages. * @param basePackages the packages to check for annotated classes * @return number of beans registered */ public int scan(String... basePackages) { int beanCountAtScanStart = this.registry.getBeanDefinitionCount(); doScan(basePackages); // Register annotation config processors, if necessary. if (this.includeAnnotationConfig) { AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); } return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart); }
?????????????mapper????
org.mybatis.spring.mapper.MapperFactoryBean<Object>
?????????org.mybatis.spring.mapper.ClassPathMapperScanner.processBeanDefinitions(Set<BeanDefinitionHolder>)
???????????????????org.mybatis.spring.SqlSessionTemplate
???????????????????//??6 /** * Proxy needed to route MyBatis method calls to the proper SqlSession got * from Spring's Transaction Manager * It also unwraps exceptions thrown by {@code Method#invoke(Object, Object...)} to * pass a {@code PersistenceException} to the {@code PersistenceExceptionTranslator}. */ private class SqlSessionInterceptor implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { SqlSession sqlSession = getSqlSession( SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator); try { Object result = method.invoke(sqlSession, args); if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) { // force commit even on non-dirty sessions because some databases require // a commit/rollback before calling close() sqlSession.commit(true); } return result; } catch (Throwable t) { //???????? throw unwrapped; } finally { if (sqlSession != null) { closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); } } } }
-
MyBatis?Spring????????
??????????SqlSessionFactory????
org.apache.ibatis.session.defaults.DefaultSqlSessionFactory
?????????????org.mybatis.spring.transaction.SpringManagedTransactionFactory
??????1?????????Spring?????????????Service????????????????????@Transactional
?????Spring?????????????????MyBatis????????????????????6????
isSqlSessionTransactional()
?????????????Spring????????????????commit()
???????????????Spring??????????????commit()
??????????SpringManagedTransactionFactory
??commit()
?????//??7 private void openConnection() throws SQLException { this.connection = DataSourceUtils.getConnection(this.dataSource); this.autoCommit = this.connection.getAutoCommit(); this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource); } public void commit() throws SQLException { if (this.connection != null && !this.isConnectionTransactional && !this.autoCommit) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Committing JDBC Connection [" + this.connection + "]"); } this.connection.commit(); } }
????????????
commit()
????3?????????DataSource?autoCommit
?false
????????true
?????????????Committing JDBC Connection [xxxxxx]
?????????????????????????????????????
????
???????????DAO??????????????????????????????Spring?????????????? defaultAutoCommit
? true
??????????????????????????MyBatis??????DAO??????????????????Service???? @transactional
??????? true
? false
???????
???????????????????????????????????????????????????????2?????????????????????????