【问题标题】:How to force commit entity in @Transactional mode如何在@Transactional 模式下强制提交实体
【发布时间】:2018-06-02 06:38:32
【问题描述】:

我正在使用 Spring 数据 jpa 和 jdbc(使用 entityManager.unwrap(Session.class) )连接。
我的请求通过 3 种方法流动。第一 -> 第二 -> 第三。
第一个和第三个用@Transactional 注释。
在第一种和第二种方法中通过 JPA CRUD 存储库保存实体。
在第三种方法中,使用基本的 jdbc 查询来检索保存在第二种方法中的值。
正如我们所知,@Transaction 实体在 JPA 提交事务之前不会提交到数据库。
我在第二种方法中也使用了 saveAndFlush,但在方法 3 中使用 jdbc 查询时看不到检索更新的值。

第一种方法 - update()

@Transactional
@Override
public ApiResponse update(RequestInput input) {
    ApiResponse response = doRestOfWork(input);   // calling 2nd method
    // .... some other entity save....
}

第二种方法 - doRestOfWork():设置 status=true 并调用 saveAndFlush 方法

@Override
public ApiResponse doRestOfWork(Request request) {
    Insight insight = insightRepository.findOne(request.getTypeId());
    insight.setStatus(true);
    insightRepository.saveAndFlush(insight);

    processOperation();    // calling 3rd method
}

第三种方法 - processOperation():通过 jdbc 连接检索更新的状态值。

@Transactional
public void processOperation() {
Connection conn = null;
        SessionImplementor sessionImpl = (SessionImplementor) entityManager.unwrap(Session.class);
        try {
            conn = sessionImpl.getJdbcConnectionAccess().obtainConnection();
            Connection conn = null;
            String stmt = "SELECT status from insight";
            PreparedStatement ps = conn.prepareStatement(stmt);
            ResultSet rs = ps.executeQuery();
            while (rs.next()) {
               boolean value = rs.getBoolean(status);   // returning false, i want this to return true as already set true in 2nd method and called saveAndFlush 
        }
    } catch (SQLException ex) {
    } finally {
        JdbcUtility.closeResources(conn, ps, rs);
    }
}

InsightRepository 正在扩展 JpaRepository

@Repository
public interface InsightRepository extends JpaRepository<Insight, Long> {}

我想在方法 3 中更新状态值(布尔值 true - 在方法 2 中更新)。 如何做到这一点?

更新

我搜索了很多,不认为如果一个方法用 @Transactional 注释,那么你可以在完成 JPA 事务之前提交更改。所以解决办法是去掉@Transactional注解,如果想控制JPA事务,使用entityManager.getTransaction()。

【问题讨论】:

  • 为什么不将第二个方法注释为@Transactional,而不是第三个?只做select 不需要显式事务。另外:我怀疑saveAndFlush 提交了更改。
  • 因为方法 3 有其他获取和更新 JPA 事务。为简单起见,我只添加了一个选择操作。 saveAndFlush 没有提交事务。这就是我的问题是如何在第二种方法中提交更改。
  • 您可以同时注释两者。第三种方法会超过第一个交易。
  • 我搜索了很多,不认为如果一个方法用 Transactional 注释,那么你可以在完成 JPA 事务之前提交更改。所以解决办法是要么去掉 Transactional 注解,要么使用 entityManager.getTransaction() 来控制 JPA 事务。
  • 如果方法 3 使用与方法 2 相同的事务,尽管更新尚未提交,但它会看到更新。如果您真的想要在进入方法 3 之前提交,而不是将方法 2 的更新代码提取到自己的子方法中,该子方法被注释为 Transactional。

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


【解决方案1】:

在不同于第二个方法的 bean 中创建一个用于更新的方法(第四个方法)@Transactional(propagation = Propagation.REQUIRES_NEW) 注释,并将第二个方法更改为调用第四个方法,如下所示:

@Component //or Service etc..
public class AnotherBean{

  @Transactional(propagation = Propagation.REQUIRES_NEW)
  public void doUpdateWork(Request request){
    Insight insight = insightRepository.findOne(request.getTypeId());
    insight.setStatus(true);
    insightRepository.save(insight);
  }
} 

    @Override
    public ApiResponse doRestOfWork(Request request) {
        anotherBean.doUpdateWork(request);
        processOperation();    // calling 3rd method
    }

【讨论】:

  • 我不确定它是否会起作用,因为从文档中 Propagation.REQUIRES_NEW 只是暂停(而不是提交)当前事务并创建一个新事务。我认为您应该添加的是获取实体管理器实例并在“父”事务方法中提交它
  • 根据规范,这应该是可行的。但它不起作用。
  • @Aladin ,带有 REQUIRES_NEW 的方法使用自己的事务提交其操作,它不等待父事务的提交。关键是 REQUIRES_NEW 传播的方法必须在另一个 bean 中,because of spring proxies
【解决方案2】:

如果你想控制事务,不要使用@Transactional

em; //entityManager

Transaction tx=em.getTransaction();
try{
   tx.begin();
   //do work
   tx.commit(); // comminted transaction 
 // start another transaction if you need to, doo more work etc.
...
}

这是使用 JPA 进行原子操作的最基本和最基本的代码块。

也不需要unwrap实体管理器,使用纯JPA。通过展开,您将当前实现绑定到 JPA 提供者,这与 JPA 独立于底层 JPA 实现提供者的想法相反。换句话说,如果由于某些原因 Spring 将其 JPA 实现从 Hibrnate 更改为 eg,它将停止工作。 Eclipse 链接。

【讨论】:

  • 但我必须同时使用 JPA 和纯 jdbc 连接(用于复杂的连接查询)。当我通过方法 3 中的 prepareStatement 获取值时,我需要通过 JPA 事务(尚未提交)在方法 2 中保存和刷新的更新状态值。你是说如果我删除 @Transactional 注释然后更新的值将反映在 SELECT 查询中?
  • 我说如果您需要控制交易流程,请不要使用@Transactional 并像上面显示的那样手动进行。我怀疑您需要使用 jdbc 连接,因为您可以使用 JPA 执行本机查询。
  • 当我们尝试这个时,Spring 会抛出异常。 (v.5.2.2)
猜你喜欢
  • 2014-08-11
  • 2014-05-17
  • 2015-11-28
  • 1970-01-01
  • 2012-12-25
  • 1970-01-01
  • 2016-02-05
  • 2018-03-20
  • 2014-04-22
相关资源
最近更新 更多