Donosor Nabuco
Donosor Nabuco

Reputation: 23

Hibernate create notification when retrieved objects are externally changed?

Suppose i have retrieved object A from the database and now it is in a detached state. The row in the database that corresponds to the object A is externally updated. Is there any way of notifying my application that object A is no longer up to date and perhaps do an automatic refresh? Maybe a trigger on the database that could create an event for Hibernate?

Upvotes: 2

Views: 2387

Answers (2)

Mike Nakis
Mike Nakis

Reputation: 61969

Hibernate has no support in for this scenario because it is meant to be used in client-server applications where there exists only one instance of hibernate, running on the server, and having exclusive access to the database.

Hibernate is supposed to provide persistence for the server application, and clients are supposed to talk to that server application instead of directly to the database, so all updates are initiated by the server application, and they go through a single instance of hibernate to the database, so there can be no external updates, and the one and only instance of hibernate has complete control over everything.

Of course, using the database as a server is tempting, because it may save you from having to develop your own client-server software. In this scenario, every client would run its own instance of hibernate, and each of these instances would talk directly to the database. Then, the problem is that you have to somehow convince each instance of hibernate to see the changes made to the database by other instances. This might seem like a daunting task, but then again the savings are huge, (not having to write your own client-server software, with your own communication protocol between them,) so it might be worth the try, right?

Well, I have implemented an application this way, and I have come to regret it: it is clunky, buggy, and has very bad performance.

But if you really want to also give it a try, here is how to do it:

Each client will need to record each change that it makes by adding rows to a "change_log" table, and it will need to detect changes made by other clients by looking for new records appearing in the change log table.

For change recording, use a hibernate interceptor to detect changes made by the local client to the database, and record each change in the change log table. Each row of the change log will need to have an auto-incrementing id, the name of the class of the changed entity, the id (database key) of the entity, and the type of the event: added, modified, or deleted. (Of course you don't care about the fourth type of event supported by the interceptor, row loaded.) Note that you will have to use good old JDBC for this, because hibernate cannot be re-entered from within an interceptor callback.

For change detection, run a separate thread which polls the change log table for changes. All it needs to do is remember the id of the last change log row it has seen, and keep asking the database if there are any rows with an id higher than that. (Do not use client-issued timestamps for this, because the inevitable minor differences in the clocks of the clients will cause havoc. Database-issued timestamps might work, but using an integer id which is auto-incremented by the database is simpler.) Of course, you will need to use JDBC here too, because hibernate would never tell you that any rows have been added to the change log: remember, hibernate is under the impression that the entire database belongs to it, so it does not consider the possibility that someone else may have added rows to it. This is actually the problem that we are trying to solve here.

The change detection thread should generate events and somehow pass them to your main thread, where hibernate lives. This means that your main thread has to have some event loop running, or some ticking mechanism being invoked regularly. Once your main thread receives a change detection event, it takes action based on the type of the event: when an entity is added, it tells hibernate to load it, when a record is deleted it tells hibernate to forget it, and when a record is updated it tells hibernate to refresh it.

Personally, I have not been able to convince hibernate to refresh a single entity, so I have been completely flushing hibernate on every single change detection, which has resulted in very poor performance. I will be fixing this in the month to come, and I fear that the way to fix it will be to get rid of hibernate.

Upvotes: 4

Ryan Stewart
Ryan Stewart

Reputation: 128799

I don't think there's anything in Hibernate that will help you much with this, so it would just be whatever you can come up with. You'll have to keep track of all the instances and where they are so you know where to send events. You could use a Hibernate Interceptor or Listener to find out when a particular entity is updated.

The typical way of handling conflicting updates is with optimistic locking based on version checking and is discussed extensively under "Optimistic concurrency control" in the Hibernate reference guide.

Upvotes: 1

Related Questions