为什么OpenEntityManagerInViewFilter会更改@Transactional传播REQUIRES_NEW行为?

时间:2017-11-13 19:37:49

标签: spring hibernate spring-mvc jpa spring-data-jpa

使用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,这都将按预期工作。

1 个答案:

答案 0 :(得分:0)

当您的应用程序(服务器)尝试进行两个单独的事务时,您仍然使用单个EntityManager和单个Datasource,因此在任何给定时间,JPA和数据库只能看到一个事务。因此,如果您希望将这些内容分开,则需要设置两个Datasource和两个EntityManagers