Spring数据:@Transactional和传播

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring数据:@Transactional和传播相关的知识,希望对你有一定的参考价值。

我有以下代码:

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(懒惰的loded集合)和method2()尝试加载所有链接的entities

现在,在method2,我得到了

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

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

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

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

答案

这个怎么样:

选项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方法下运行,但是当状态要改为'running'时,那么:

  • 当前交易将被暂停
  • 将创建一个新的
  • 国家将被改变和交易承诺
  • 父事务将继续,您可以使用您的大型操作进行处理,而不会出现其他用户在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");
  }

}
另一答案

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

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

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

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

以上是关于Spring数据:@Transactional和传播的主要内容,如果未能解决你的问题,请参考以下文章

Spring @Transactional 与跨多个数据源的事务

Spring——事务注解@Transactional建议收藏

Spring @Transactional 隔离传播

Spring数据:@Transactional和传播

Spring @Transactional,具有跨多个数据源的事务

Spring @Transactional ——事务回滚