温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Mybatis Spring与Mybatis事物transaction的示例分析

发布时间:2021-09-15 11:17:25 来源:亿速云 阅读:165 作者:小新 栏目:大数据

这篇文章主要介绍Mybatis Spring与Mybatis事物transaction的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!

    mybatis-3.4.6.release, mybatis-spring-1.3.2.release.

    List-1

<!-- 配置mybatis的sqlSessionFactory --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">     <property name="dataSource" ref="dataSource"/>     <!-- 自动扫描mappers.xml文件 -->     <!--<property name="mapperLocations" value="classpath:com/mjduan/project/mapper/*.xml"/>-->     <!-- mybatis配置文件 -->     <property name="configLocation" value="mybatis-config.xml"/> </bean> <!-- DAO接口所在包名,Spring会自动查找其下的类 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">     <property name="basePackage" value="com.mjduan.project.mapper"/>     <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> </bean>

    先来看SqlSessionFactoryBean,它需要一个dataSource,还是就是mybatis的xml配置文件。

    SqlSessionFactoryBean类实现了InitializingBean接口,了解SpringIOC的话,就知道InitializingBean的作用了。来看InitializingBean的afterPropertiesSet(),afterPropertiesSet()调用buildSqlSessionFactory()生成SqlSessionFactory,

    List-2

protected SqlSessionFactory buildSqlSessionFactory() throws IOException {     Configuration configuration;     XMLConfigBuilder xmlConfigBuilder = null;     if (this.configuration != null) {         configuration = this.configuration;         if (configuration.getVariables() == null) {         configuration.setVariables(this.configurationProperties);         } else if (this.configurationProperties != null) {         configuration.getVariables().putAll(this.configurationProperties);         }     } else if (this.configLocation != null) {         xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);         configuration = xmlConfigBuilder.getConfiguration();     } else {         if (LOGGER.isDebugEnabled()) {         LOGGER.debug("Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");         }         configuration = new Configuration();         if (this.configurationProperties != null) {         configuration.setVariables(this.configurationProperties);         }     }     if (this.objectFactory != null) {         configuration.setObjectFactory(this.objectFactory);     }     if (this.objectWrapperFactory != null) {         configuration.setObjectWrapperFactory(this.objectWrapperFactory);     }     if (this.vfs != null) {         configuration.setVfsImpl(this.vfs);     }     if (hasLength(this.typeAliasesPackage)) {         String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,             ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);         for (String packageToScan : typeAliasPackageArray) {         configuration.getTypeAliasRegistry().registerAliases(packageToScan,                 typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);         if (LOGGER.isDebugEnabled()) {             LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");         }         }     }     if (!isEmpty(this.typeAliases)) {         for (Class<?> typeAlias : this.typeAliases) {         configuration.getTypeAliasRegistry().registerAlias(typeAlias);         if (LOGGER.isDebugEnabled()) {             LOGGER.debug("Registered type alias: '" + typeAlias + "'");         }         }     }     if (!isEmpty(this.plugins)) {         for (Interceptor plugin : this.plugins) {         configuration.addInterceptor(plugin);         if (LOGGER.isDebugEnabled()) {             LOGGER.debug("Registered plugin: '" + plugin + "'");         }         }     }     if (hasLength(this.typeHandlersPackage)) {         String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,             ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);         for (String packageToScan : typeHandlersPackageArray) {         configuration.getTypeHandlerRegistry().register(packageToScan);         if (LOGGER.isDebugEnabled()) {             LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers");         }         }     }     if (!isEmpty(this.typeHandlers)) {         for (TypeHandler<?> typeHandler : this.typeHandlers) {         configuration.getTypeHandlerRegistry().register(typeHandler);         if (LOGGER.isDebugEnabled()) {             LOGGER.debug("Registered type handler: '" + typeHandler + "'");         }         }     }     if (this.databaseIdProvider != null) {//fix #64 set databaseId before parse mapper xmls         try {         configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));         } catch (SQLException e) {         throw new NestedIOException("Failed getting a databaseId", e);         }     }     if (this.cache != null) {         configuration.addCache(this.cache);     }     if (xmlConfigBuilder != null) {         try {         xmlConfigBuilder.parse();         if (LOGGER.isDebugEnabled()) {             LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'");         }         } catch (Exception ex) {         throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);         } finally {         ErrorContext.instance().reset();         }     }     //这里transactionFactory使用SpringManagedTransactionFactory,在获取connection时会用到     if (this.transactionFactory == null) {         this.transactionFactory = new SpringManagedTransactionFactory();     }     //通过environment来传递     configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));     if (!isEmpty(this.mapperLocations)) {         for (Resource mapperLocation : this.mapperLocations) {         if (mapperLocation == null) {             continue;         }         try {             XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),                 configuration, mapperLocation.toString(), configuration.getSqlFragments());             xmlMapperBuilder.parse();         } catch (Exception e) {             throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);         } finally {             ErrorContext.instance().reset();         }         if (LOGGER.isDebugEnabled()) {             LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");         }         }     } else {         if (LOGGER.isDebugEnabled()) {         LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");         }     }     return this.sqlSessionFactoryBuilder.build(configuration); }

    List-2中的方法内容很多,但是整个在做的就是生成Configuration。

    我们需要注意的点是this.transactionFactory = new SpringManagedTransactionFactory(),这是mybatis和spring联系到一起的点。

    再来看MapperScannerConfigurer,这个类实现了BeanDefinitionRegistryPostProcessor接口,在postProcessBeanDefinitionRegistry方法中实例化了个ClassPathMapperScanner,之后scan->doScan:

    List-3

  private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {     GenericBeanDefinition definition;     for (BeanDefinitionHolder holder : beanDefinitions) {       definition = (GenericBeanDefinition) holder.getBeanDefinition();       if (logger.isDebugEnabled()) {         logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName()            + "' and '" + definition.getBeanClassName() + "' mapperInterface");       }       // the mapper interface is the original class of the bean       // but, the actual class of the bean is MapperFactoryBean       definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59       //设置MapperFactoryBean类型       definition.setBeanClass(this.mapperFactoryBean.getClass());       definition.getPropertyValues().add("addToConfig", this.addToConfig);       boolean explicitFactoryUsed = false;       if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {         definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));         explicitFactoryUsed = true;       } else if (this.sqlSessionFactory != null) {         //设置属性引用SqlSessionFactory           definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);         explicitFactoryUsed = true;       }       if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {         if (explicitFactoryUsed) {           logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");         }         definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));         explicitFactoryUsed = true;       } else if (this.sqlSessionTemplate != null) {         if (explicitFactoryUsed) {           logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");         }         definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);         explicitFactoryUsed = true;       }       if (!explicitFactoryUsed) {         if (logger.isDebugEnabled()) {           logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");         }         definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);       }     }   }

    List-3中将扫描到的mapper接口,封装为MapperFactoryBean,且将SqlSessionFactory设置为其属性。

    MapperFactoryBean类继承SqlSessionDaoSupport,setSqlSessionFactory的方法在父类SqlSessionDaoSupport中,如下List-4,利用SqlSessionTemplate初始化sqlSession。

    List-4

public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {     if (!this.externalSqlSession) {         this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);     } }

    来看SqlSessionTemplate的构造方法,如下List-5

    List-5

  public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,       PersistenceExceptionTranslator exceptionTranslator) {     notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");     notNull(executorType, "Property 'executorType' is required");     this.sqlSessionFactory = sqlSessionFactory;     this.executorType = executorType;     this.exceptionTranslator = exceptionTranslator;     this.sqlSessionProxy = (SqlSession) newProxyInstance(         SqlSessionFactory.class.getClassLoader(),         new Class[] { SqlSession.class },         new SqlSessionInterceptor());   }

    SqlSessionTemplate实现了接口SqlSession,SqlSessionTemplate内部所有的方法实现都委托给sqlSessionProxy来实现的,sqlSessionProxy是JDK动态代理的,来看SqlSessionInterceptor:

    List-6

  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) {         Throwable unwrapped = unwrapThrowable(t);         if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {           // release the connection to avoid a deadlock if the translator is no loaded. See issue #22           closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);           sqlSession = null;           Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);           if (translated != null) {             unwrapped = translated;           }         }         throw unwrapped;       } finally {         if (sqlSession != null) {           closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);         }       }     }   }

    List-6中首先获得SqlSession,如果SqlSession不存在,则通过sessionFactory.openSession来创建一个。

    List-7

  public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {     notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);     notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);     SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);     SqlSession session = sessionHolder(executorType, holder);     if (session != null) {       return session;     }     if (LOGGER.isDebugEnabled()) {       LOGGER.debug("Creating a new SqlSession");     }     session = sessionFactory.openSession(executorType);     registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);     return session;   }

    sessionFactory.openSession()->openSessionFromDataSource(),获取Environemnt,之后从Environment中获取TransactionFactory——List-2中的SpringManagedTransactionFactory, newTransaction()返还的是SpringManagedTransaction。

    List-8

  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); // may have fetched a connection so lets call close()       throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);     } finally {       ErrorContext.instance().reset();     }   }

    final Executor executor = configuration.newExecutor(tx, execType),默认情况下是SimpleExecutor,现在SqlSession有了,回到List-6的method.invoke(),即实际调用时这个方法会被调用,以update为例,看SimpleExecutor的doUpdate方法

    List-9

  @Override   public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {     Statement stmt = null;     try {       Configuration configuration = ms.getConfiguration();       StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);       stmt = prepareStatement(handler, ms.getStatementLog());       return handler.update(stmt);     } finally {       closeStatement(stmt);     }   }

    来看prepareStatement(),如下List-10,通过transaction.getConnection来获取Connection,而这个transaction就是SpringManagedTransaction,

    List-10

  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {     Statement stmt;     Connection connection = getConnection(statementLog);     stmt = handler.prepare(connection, transaction.getTimeout());     handler.parameterize(stmt);     return stmt;   }   protected Connection getConnection(Log statementLog) throws SQLException {     Connection connection = transaction.getConnection();     if (statementLog.isDebugEnabled()) {       return ConnectionLogger.newInstance(connection, statementLog, queryStack);     } else {       return connection;     }   }

    SpringManagedTransaction中getConnection会调用openConnection,如下List-11,

    List-11

  private void openConnection() throws SQLException {     this.connection = DataSourceUtils.getConnection(this.dataSource);     this.autoCommit = this.connection.getAutoCommit();     this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource);     if (LOGGER.isDebugEnabled()) {       LOGGER.debug(           "JDBC Connection ["               + this.connection               + "] will"               + (this.isConnectionTransactional ? " " : " not ")               + "be managed by Spring");     }   }

    通过DataSourceUtils.getConnection()来获取Connection->doGetConnection(),从TransactionSynchronizationManager中获取ConnectionHler,如果是null,则通过dataSource来得到一个Connection;如果不为null,且已经开启事物,则直接返回connectionHolder中的connection。

    List-12

public static Connection doGetConnection(DataSource dataSource) throws SQLException {     Assert.notNull(dataSource, "No DataSource specified");     ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(dataSource);     if (conHolder == null || !conHolder.hasConnection() && !conHolder.isSynchronizedWithTransaction()) {         logger.debug("Fetching JDBC Connection from DataSource");         Connection con = dataSource.getConnection();         if (TransactionSynchronizationManager.isSynchronizationActive()) {             logger.debug("Registering transaction synchronization for JDBC Connection");             ConnectionHolder holderToUse = conHolder;             if (conHolder == null) {                 holderToUse = new ConnectionHolder(con);             } else {                 conHolder.setConnection(con);             }             holderToUse.requested();             TransactionSynchronizationManager.registerSynchronization(new DataSourceUtils.ConnectionSynchronization(holderToUse, dataSource));             holderToUse.setSynchronizedWithTransaction(true);             if (holderToUse != conHolder) {                 TransactionSynchronizationManager.bindResource(dataSource, holderToUse);             }         }         return con;     } else {         conHolder.requested();         if (!conHolder.hasConnection()) {             logger.debug("Fetching resumed JDBC Connection from DataSource");             conHolder.setConnection(dataSource.getConnection());         }         return conHolder.getConnection();     } }

    TransactionSynchronizationManager通过ThreadLocal来保存ConnectionHolder,而Spring tx中也是通过这个类来对ConnectionHolder进行持有的。

以上是“Mybatis Spring与Mybatis事物transaction的示例分析”这篇文章的所有内容,感谢各位的阅读!希望分享的内容对大家有帮助,更多相关知识,欢迎关注亿速云行业资讯频道!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI