使用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
执行updateEmployee?id=1&pin=12345
方法时(数据库中的引脚号限制为4个字符),则不会向数据库中插入任何审计。
这是为什么?调用MethodAuditor
时,是否应暂停当前事务?当Propagation.REQUIRES_NEW
事务提交时,为什么修改后的员工会刷新?
但是,如果我通过将updateEmployee
方法注释为@Transactional
来将其包装在事务中,则审核将根据需要保留。无论是否使用OpenEntityManagerInViewFilter
,这都将按预期工作。
答案 0 :(得分:0)
当您的应用程序(服务器)尝试进行两个单独的事务时,您仍然使用单个EntityManager
和单个Datasource
,因此在任何给定时间,JPA和数据库只能看到一个事务。因此,如果您希望将这些内容分开,则需要设置两个Datasource
和两个EntityManagers