Java Spring JDBC事务性副作用

Java Spring JDBC事务性副作用,java,mysql,spring,mybatis,spring-jdbc,Java,Mysql,Spring,Mybatis,Spring Jdbc,我设置了两个MySQL表:Job和Summaries 我正在尝试检查事务管理是否实际按照预期工作: 功能如下: @Transactional("mysqlTransactionManager") public void sumLicenseDayUsage(List<LicenseUsage> usages) { Job job = startJob(); calculateDailyUsage(usages); updateJob

我设置了两个MySQL表:Job和Summaries

我正在尝试检查事务管理是否实际按照预期工作:

功能如下:

@Transactional("mysqlTransactionManager")
public void sumLicenseDayUsage(List<LicenseUsage> usages)
{
        Job job = startJob();

        calculateDailyUsage(usages);

        updateJob(usages, job);
        completeJob(job);
}
我确保calculateDailyUsage抛出异常。这里的不同函数对实际的SQL命令使用MyBatis映射器

我希望在抛出异常时,作业表中不会存在任何行,尽管它们是在startJob中创建的。仍然会始终创建一行,并且从不回滚

我想我是在根据医生的记录使用它,但很明显我肯定遗漏了什么

有什么与我使用MyBatis有关的吗?或者有两个表,因此有两个不同的映射器?它看起来不像是从医生和做各种测试得到的

以下是我的XML配置:

<tx:annotation-driven transaction-manager="mysqlTransactionManager"/>

<bean id="mysqlBuilder" class="com.company.project.mysqlutils.EmbeddedMysqlDatabaseBuilder">
    <constructor-arg value="sql/create_sum_tables.sql"/>
</bean>


<bean id="billingDB" class="com.company.project.mysqlutils.EmbeddedMysqlDatabase" factory-bean="mysqlBuilder" factory-method="build"
      destroy-method="shutdown"/>

<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="billingDB" />
</bean>

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="billingDB" />
    <property name="mapperLocations" value="classpath*:META-INF/mappers/mysql/**.xml" />
</bean>

<bean id="mysqlTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="billingDB" />
</bean>

<bean id="sumLicenseDayMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
    <property name="mapperInterface" value="com.company.project.sumtables.mappers.SumLicenseDayMapper" />
    <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

<bean id="jobsMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
    <property name="mapperInterface" value="com.company.project.sumtables.mappers.JobsMapper" />
    <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

Thx提前

回答我自己的问题,因为我认为这个问题被很多人忽视了,可能会对应用程序的交易方案产生巨大影响

这个问题与Spring如何管理事务无关。这实际上非常有效

然而,本例中DB MySQL的行为是导致事务过早提交的原因

根据位于的MySQL文档,某些DML和DDL语句会导致当前事务的隐式提交

根据Oracle和Postgres的文档以及MySQL报告的bug/feature,这似乎对Oracle和Postgres也是如此,MySQL报告的bug/feature表明Oracle的行为相同:

在我的例子中,calculateDailyUsage函数中有一个CREATE TABLE语句,该语句导致隐式提交,因此不可能回滚它之前发生的所有SQL语句,以防该语句之后出现错误

为了进一步解释它的重要性,假设我们设计了一个计费应用程序,其中过度计费是最严重的问题,请参见下面的示例:

@Transactional("mysqlTransactionManager")
public void sumLicenseDayUsage(List<LicenseUsage> usages)
{
        insertBillingData();

        createIntermediateTable();

        summarizeData();
}
在上面的例子中,如果Summaredata出现问题,我们肯定希望将insertBillingData插入的数据回滚。但这实际上不会发生,因为中间语句createIntermediateTable将实际提交事务,Summaredata将在不同的事务中完成。这是非常糟糕的,设计者应该注意这一点,因为在应用程序开发过程中创建临时表是很常见的

克服这个问题的方法和解决方法是将所有DDL/DML语句放在函数的开头,以便在其事务中执行它们,函数的其余部分在另一个事务中执行,在出现问题时可以正确回滚,如下所示:

@Transactional("mysqlTransactionManager")
public void sumLicenseDayUsage(List<LicenseUsage> usages)
{
        createIntermediateTable();

        insertBillingData();

        summarizeData();
}
最后,需要记住的一件事是,有时文档并不准确,例如MySQL doc声明CREATE临时表不执行隐式提交,尽管在我彻底测试它之后执行了隐式提交

希望有帮助