千家信息网

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

发表于:2025-01-24 作者:千家信息网编辑
千家信息网最后更新 2025年01月24日,这篇文章主要介绍Mybatis Spring与Mybatis事物transaction的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!mybatis-3.4.6.rel
千家信息网最后更新 2025年01月24日Mybatis Spring与Mybatis事物transaction的示例分析

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

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

List-1

                            

先来看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 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的示例分析"这篇文章的所有内容,感谢各位的阅读!希望分享的内容对大家有帮助,更多相关知识,欢迎关注行业资讯频道!

0