Spring JPA transaction在@ transmitted方法结束之前提交

rxztt3cl  于 5个月前  发布在  Spring
关注(0)|答案(3)|浏览(52)

我有一个控制器方法,它更新订单实体的某些字段。我在调试模式下跟踪了控制器方法的执行流程。我发现事务提交过早。事务在调用存储库更新方法后立即提交。这是什么问题?
源代码在下面。
//控制器

@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
    }
}

字符串
// Repository

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);
}


//application.yml

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


// pom依赖

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


如果我从控制器方法中删除@Transactional注解,则会发生javax.persistence. TransactionAddressedException。

mtb9vblg

mtb9vblg1#

在spring中,@Transactional定义了单个数据库事务。由于spring内部使用Hibernate EntityManager来管理数据库事务的会话,并且是自动处理的。一旦数据库事务成功,就会进行提交。我们可以在一个方法中有多个数据库事务。在这种情况下,commit将在每个成功的事务后发生。@Transactional并不意味着我们使用的方法。它只是说该方法有一个数据库事务,并且将由spring处理。另一点是我们不应该在控制器上写transactional级别,我们应该为它提供一个服务类,在那里我们可以使用transactional。
请参阅下面的链接,其中详细介绍了@Transactional
How Spring Jpa Transactional Works

iklwldmw

iklwldmw2#

很长很长一段时间以来,我们都无法使用默认的Java代理机制在控制器上使用@ translation annotations。Spring创建了一个控制器的代理,管理transactions的annotation processor失去了@Transactional annotation的可见性,因为它只能看到代理。

TL;DR:Spring托管的transactions无法在controller中启动,请将其移到服务层。

顺便说一下,控制器不应该有业务逻辑,因为你有(这3行'查找-设置-更新'是业务逻辑)。

ggazkfy8

ggazkfy83#

这个问题是由于mysql引擎类型。
默认情况下,Hibernate使用MyISAM引擎创建表,而MyISAM引擎不是事务性的。基本上,您只需要定义Hibernate的方言即可切换到InnoDB等事务性引擎类型。
试试这个:

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

字符串
下面的链接包含了关于mysql引擎类型的有用细节作为摘要信息:
https://www.w3resource.com/mysql/mysql-storage-engines.php

相关问题