在@Transactional注释方法结束之前,Spring JPA事务提交

时间:2017-05-04 04:33:59

标签: spring jpa transactions

我有一个控制器方法可以更新订单实体的某些字段。 我在调试模式下跟踪了控制器方法的执行流程。 我发现交易过早提交。 事务在调用存储库更新方法之后提交。 问题是什么?

源代码如下。

//控制器

attendees.removeCost = function(index){
            var cost = attendees.formData.scenarios[0].scenarioItems[index];
            if(cost.id) {
                cost._destroy = true;
            } else {
                attendees.cost.splice(index, 1);
            }


        var cost = attendees.formData.scenarios[0].scenarioItems[index];
};

//存储库

@RestController
@RequestMapping(value = "/test", produces = {MediaType.APPLICATION_JSON_UTF8_VALUE})
public class TxTestController extends BaseController {

    @Autowired
    private OrderRepository orderRepository;

    @Transactional
    @GetMapping(value = "/update")
    public void updateOrder() throws Exception {
        Order order = orderRepository.findAll().get(0);
        order.setFeeRemains(order.getFeeRemains().add(BigDecimal.valueOf(100000000)));

        orderRepository.updateOrder(order.getId(), order.getRemains(), order.getFeeRemains(), order.getStatus());
        // The transaction is commited after execution of the above line.
        // and the external database tools can see the changed data from here.
        // So no way to rollback transaction after this line.

        System.out.println(order);
        // do another persistence jobs
    }
}

// application.yml

public interface OrderRepository extends JpaRepository<Order, String>, QueryDslPredicateExecutor<Order> {
@Modifying
@Query("update Order o set o.remains = :remains, o.feeRemains = :feeRemains, o.status = :status where o.id = :orderId")
void updateOrder(@Param("orderId") String orderId,
                 @Param("remains") BigDecimal remains,
                 @Param("feeRemains") BigDecimal feeRemains,
                 @Param("status") Order.Status status);
}

// pom依赖

spring:
  jpa:
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQL5Dialect
    generate-ddl: true
    hibernate:
      ddl-auto: update
    show-sql: false
  datasource:
    url: jdbc:mysql://localhost:3306/plutusds
    username: root
    password: root
    testWhileIdle: true
    validationQuery: SELECT 1

如果我从控制器方法中删除<?xml version="1.0" encoding="UTF-8"?> ... <dependencies> ... <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> <version>1.5.2.RELEASE</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.41</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>5.2.9.Final</version> </dependency> ... </dependencies> ... </project> 注释,那么 发生了javax.persistence.TransactionRequiredException。

3 个答案:

答案 0 :(得分:0)

在spring @Transactional定义单个数据库事务。由于spring在内部使用Hibernate EntityManager来管理数据库事务的会话,因此会自动处理。一旦数据库事务成功,就会完成。我们可以在单个方法中拥有多个数据库事务。在这种情况下{{1每次成功交易后都会发生。 commit并不意味着我们使用的方法。它只是说该方法具有数据库事务,并且将由spring处理。另一点是我们不应该在控制器级写入事务,我们应该有一个服务类,我们可以使用事务。请参阅以下链接,其中详细介绍了@Transactional

How Spring Jpa Transactional Works

答案 1 :(得分:0)

很长一段时间以来,无法在使用默认Java代理机制的控制器上使用@Transactional注释。 Spring创建了一个控制器代理,管理事务的注释处理器失去了@Transactional注释的可见性,因为它只能看到代理。

TL; DR:Spring管理的事务无法在控制器中启动。将其移至服务层。

顺便说一句,控制器不应该像你的那样拥有业务逻辑('find-set-update'的3行是业务逻辑)。

答案 2 :(得分:0)

此问题是由于mysql引擎类型引起的。

默认情况下,Hibernate使用非事务MyISAM引擎创建表。基本上,您只需要为休眠定义方言即可切换到事务引擎类型,例如InnoDB。

尝试一下:

spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQLInnoDBDialect

下面的链接包含有关mysql引擎类型的有用详细信息作为摘要信息:

https://www.w3resource.com/mysql/mysql-storage-engines.php