2

I have a common database that is used by two different applications (different technologies, different deployment servers, they just use the same database).

Let's call them application #1 and application #2.

Suppose we have the following scenario:

  • the database contains a table called items (doesn't matter its content)
  • application #2 is developed in Spring Boot and it is mainly used just for reading data from the database
  • application #2 retrieves an item from the database
  • application #1 changes that item
  • application #2 retrieves the same item again, but the changes are not visible

What I understood by reading a lot of articles:

  • when application #2 retrieves the item, Hibernate stores it in the first level cache
  • the changes that are done to the item by application #1 are external changes and Hibernate is unaware of them, and thus, the cache is not updated (same happens when you do a manual change in the database)
  • you cannot disable Hibernate's first level cache.

So, my question is, can you force Hibernate into refreshing the entities every time they are read (or make it go into the database) without explicitly calling em.refresh(entity)? The problem is that the business logic module from application1 is used as a dependency in application1 so I can only call service methods (i.e. I don't have access to the entityManager or session references).

  • Maybe if you explain your main problem, probably concurrency in a specific case, we could propose a different solution than using a workaround for the L1 cache. I know we all want to apply the solution we have in mind, but you may rething your problem, it's called the XY problem see https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem – pdem Jul 05 '19 at 09:13

4 Answers4

1

Hibernate L1 cache is roughly equivalent to a DB transaction when you run in a repeatable-read level isolation. Basically, if you read/write some data, the next time you query in the context of the same session, you will get the same data. Further, within the same process, sessions run independent of each other, which means 2 session are looking at different data in the L1 cache.

If you use repeatable read or less, then you shouldn't really be concerned about the L1 cache, as you might run into this scenario regardless of the ORM (or no ORM).

I think you only need to think about the L2 cache here. The L2 cache is what stores data and assumes only hibernate is accessing the DB, which means that if some change happens in the DB, hibernate might not know about it. If you just disable the L2 cache, you are sorted.

Further reading - Short description of hibernate cache levels

Aditya
  • 2,148
  • 3
  • 21
  • 34
  • I don't use Hibernate L2 caching and I don't need it. My problem is with L1 cache, because if I restart the Spring Boot application and make the request for that entity again, it brings the correct data. – Ciprian Stan Jul 05 '19 at 19:15
  • hibernate comes bundled with ehcache, and IIRC, it is enabled by default. Try calling a clear cache between 2 sessions to see if you get the behaviour you are expecting. Example of how to do it - https://stackoverflow.com/questions/38358664/how-can-i-evict-all-cache-in-spring-boot – Aditya Jul 08 '19 at 03:08
  • In the above example, just take the cacheManager and call clear on all the caches – Aditya Jul 08 '19 at 03:09
  • This is not working. All the documentation states that Hibernate L2 cache is disabled by default and besides that, in order to enable it you need a provider, which in turn needs other dependencies as well (dependencies which I don't have imported). Also, the cache manager approach is not working as well since I don't have any cache modules imported. – Ciprian Stan Jul 15 '19 at 15:04
  • Hibernate comes bundled with EHCache. You don't need to import it independently. – Aditya Jul 16 '19 at 07:18
0

Well, if you cannot access hibernate session you are left with nothing. Any operations you want to do requires session access. For instance you can remove entity from cache after reading it like this:

session.evict(entity);

or this

session.clear();

but first and foremost you need a session. Since you calling only services you need to create service endpoints clearing session cache after serving them or modify existing endpoints to do that.

smoczyna
  • 489
  • 6
  • 18
  • I tried clearing the session, but the problem is that within my service I obtain a Session reference, and when I debug and step into the depths of Hibernate, the Session reference there is different. The result is that I am not clearing the proper session... – Ciprian Stan Jul 17 '19 at 10:46
  • At the end of my answer I suggested you to clear the session in back-end instead. You can add session.evict or session.clear at the end of the call (after the response is built) or do this in the interceptor or so – smoczyna Jul 18 '19 at 07:15
0

You can try to use StatelessSession, but you will lose cascading and other things.

https://docs.jboss.org/hibernate/orm/current/userguide/html_single/Hibernate_User_Guide.html#_statelesssession

https://stackoverflow.com/a/48978736/3405171

v.ladynev
  • 19,275
  • 8
  • 46
  • 67
0

You can force to start a new transaction, so in this manner hibernate will not be read from the cache and it will redo the read from the db.

You can annotate your function in this manner

@Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)

Requesting a new transaction, the system will generation a new hibernate session, so the data will not be in the cache.

balax85
  • 36
  • 2