1

I know the title is quite confusing and so is the question. I was working on spring boot with the JPA repository and some unusual behaviour happened.

I made a repository call to get an object, let's say obj1 and I made some changes on it and then I passed obj1 to another function funcInSameClass(obj1) which is in the same class and from that function I passed the obj1 to a function funcInAutowiredClass1(obj) of an autowired component AutowiredClass1, from that component I passed the Id of obj1 to another function funcInAutowiredClass2(obj1.id) of autowired component AutowiredClass2. And in funcInAutowiredClass2(obj1.id) I made a repository call to get the same object as obj1 and make some changes in obj1 and saveAndFlush to the database.

class MainClass {

  @Autowired
  AutowiredClass1 autowiredClass1;

  public void main() {
    //inital object after first repository call
    Object obj1 = repo.findById(id);
    //make some changes in obj1
    obj1 = repo.saveAndFlush(obj1);
    funcInTheSameClass(obj1);
    repo.saveAndFlush(obj1)
    log.info(obj1); //here also obj1 name is now XYZ
  }
  void funcInTheSameClass(Object obj1) {
    autowiredClass1.funcInAutowiredClass1(obj1);
    log.info(obj1); //here obj1 name is now XYZ
  }
}

class AutowiredClass1 {
  @Autowired
  AutowiredClass2 autowiredClass2;
  public void funcInAutowiredClasss1(Object obj1) {
    autowiredClass2.funcInAutowiredClass2(obj1.id);
    log.info(obj1); // now obj1 name is now XYZ
  }
}

class AutowiredClass2 {
  public void funcInAutowiredClasss2(int id) {
    Object obj2 = repo.findById(id);
    obj2.setName("XYZ");
    repo.saveAndFlush(obj2);
    log.info(obj2); // here I set the obj2 name as "XYZ"
  }
}

You can see that in the funcInAutowiredClass2, reference of obj1 is not passed but a fresh entity is fetched from the repository and updated and saved. Then how come the changes reflect back in main function().

java user
  • 67
  • 1
  • 9
  • https://vladmihalcea.com/jpa-hibernate-first-level-cache/ take a look at this article, that's how first level cache works in Hibernate – Danila Zharenkov Jul 16 '21 at 11:33
  • @DanilaZharenkov Can you explain how this caching is done here by taking an example from question? – java user Jul 16 '21 at 11:36
  • After the first call `Object obj1 = repo.findById(id);` your entrity exists in PersistenceContext(L1 cache). After each flush - Hibernate synchronize entity state in Persistence Context with entity state in DB. On the second call of `findById` Hibernate checks cache first. Entity with such ID is already there, so Hibernate grabs cached entrity. Also important thing - Hibernate uses proxies for entities, your obj1 and obj2 are proxies for of the same entity in Persistence Context. That's why obj1 reflects ALL changes made with the entity – Danila Zharenkov Jul 16 '21 at 11:44
  • @DanilaZharenkov Thanks for detailed explaination. Checkout this question, in the case 1 why isn't this scenario applicable there? https://stackoverflow.com/questions/68406919/how-does-an-existing-object-retrieve-its-changes-once-we-update-it-somewhere-el – java user Jul 16 '21 at 11:58

1 Answers1

0

Hibernate have transaction level cache. When you fetch items from db it checks the cache. If it contains entity you fetching such entity will be returned.

Little example to demonstrate.

@Transactional
public void someMethod() {
    MyEntity e1 = repository.findById(id);

    // Some other code

    MyEntity e2 = repository.findById(id);

    // Here e1==e2
    // So if you
    e2.setX(1);
    // you will see that
    assert e1.getX() == 1;
    // is true
}
talex
  • 17,973
  • 3
  • 29
  • 66
  • But how does that make changes reflect back in another class after ```saveAndFlush()```, as in this question, It's understandable that after first ```saveAndFlush()``` the entry is stored in cache and when we use ```findById``` for second time the entry from cache is returned but when we make changes in ```obj2``` and use ```saveAndFlush``` then how are these changes reflected back in ```obj1``` in the starting class? We didn't use ```findById``` after making changes for ```obj2```. – java user Jul 16 '21 at 11:51
  • I added example, hope it helps. – talex Jul 16 '21 at 12:06