Spring DATA JPA 更新後の値が取得できなくてはまった。

やりたいこと

DBにinsert→insertしたデータを他のアプリがupdate→update後の値を取得したい

結論から言うと、「EntityManagerのキャッシュクリア」で解決。

はまった内容

insertしたデータを他のアプリがupdateするが、update後の値が取得できない。
下のようにupdate後の値が取得できるまでwhileループで何度も検索して無限ループ!!!

MyController.java

public class MyController{
    @Autowired
    MyService service;

    @PostMapping(value = "/insert")
    public String insert(){
        Item item = new Item("test");
        // insert
        service.saveItem(item); 
        // 他のアプリが更新するまでselect繰り返す
        searchUpdatedItem(item.getId);
        
        return "index";
    }

    private void  searchUpdatedItem(Item item){
        Item result;
        do{
            result = service.findItem(item);
        }while(result.isUpdate == false);
    }
}

MyService.java

    @Autowired
    ItemRepository repository;
    
    @Transactional
    public void saveItem(Item item){
        repository.save(item);
    }

    @Transactional(readonly = true)
    public Item findItem(int itemId){
        return repository.getOne(Item itemId);
    }

結論

Entity Managerが自身で更新したデータをキャッシュに保存、他のアプリが更新したデータは管理外のため取得してなかったのが原因でした。whileの中でいくらselectしてもDBじゃなくキャッシュ内の古い情報しか参照してなかったと。

修正したこと

自分の場合は一時的にキャッシュクリアして対応。すべてのエンティティ/エンティティごとにキャッシュの使用有無も設定できるもよう。(参考文献参照ください)

MyService.java

    @Autowired
    ItemRepository repository;
    @Autowired
    EntityManager manager; // これ追加

    
    @Transactional
    public void saveItem(Item item){
        repository.save(item);
    }

    @Transactional(readonly = true)
    public Item findItem(int itemId){
        manager.clear(); // これ追加
        return repository.getOne(Item itemId);
    }

おわりに

Spring Boot使ってるとめっちゃ簡単に実装できるけど、だんだんやりたいことが深くなってくると、フレームワークの中身わかってなくて嵌まることが最近増えた。フレームワークが「気を遣ってこういう処理してくれる」みたいなナレッジもちゃんと勉強して増やしていかないといけないと思いました。

参考文献

メモリを逼迫させずにJPAで大量データを取得する方法 - エンタープライズギークス (Enterprise Geeks)
Java - Spring Data JPAでのキャッシュについて|teratail