Spring Boot Data JPA - 修改更新查询 - 刷新持久性上下文 [英] Spring Boot Data JPA - Modifying update query - Refresh persistence context

查看:1883
本文介绍了Spring Boot Data JPA - 修改更新查询 - 刷新持久性上下文的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Spring Boot 1.3.0.M4和MySQL数据库。

我在使用修改查询时遇到问题,EntityManager包含过时的实体查询已执行。



原始JPA储存库:



  public interface EmailRepository扩展了JpaRepository< Email,Long> {

@Transactional
@修改
@Query(更新Email e set e.active = false其中e.active = true且e.expire <= NOW() )
整数deactivateByExpired();

$ b

假设我们有 Email [id = 1,

执行完毕后:


pre class =lang-java prettyprint-override> emailRepository.save(email);
emailRepository.deactivateByExpired();
System.out.println(emailRepository.findOne(1L).isActive()); //打印真实!它应该打印出真正的






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


$ b

 公共接口EmailRepository扩展了JpaRepository< Email,Long> {

@Transactional
@Modifying(clearAutomatically = true)
@Query(update Email e set e.active = false where e.active = true and e.expire< ; = NOW())
整数deactivateByExpired();




$ b $ p
$ b

这种方法清除了持久性上下文而没有过时的值,但它会删除EntityManager中尚未处理的所有未刷新的更改。因为我只使用 save() 方法而不使用 saveAndFlush() 其他实体的某些更改已丢失:(b / b)




解决此问题的第二种方法: / h3>

  public interface EmailRepository扩展了JpaRepository< Email,Long> ;, EmailRepositoryCustom {

}

public interface EmailRepositoryCustom {

Integer deactivateByExpired();

}

公共类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.flus H();
整数结果= query.executeUpdate();
entityManager.clear();
返回结果;
}

}

这种方法类似于 @Modifying(clearAutomatically = true)但它首先强制EntityManager在执行更新之前刷新对数据库的所有更改,然后清除持久性上下文。这样就不会有过时的实体,所有的更改都会保存在数据库中。






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






2018年更新



Spring数据JPA批准了我的PR,在 @Modifying() flushAutomatically 选项>现在。


$ b

@Modifying(flushAutomatically = true,clearAutomatically = true)


解决方案

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



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

  @Query(SELECT * FROM Email e where e.active = true和e.expire <= NOW())
List< Email> findExpired();

然后遍历它们并更新值:

 (电子邮件电子邮件地址:findExpired()){
email.setActive(false);
}

现在hibernate知道所有的更改,并且如果事务是写入数据库的完成或者手动调用 EntityManager.flush()。我知道如果你有大量的数据条目,这将不会有效,因为你将所有的实体加载到内存中。但这是保持hibernate实体缓存,二级缓存和数据库同步的最好方法。



这个答案是否表示@ Modifying'注释是无用?没有!如果您确保修改的实体不在您的本地缓存中,例如只写应用程序,这种方法只是要走的路。



只是为了记录:你不需要 @Transactional



仅用于记录v2:活动列看起来像它直接依赖于 expire 。那么为什么不彻底删除 active ,并在每个查询中看看 expire


I'm working with Spring Boot 1.3.0.M4 and a MySQL database.

I have a problem when using modifying queries, the EntityManager contains outdated entities after the query has executed.

Original JPA Repository:

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();

}

Suppose we have Email [id=1, active=true, expire=2015/01/01] in DB.

After executing:

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


First approach to solve the problem: add clearAutomatically = true

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();

}

This approach clears the persistence context not to have outdated values, but it drops all non-flushed changes still pending in the EntityManager. As I use only save() methods and not saveAndFlush() some changes are lost for other entities :(


Second approach to solve the problem: custom implementation for repository

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;
    }

}

This approach works similar to @Modifying(clearAutomatically = true) but it first forces the EntityManager to flush all changes to DB before executing the update and then it clears the persistence context. This way there won't be outdated entities and all changes will be saved in DB.


I would like to know if there's a better way to execute update statements in JPA without having the issue of the outdated entities and without the manual flush to DB. Perhaps disabling the 2nd level cache? How can I do it in Spring Boot?


Update 2018

Spring Data JPA approved my PR, there's a flushAutomatically option in @Modifying() now.

@Modifying(flushAutomatically = true, clearAutomatically = true)

解决方案

I know this is not a direct answer to your question, since you already have built a fix and started a pull request on Github. Thank you for that!

But I would like to explain the JPA way you can go. So you would like to change all entities which match a specific criteria and update a value on each. The normal approach is just to load all needed entities:

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

Then iterate over them and update the values:

for (Email email : findExpired()) {
  email.setActive(false);
}

Now hibernate knows all changes and will write them to the database if the transaction is done or you call EntityManager.flush() manually. I know this won't work good if you have a big amount of data entries, since you load all entities into memory. But this is the best way, to keep the hibernate entity cache, 2nd level caches and the database in sync.

Does this answer say "the `@Modifying´ annotation is useless"? No! If you ensure the modified entities are not in your local cache e.g. write-only application, this approach is just the way to go.

And just for the record: you don't need @Transactional on your repository methods.

Just for the record v2: the active column looks as it has a direct dependency to expire. So why not delete active completely and look just on expire in every query?

这篇关于Spring Boot Data JPA - 修改更新查询 - 刷新持久性上下文的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆