Spring Hibernate best way to handle modifying queries in a session - java

The issue:
Within a session if we have to use a modifying query such as an update, that change won't be visible within the session scope. One way to make it visible is to set the modifying query to clearAutomatically=true in the #Modifying annotation. The problem is that this change will effect all the changes in the same session, i.e. objects that were already save previous to that call will be voided and the changed won't be persisted once the transaction closes.
My question is: what's the best way to deal with this issue when clearAutomatically is not an option?

Depending on the Hibernate Session FLUSHMODE, your persistent objects may be synchronized with the persistence store before a query is executed by performing a flush() operation on the Session. The flush process synchronizes database state with session state by detecting state changes and executing SQL statements, but it does not commit the transaction.
The default FLUSHMODE is AUTO, which is described as :
The Session is sometimes flushed before query execution in order to ensure that queries never return stale state. This is the default flush mode. If you are getting stale data returned in your queries, flushing the Session manually should resolve your issue.

Related

What is exact purpose of flush in JPA

Some confusing explanation:
flush(); Flushing is the process of synchronizing the underlying persistent store with persistable state held in memory.it will update or insert into your tables in the running transaction, but it may not commit those changes.
If the changes are anyways going to be persisted in the database only after the commit then why to flush in the middle of the code.
And after running the flush if any changes are made to the managed object then will that throw an Exception or will those get synchronized and then will get perisisted. If they get synchronized then again why flush in the first place.
In theory, you (as a user of JPA) should never (or in absolutely rare situations) get in a situation to call flush().
Flushing is the process of synchronizing the underlying persistent
store with persistable state held in memory
In other words, on a flush() all the insert, update, delete or whatever statements are actually called on the database, before a flush() nothing happens on your database. Flushing is caused by a commit of your transaction or some kinds of database reads. For example if you execute a JPQL query, a flush() has to be done to get the correct results from the database. But this is just very nice to know and completely handled by your JPA implementation.
There may be some situations you want to control this flushing on your own and then you can invoke it with flush().
Edit to answer the questions in comment:
Not on every read a flush is necessary, consider this scenario (one transaction):
Read a person Person p = em.find(Person.class, 234)
Update person p.setAge(31)
Read a building Building b = em.find(Building.class, 123
Read a building with JPQL query select b from Building b where b.id = 123
Automatic flush occurs only before 4., because Eclipselink can't determine what you are gonna read, so the person's age must be up to date on the database before this read can occur. Before 3. there is no flush needed because Eclipselink knows that the update on a person can not affect a building.
To work with optimistic locking, you have to implement it. Read about the #Version annotation here: https://blogs.oracle.com/carolmcdonald/entry/jpa_2_0_concurrency_and. Without that your entity will not use optimistic locking and the "last update wins".
When the transaction commits the entity manager does that flush-ing for you. In some case, like handling optimistic locking in a container-managed transaction, you may need to manually invoke the flush method to catch and handle specific locking exception.

Which FlushMode in Hibernate commits when you call session.flush()?

I read that whether a call to session.flush() commits or not depends on the FlushMode that is set. However, I don't know which FlushMode does this. I want the session to be flushed and commited. Which FlushMode should I use?
It's the other way around. you can flush when calling commit. These are the flushmodes in Hibernate: FlushModes
ALWAYS
The Session is flushed before every query.
AUTO
The Session is sometimes flushed before query execution in order to ensure that queries never return stale state.
COMMIT
The Session is flushed when Transaction.commit() is called.
MANUAL
The Session is only ever flushed when Session.flush() is explicitly called by the application.
I think you are looking for AUTO. So the session is flushed on Commit
I'd say vice versa - whether Hibernate flushes or not on commit depends on FlushMode.

Hibernate false negative read

I have a web application that consists of a web service with two operations: createA and createB. An handler is registered for the endpoint. This handler opens a Session and start a transaction when the request is received. Then the code of the requested operation is executed. Before the response is sent back, the transaction is committed and the session is closed.
The code of createA consists of creating an entity of type A and persisting it using Session.save() method. In DEBUG mode, after Session.save() is called, I can see that there is one insertion in the ActionQueue of the session.
The code of createB consists of :
retrieving the previously created entity of type A
creating an Entity B that references the instance of A (B has a property that represents an associated A)
updating A to reference the new instance of B
call Session.save() for the new instance of B
call Session.update() for the new modified instance of A
However, in DEBUG mode, after calling Session.save() and Session.update(), the ActionQueue of the corresponding Session is empty. But, after the transaction commits, I can see the created entity in the database.
Operation createA and createB are invoked in this order without DEBUG. An error appears during the execution of the create B when it tries to retrieve the instance of A previously created using a criteria and the Session.list() method. The problem is that the instance of A is not found.
However, if I repeat the same sequence of operations in DEBUG or using Thread.sleep(15s) between invocations of the two operations, the instance of A can be found.
Thanks
EDIT: I forgot to precise that it works on certain machines but not on others. And I don't see any differences between these machines.
If you use the same Hibernate session for both createA and createB, then it'll work. You can store the Hibernate session in the Http session for achieving this (pay attention to synchronize the access to the session object, as requests from the same browser session can com in different threads).
Your problem is, Hibernate opens a new database connection for every session. Now your database seems not to synchronize the statements. It can happen in the database the select arrives before the insert is finished. Then it just depends of the speed of the involved computers if this condition happens or not. With the debug mode or the sleep() you make one computer slower so you don't have the problem any more.
If you want to continue with two different sessions for these two procedures, you can
look for the transaction mode of your database. Some databases have a dirty read where no correct locking or synchronization is done. Check if you accidentally used such a mode.
Check the JDBC parameters (they can be used in the hibernate connection.url) if there are parameters for your database which change timing and synchronization.
Check your connection pool (for the case you're using one).
The problem is that Hibernate does not save the entity to the database when you call Session.save(). It simply prepares the statement for execution later. This happens when you the transaction ends or when the you flush the session.
Your call to B is probably happening sometimes before the transaction ends for the A request. That is why it works if you wait a little while.
Try adding session.flush() after the save call. This will force Hibernate to persist the changes to the DB.

Will Hibernate flush my updated persistent object when calling session.close() with FlushMode.AUTO?

If FlushMode.AUTO is set, will Hibernate flush my updated persistent object when I call session.close()?
I know that session.close() does not normally flush the session but I'm not sure how FlushMode.AUTO affects this.
From the Docs:
FlushMode.AUTO
The Session is sometimes flushed before query execution in order to ensure that queries never return stale state. This is the default flush mode.
Does this mean I can rely on Hibernate to verify my changes are flushed sometimes before my session is closed?
Small code example:
Session session = HibernateSessionFactory.getSession();
PersistedObject p = session.get(PersistedObject.class,id);
p.setSomeProperty(newValue);
session.close();
UPDATE
According to the docs these are the places where the session will flush (when AUTO is used)
before some query executions
from org.hibernate.Transaction.commit()
from Session.flush()
This does not say anything about Session.close()
Will Hibernate flush my updated persistent object when calling session.close() (using FlushMode.AUTO)?
No it won't, and you should use a transaction with well defined boundaries. Quoting Non-transactional data access and the auto-commit mode:
Working nontransactionally with Hibernate
Look at the following code, which
accesses the database without
transaction boundaries:
Session session = sessionFactory.openSession();
session.get(Item.class, 123l);
session.close();
By default, in a Java SE environment
with a JDBC configuration, this is
what happens if you execute this
snippet:
A new Session is opened. It doesn’t obtain a database connection at this
point.
The call to get() triggers an SQL SELECT. The Session now obtains a JDBC
Connection from the connection pool.
Hibernate, by default, immediately
turns off the autocommit mode on this
connection with setAutoCommit(false).
This effectively starts a JDBC
transaction!
The SELECT is executed inside this JDBC transaction. The Session is
closed, and the connection is returned
to the pool and released by Hibernate
— Hibernate calls close() on the JDBC
Connection. What happens to the
uncommitted transaction?
The answer to that question is, “It
depends!” The JDBC specification
doesn’t say anything about pending
transactions when close() is called on
a connection. What happens depends on
how the vendors implement the
specification. With Oracle JDBC
drivers, for example, the call to
close() commits the transaction! Most
other JDBC vendors take the sane route
and roll back any pending transaction
when the JDBC Connection object is
closed and the resource is returned to
the pool.
Obviously, this won’t be a problem for
the SELECT you’ve executed, but look
at this variation:
Session session = getSessionFactory().openSession();
Long generatedId = session.save(item);
session.close();
This code results in an INSERT
statement, executed inside a
transaction that is never committed or
rolled back. On Oracle, this piece of
code inserts data permanently; in
other databases, it may not. (This
situation is slightly more
complicated: The INSERT is executed
only if the identifier generator
requires it. For example, an
identifier value can be obtained from
a sequence without an INSERT. The
persistent entity is then queued until
flush-time insertion — which never
happens in this code. An identity
strategy requires an immediate INSERT
for the value to be generated.)
Bottom line: use explicit transaction demarcation.
closing a session will always flush al work to the database. Flushmode.AUTO flushes work to the database when there are changes and you are quering the table with changed records.

What's the use of session.flush() in Hibernate

When we are updating a record, we can use session.flush() with Hibernate. What's the need for flush()?
Flushing the session forces Hibernate to synchronize the in-memory state of the Session with the database (i.e. to write changes to the database). By default, Hibernate will flush changes automatically for you:
before some query executions
when a transaction is committed
Allowing to explicitly flush the Session gives finer control that may be required in some circumstances (to get an ID assigned, to control the size of the Session,...).
As rightly said in above answers, by calling flush() we force hibernate to execute the SQL commands on Database. But do understand that changes are not "committed" yet.
So after doing flush and before doing commit, if you access DB directly (say from SQL prompt) and check the modified rows, you will NOT see the changes.
This is same as opening 2 SQL command sessions. And changes done in 1 session are not visible to others until committed.
I only know that when we call session.flush() our statements are execute in database but not committed.
Suppose we don't call flush() method on session object and if we call commit method it will internally do the work of executing statements on the database and then committing.
commit=flush+commit (in case of functionality)
Thus, I conclude that when we call method flush() on Session object, then it doesn't get commit but hits the database and executes the query and gets rollback too.
In order to commit we use commit() on Transaction object.
Flushing the Session gets the data that is currently in the session synchronized with what is in the database.
More on the Hibernate website:
http://docs.jboss.org/hibernate/core/3.3/reference/en/html/objectstate.html#objectstate-flushing
flush() is useful, because there are absolutely no guarantees about when the Session executes the JDBC calls, only the order in which they are executed - except you use flush().
You might use flush to force validation constraints to be realised and detected in a known place rather than when the transaction is committed. It may be that commit gets called implicitly by some framework logic, through declarative logic, the container, or by a template. In this case, any exception thrown may be difficult to catch and handle (it could be too high in the code).
For example, if you save() a new EmailAddress object, which has a unique constraint on the address, you won't get an error until you commit.
Calling flush() forces the row to be inserted, throwing an Exception if there is a duplicate.
However, you will have to roll back the session after the exception.
I would just like to club all the answers given above and also relate Flush() method with Session.save() so as to give more importance
Hibernate save() can be used to save entity to database. We can invoke this method outside a transaction, that’s why I don’t like this method to save data. If we use this without transaction and we have cascading between entities, then only the primary entity gets saved unless we flush the session.
flush(): Forces the session to flush. It is used to synchronize session data with database.
When you call session.flush(), the statements are executed in database but it will not committed.
If you don’t call session.flush() and if you call session.commit() , internally commit() method executes the statement and commits.
So commit()= flush+commit.
So session.flush() just executes the statements in database (but not commits) and statements are NOT IN MEMORY anymore. It just forces the session to flush.
Few important points:
We should avoid save outside transaction boundary, otherwise mapped entities will not be saved causing data inconsistency. It’s very normal to forget flushing the session because it doesn’t throw any exception or warnings.
By default, Hibernate will flush changes automatically for you:
before some query executions
when a transaction is committed
Allowing to explicitly flush the Session gives finer control that may be required in some circumstances (to get an ID assigned, to control the size of the Session)
The flush() method causes Hibernate to flush the session. You can configure Hibernate to use flushing mode for the session by using setFlushMode() method. To get the flush mode for the current session, you can use getFlushMode() method. To check, whether session is dirty, you can use isDirty() method. By default, Hibernate manages flushing of the sessions.
As stated in the documentation:
https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/chapters/flushing/Flushing.html
Flushing
Flushing is the process of synchronizing the state of the persistence
context with the underlying database. The EntityManager and the
Hibernate Session expose a set of methods, through which the
application developer can change the persistent state of an entity.
The persistence context acts as a transactional write-behind cache,
queuing any entity state change. Like any write-behind cache, changes
are first applied in-memory and synchronized with the database during
flush time. The flush operation takes every entity state change and
translates it to an INSERT, UPDATE or DELETE statement.
The flushing strategy is given by the flushMode of the current
running Hibernate Session. Although JPA defines only two flushing
strategies (AUTO and COMMIT), Hibernate has a much
broader spectrum of flush types:
ALWAYS: Flushes the Session before every query;
AUTO: This is the default mode and it flushes the Session only if necessary;
COMMIT: The Session tries to delay the flush until the current Transaction is committed, although it might flush prematurely too;
MANUAL: The Session flushing is delegated to the application, which must call Session.flush() explicitly in order to apply the
persistence context changes.
By default, Hibernate uses the AUTO flush mode which triggers a
flush in the following circumstances:
prior to committing a Transaction;
prior to executing a JPQL/HQL query that overlaps with the queued entity actions;
before executing any native SQL query that has no registered synchronization.
Calling EntityManager#flush does have side-effects. It is conveniently used for entity types with generated ID values (sequence values): such an ID is available only upon synchronization with underlying persistence layer. If this ID is required before the current transaction ends (for logging purposes for instance), flushing the session is required.
With this method you evoke the flush process. This process synchronizes
the state of your database with state of your session by detecting state changes and executing the respective SQL statements.

Categories

Resources