【发布时间】:2018-04-26 14:23:02
【问题描述】:
使用 Spring 4.3.12、Spring Data JPA 1.11.8 和 Hibernate 5.2.12。
我们使用OpenEntityManagerInViewFilter 来确保我们的实体关系在加载实体后不会抛出LazyInitializationException。通常在我们的控制器中,我们使用 @ModelAttribute 注释方法通过 id 加载实体,并使加载的实体可用于控制器的请求映射处理程序方法。
在某些情况下,例如审计,我们想要提交实体修改,即使其他事务可能出错和回滚。因此,我们使用@Transactional(propagation = Propagation.REQUIRES_NEW) 注释我们的审计工作,以确保无论任何其他(如果有)可能成功完成或未成功完成的交易,该交易都能成功提交。
我在使用OpenEntityManagerInviewFilter 的实践中看到的是,当Propagation.REQUIRES_NEW 事务尝试提交发生在新事务范围之外的更改时,导致工作应该总是导致成功提交到数据库而不是回滚。
示例
鉴于此 Spring Data JPA 支持的存储库(EmployeeRepository 的定义类似):
import org.springframework.data.jpa.repository.JpaRepository;
public interface MethodAuditRepository extends JpaRepository<MethodAudit,Long> {
}
这项服务:
@Service
public class MethodAuditorImpl implements MethodAuditor {
private final MethodAuditRepository methodAuditRepository;
public MethodAuditorImpl(MethodAuditRepository methodAuditRepository) {
this.methodAuditRepository = methodAuditRepository;
}
@Override @Transactional(propagation = Propagation.REQUIRES_NEW)
public void auditMethod(String methodName) {
MethodAudit audit = new MethodAudit();
audit.setMethodName(methodName);
audit.setInvocationTime(LocalDateTime.now());
methodAuditRepository.save(audit);
}
}
还有这个控制器:
@Controller
public class StackOverflowQuestionController {
private final EmployeeRepository employeeRepository;
private final MethodAuditor methodAuditor;
public StackOverflowQuestionController(EmployeeRepository employeeRepository, MethodAuditor methodAuditor) {
this.employeeRepository = employeeRepository;
this.methodAuditor = methodAuditor;
}
@ModelAttribute
public Employee loadEmployee(@RequestParam Long id) {
return employeeRepository.findOne(id);
}
@GetMapping("/updateEmployee")
// @Transactional // <-- When uncommented, transactions work as expected (using OpenEntityManagerInViewFilter or not)
public String updateEmployee(@ModelAttribute Employee employee, RedirectAttributes ra) {
// method auditor performs work in new transaction
methodAuditor.auditMethod("updateEmployee"); // <-- at close of this method, employee update occurrs trigging rollback
// No code after this point executes
System.out.println(employee.getPin());
employeeRepository.save(employee);
return "redirect:/";
}
}
当updateEmployee 方法使用无效的pin 编号updateEmployee?id=1&pin=12345(数据库中的pin 编号限制为4 个字符)时,不会将审计插入到数据库中。
这是为什么?调用MethodAuditor 时不应该暂停当前事务吗?为什么当这个 Propagation.REQUIRES_NEW 事务提交时修改的员工刷新?
如果我通过将updateEmployee 方法注释为@Transactional 将其包装在事务中,那么审计将根据需要持续存在。无论是否使用OpenEntityManagerInViewFilter,这都会按预期工作。
【问题讨论】:
标签: spring hibernate spring-mvc jpa spring-data-jpa