【问题标题】:Spring JPA @Transactional data accessingSpring JPA @Transactional 数据访问
【发布时间】:2020-07-15 15:58:34
【问题描述】:

我在解析或处理我的 @Transational 方法之间的数据时遇到了问题。或者我对@Transactionl的理解有误。

这是我的场景

Method A

@Transactional
public void methodA {
   MyEntity e = myRepo.findByid(1).orElseThrow();

   e.setMyFlag(1123);

   methodB();

   myRepo.save(e);
}


@Transactional 
public void methodB {
    MyEntity e = myRepo.findByMyFlag(1123).orElseThrow(); <--- This throws Not Found Exception
    // do something 'e' entity that is having a myFlag = 1123
    // i am not modifying the entity just doing something externally 
    // using myFlag value 1123
}

我的理解是,methodB 正在使用methodA 中的会话。现在,鉴于它使用的是同一个会话,我可以在methodA 中获取尚未提交的数据集或尚未调用save()。如果我错了,请纠正我并指出正确的。另外,上面的场景是我想要实现的。

仅供参考,我还尝试在 save() 呼叫之后移动 methodB 呼叫。还是出现Not found异常

【问题讨论】:

  • 看起来不正确。由于它们在同一事务和同一会话中执行,因此休眠将在执行findByMyFlag(1123) 之前自动刷新(不提交)会话中的任何内容。 myRepo.findByMyFlag(1123)是如何实现的?
  • 只是一个普通的@Query(value="select * from my_table where my_flag = ?1", nativeSql=true)
  • 可能是hibernate没有在原生查询上刷新。为什么需要原生查询查询? SpringDataJPA 应该根据你的方法生成查询吧?findByMyFlag 方法名称似乎正确
  • 哦,是的,也通过spring-jpa尝试过,仍然无法访问数据
  • 只是为了排查问题,能不能在methodB();上面加上myRepo.flush(e);并删除myRepo.save(e);

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


【解决方案1】:

您是对的,它使用相同的会话并且是相同事务的一部分。您错误的地方是在实体中设置值(尚未保存)将其放置在执行另一个查询会找到它的任何位置。

换句话说,methodB 中的 findByMyFlag 仍然无法找到您刚刚修改的实体,因为您正在搜索的值尚未保存到数据库中。

另一方面,如果您在方法 B 中使用 findByid(1) 进行了搜索,那么您将找到具有该更改的同一实体。

【讨论】:

    【解决方案2】:

    在同一个类中,一个方法调用另一个注解的方法(如@Async、@Transational),注解不会生效

    【讨论】:

    • 两者在不同的类中methodA在ServiceA中methodB在ServiceB中
    • 原因:Spring 会在扫描 bean 时扫描 bean 的 @Transactional 注解。如果是这样,Spring 将为这个 bean 动态生成一个子类(即代理类)。代理类继承了原始 bean。这时候,被注解的方法被调用的时候,其实是被代理类调用的,
    • 并且代理类会在调用之前启动事务。但是如果被注解的方法被同一个类中的其他方法调用,那么方法调用就不会经过代理类,而是直接经过原来的bean,所以事务不会启动,我们看到的现象是@事务注释无效。
    【解决方案3】:

    只有从您添加的各种 cmet 中才能推断出答案,即您的问题本身未能提供所有相关信息。

    您注意到您查找的查询“只是一个普通的@Query(value="select * from my_table where my_flag = ?1", nativeSql=true)”。

    这就是您的问题:您正在执行 native sql 查询而不是 JPQL 查询。

    如果这是一个 JPQL 查询,那么同一事务中的任何未决更改都将被刷新到数据库(不提交,仅刷新),以便查询返回准确结果,即反映任何未决但尚未刷新到数据库的更改。

    更详细的解释请看这里:

    https://vladmihalcea.com/how-does-the-auto-flush-work-in-jpa-and-hibernate/

    所以要解决您的问题,请将您的查询更改为:

    @Query(value="select e from MyEntity e where e.myFlag = ?1") 即 JPQL 查询,它将触发待处理更改的刷新。

    即使将myRepo.save(e); 移动到对methodB() 的调用上方也无法修复它。您需要保存 刷新,以便本机查询找到它。

    这引出了一个问题,你为什么使用原生查询?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-01-06
      • 2018-06-27
      • 2020-02-03
      • 2014-12-24
      • 1970-01-01
      • 2013-11-12
      • 1970-01-01
      相关资源
      最近更新 更多