Spring数据:@Transactional和propogation

时间:2017-01-25 17:46:40

标签: java spring orm spring-data-jpa spring-transactions

我有以下代码:

public void method1(String id){
  Object object = repository.findOne(id);
  object.setState("running");
  repository.save(object);
  try{
    object2.method2(object); //This method takes 2 hours to complete
    object.setState("complete");
  }catch(Exception e){
    object.setState("failed");
  }
  repository.save(object);
}

因此,在调用需要数小时执行的方法之前,我将状态更改为“running”。我的对象是JPA Entity(包含懒惰的集合),method2()尝试加载所有链接的entities

现在,在method2,我正在

  

无法初始化代理 - 没有会话

错误,因为它超出了事务(预期的行为)。为了防止这种情况,有两种解决方案:

  • 使用method1注释@Transactional。这将解决它,但是,在方法执行完成之前,state将不会反映到其他事务。
  • 更改实体配置中的fetch mode并将其设为Eager。这也可以解决它,但我不希望每次eager取出。

还有其他方法可以让它发挥作用吗?

2 个答案:

答案 0 :(得分:1)

这个怎么样:

选项1

1)为状态更改创建服务方法,如下所示:

@Transactional( propagation = Propagation.REQUIRES_NEW)
public void changeStatusInNewTransaction(String id, String status){
  Object object = repository.findOne(id);
  object.setState(status);
  repository.save(object);
}

2)按如下方式更改原始方法:

@Autowired
Service service;

@Transactional
public void method1(String id){
  service.changeStatusInNewTransaction(id, "running");
  Object object = repository.findOne(id);
  try{
    object2.method2(object); //This method takes 2 hours to complete
    object.setState("complete");
  }catch(Exception e){
    object.setState("failed");
  }
  repository.save(object);
}

由于这个设置,一切都可以在一个@Transactional方法下运行,但是当状态要改为'运行'然后:

  • 当前交易将暂停
  • 将创建新的
  • 状态将被更改并且事务已提交
  • 父事务将继续,您可以使用大型操作进行处理,而不会遇到其他用户在2小时内看不到状态更改的问题。

选项2

1)为状态更改创建服务方法,如下所示:

@Transactional
public void changeStatusInNewTransaction(String id, String status){
  Object object = repository.findOne(id);
  object.setState(status);
  repository.save(object);
}

2)仅为长时间处理创建交易方法

@Transactional
public void performLongProcessing(String id){
    Object object = repository.findOne(id);
    object2.method2(object); //This method takes 2 hours to complete
    object.setState("complete");
    repository.save(objects;
}

3)标记主要方法,无需交易即可运行:

@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void method1(String id){
  service.changeStatusInNewTransaction(id, "running");

  try{
    service.performLongProcessing(id);
  }catch(Exception e){
    service.changeStatusInNewTransaction(id, "failed");
  }

}

答案 1 :(得分:1)

围绕一个执行了几个小时的方法的事务,似乎是一个设计错误,所以method1()不应该有@Transactional!当您启动事务时,您需要一个连接,并且此连接将在整个持续时间内从您的连接池中分配,这极大地限制了可伸缩性(以及您的DBA的生命)。

  

无法初始化代理 - 没有会话

您收到此错误是因为(在method1上没有@Transactional)您的实体在调用repository.save()后被分离,并且您无法加载延迟集合。一个快速解决方案是将EntityManager注入object2并在EntityManager.refresh()内调用method2()这不需要事务,因为您只是在读取数据。

没有理由使用任何类型的事务传播来解决此问题。