MyBatis在Spring环境下的事务管理

  • 2019 年 10 月 3 日
  • 笔记

MyBatis???????????????JDBC??????????????SQL???????????????????????????????????????????——????????JFinal?????Hibernate??????????MyBatis???????????????????????????????????????——????????????????

  1. ????????DAO????????????????????????????????????????
  2. ??????DAO?????????????????? 2? ?

???????????????????????????????MyBatis?????????DAO??????????Connection????????????????????????????????????????????????????????????????root cause???????????

  1. ???????
  2. MyBatis?Spring????????
  3. MyBatis?Spring????????
  4. ????

????

????????????????????????????????????????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??????????

  1. ???BasicDataSource????????bean?bean#1?????? dataSource?

    ??bean?????????????Spring??????

  2. ?? dataSource??? sqlSessionFactory?bean#2)???bean??????MyBatis???????????

    ?MyBatis???????????????SqlSession?????????SqlSession???SQLSessionFactory??????? org.mybatis.spring.SqlSessionFactoryBean???FactoryBean????????????????????????Spring????bean???????SQLSessionFactory???????????????????DefaultSqlSessionFactory????

  3. ?? 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);       }     }   }   }
  4. 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?????????????????????????