Spring Boot Data JPA-修改更新查询-刷新持久性上下文

我正在使用Spring Boot" title="Spring Boot">Spring Boot 1.3.0.M4和一个MySQL数据库。

使用修改查询时出现问题,执行查询后,EntityManager包含过时的实体。

原始JPA存储库:

public interface EmailRepository extends JpaRepository<Email, Long> {

@Transactional

@Modifying

@Query("update Email e set e.active = false where e.active = true and e.expire <= NOW()")

Integer deactivateByExpired();

}

假设我们在数据库中有 电子邮件[id = 1,active = true,expire = 2015/01/01]

执行后:

emailRepository.save(email);

emailRepository.deactivateByExpired();

System.out.println(emailRepository.findOne(1L).isActive()); // prints true!! it should print false


解决问题的第一种方法:添加

public interface EmailRepository extends JpaRepository<Email, Long> {

@Transactional

@Modifying(clearAutomatically = true)

@Query("update Email e set e.active = false where e.active = true and e.expire <= NOW()")

Integer deactivateByExpired();

}

此方法清除了持久性上下文不具有过时的值,但是将所有未刷新的更改都丢弃在EntityManager中,这些更改仍未处理。由于我仅使用 save()

方法, saveAndFlush() 其他实体没有丢失一些更改:(


解决问题的第二种方法:仓库的自定义实现

public interface EmailRepository extends JpaRepository<Email, Long>, EmailRepositoryCustom {

}

public interface EmailRepositoryCustom {

Integer deactivateByExpired();

}

public class EmailRepositoryImpl implements EmailRepositoryCustom {

@PersistenceContext

private EntityManager entityManager;

@Transactional

@Override

public Integer deactivateByExpired() {

String hsql = "update Email e set e.active = false where e.active = true and e.expire <= NOW()";

Query query = entityManager.createQuery(hsql);

entityManager.flush();

Integer result = query.executeUpdate();

entityManager.clear();

return result;

}

}

这种方法的工作原理@Modifying(clearAutomatically =

true)与之类似,但是它首先强制EntityManager在执行更新之前将所有更改刷新到DB,然后清除持久性上下文。这样,就不会有过时的实体,所有更改都将保存在数据库中。


我想知道是否有更好的方法可以在JPA中执行更新语句,而不会出现过时的实体问题,也无需手动刷新到DB。也许禁用二级缓存?如何在Spring

Boot中做到这一点?


Spring Data JPA批准了我的PR,现在有一个flushAutomatically选择@Modifying()

@Modifying(flushAutomatically = true, clearAutomatically = true)

回答:

我知道这不是您问题的直接答案,因为您已经构建了修复程序并在Github上启动了pull请求。谢谢你!

但是我想解释一下您可以使用的JPA方法。因此,您想更改符合特定条件的所有实体,并更新每个实体的值。通常的方法是加载所有需要的实体:

@Query("SELECT * FROM Email e where e.active = true and e.expire <= NOW()")

List<Email> findExpired();

然后遍历它们并更新值:

for (Email email : findExpired()) {

email.setActive(false);

}

现在,hibernate知道所有更改,如果事务完成或您EntityManager.flush()手动调用,它们会将它们写入数据库。我知道如果您有大量的数据条目,这将无法正常工作,因为您将所有实体都加载到了内存中。但这是使hibernate实体高速缓存,第二级高速缓存和数据库保持同步的最佳方法。

这个答案是否说“ @Modifying”注释没有用?没有!如果您确保修改后的实体不在本地缓存中(例如,只写应用程序),则此方法只是解决之道。

仅作记录:您不需要@Transactional存储库方法。

仅用于记录v2:该active列看起来像直接与相关expire。那么,为什么不active完全删除并仅expire在每个查询中查找呢?

以上是 Spring Boot Data JPA-修改更新查询-刷新持久性上下文 的全部内容, 来源链接: utcz.com/qa/431502.html

回到顶部