0

I'm strugglingtrying to "refresh" an entity (Foo) that look like this:

public class Foo {

    //...
    @OneToMany(mappedBy = "fooRef", fetch = FetchType.LAZY)
    private List<Bar> bars;
}


public class Bar {

    //...
    @Column(name = "FOO_ID")
    private Long fooRef;
    @Column(name = "STATE")
    @Enumerated(EnumType.STRING)
    private State state;
}

As Spring JPA doesn't implement the refresh method, I just query the entity again. This is the code that it doesn't work and I dont know why:

for(int i=0; i<MAX_RETRIES; i++) {

    List<Bar> bars = foo.getBars();

    // check bars state

    if(someBarIsBad) {
        try {
            TimeUnit.SECONDS.sleep(delay);
        } catch (InterruptedException ignored) {

        }
    } else {
        break;
    }

    foo = fooRepo.findById(fooId);  // "refresh"
}

The goal is to continue retry until all bars have the desired state. The test I'm doing is placing a breakpoint at the last instruction of the loop, so when it break, I go the the table and change the state of all of the bars to the desired one to then exit the loop. The thing is that the new returned foo, the bars doesn't reflect the change I made.

I tried to remove the sleep, but that is not the problem. I think it must be something with the mapping.

Jens Schauder
  • 77,657
  • 34
  • 181
  • 348
anat0lius
  • 2,145
  • 6
  • 33
  • 60
  • I suggest that the problem is in `fetch = FetchType.LAZY`. The `bars` will be updated, when you tried to use them. Try to do something like this after refresh `for (Bar bar : foo.getBars()) { sout(bar);} `. I hope this will trigger lazy initialization. – Danila Zharenkov May 30 '18 at 09:11
  • I tested removing the LAZY fetching, but it is the same result. Anyway, that should not be problem as I'm retriving the `bars` on the same scope I retrieve `foo`. What do you mean with `sout` ? Is a jpa method? – anat0lius May 30 '18 at 09:27
  • nonono, `sout` is shortcut for `System.out.println()`. Removing fetch is incorrect, cause for `OneToMany` relation - default fetch type is LAZY. So this removing changes nothing. Try to change fetch from LAZY to EAGER. And yes, you are right - the scope the same, but difference is in direct and undirect using Bar entity table. – Danila Zharenkov May 30 '18 at 09:36
  • This is really strange. I tried with eagger fetch, nothing. I tried retriving directly `barRepo.findByFooRef` and neither. And I already assure I make the change correctly with commit. – anat0lius May 30 '18 at 10:35

1 Answers1

2

This is most likely because everything is running in a single transaction. This causes JPA to not reload the entity from the database, but to return the one it already has in the session.

Either make sure you are in a new transaction on every "refresh" or evict the entity from the session, so it has to get reloaded again.

Jens Schauder
  • 77,657
  • 34
  • 181
  • 348
  • Make sense. But I'm not sure how I should isolate the transaction. I taked `fooRepo.findById(fooId)` and wrapped it on a method annotated with `@Transactional`. Still doesn't return me an updated version. – anat0lius May 30 '18 at 13:07
  • The important part is that the code you showed in your question ISN'T wrapped in a method annotated with `@Transactional` – Jens Schauder May 30 '18 at 13:41
  • I did, both, the code above and `getFoo(ID)` are transactional. Still the same :( – anat0lius May 30 '18 at 13:49
  • 1
    Ok, I got it. I was not aware of Isolation levels. I anotated getFoo(ID) with `@Transactional(readOnly = true, isolation = Isolation.REPEATABLE_READ)` – anat0lius May 30 '18 at 14:35