【问题标题】:Why does OpenEntityManagerInViewFilter change @Transactional propagation REQUIRES_NEW behavior?为什么 OpenEntityManagerInViewFilter 会更改 @Transactional 传播 REQUIRES_NEW 行为?
【发布时间】: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&amp;pin=12345(数据库中的pin 编号限制为4 个字符)时,不会将审计插入到数据库中。

这是为什么?调用MethodAuditor 时不应该暂停当前事务吗?为什么当这个 Propagation.REQUIRES_NEW 事务提交时修改的员工刷新?

如果我通过将updateEmployee 方法注释为@Transactional 将其包装在事务中,那么审计将根据需要持续存在。无论是否使用OpenEntityManagerInViewFilter,这都会按预期工作。

【问题讨论】:

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


    【解决方案1】:

    当您的应用程序(服务器)尝试进行两个单独的事务时,您仍在使用单个 EntityManager 和单个 Datasource,因此在任何给定时间 JPA 和数据库只看到一个事务。所以如果你想把这些东西分开你需要设置两个Datasources和两个EntityManagers

    【讨论】:

    • 感谢您的回答。如果是这种情况,为什么将updateEmployee 方法注释为@Transaction 可以解决问题?同一个EntityManager 实例中的REQUIRES_NEW 事务是否会暂停当前事务,从而允许新事务以不受任何其他事务影响的方式运行?
    • 我注意到REQUIRES_NEW javadoc 上的注释:“实际的事务暂停不会在所有事务管理器上开箱即用。这尤其适用于需要 javax.xml 的 JtaTransactionManager。 transaction.TransactionManager 对其可用(在标准 Java EE 中是特定于服务器的)。”我正在使用JpaTransactionManager
    猜你喜欢
    • 1970-01-01
    • 2020-01-17
    • 1970-01-01
    • 2012-09-25
    • 1970-01-01
    • 2023-03-03
    • 2020-06-12
    • 1970-01-01
    • 2016-05-15
    相关资源
    最近更新 更多