我有一个控制器方法可以更新订单实体的某些字段。 我在调试模式下跟踪了控制器方法的执行流程。 我发现交易过早提交。 事务在调用存储库更新方法之后提交。 问题是什么?
源代码如下。
//控制器
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。
答案 0 :(得分:0)
在spring @Transactional
定义单个数据库事务。由于spring在内部使用Hibernate EntityManager
来管理数据库事务的会话,因此会自动处理。一旦数据库事务成功,就会完成。我们可以在单个方法中拥有多个数据库事务。在这种情况下{{1每次成功交易后都会发生。 commit
并不意味着我们使用的方法。它只是说该方法具有数据库事务,并且将由spring处理。另一点是我们不应该在控制器级写入事务,我们应该有一个服务类,我们可以使用事务。请参阅以下链接,其中详细介绍了@Transactional
。
答案 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引擎类型的有用详细信息作为摘要信息: