How FlushMode.COMMIT works - java

As per Session.setFlushMode(FlushMode) we can set FlushMode to the session. Now I am trying to test how the Flushmode.COMMIT mode works with a small example.
I have created an entity called Cat with just 2 properties id and name. Now here is the code that I am testing:
Session session = getSession();
session.setFlushMode(FlushMode.COMMIT);
Transaction tx = session.beginTransaction();
Cat cat = (Cat) session.get(Cat.class, 1);
cat.setName(name);
session.flush();
//tx.commit();
session.close();
From logs I can see that when the line session.flush() is executed then hibernate is issuing JDBC update call to database as:
Hibernate: update Cat set name=? where id=?
As I set the FlushMode to COMMIT, I am expecting that the update query will be executed only when I say tx.commit() but the flushing is happening at session.flush(). Can someone please explain why it is happening like this?

Note the Javadoc of Session#flush().
Force this session to flush. Must be called at the end of a unit of
work, before committing the transaction and closing the session
(depending on flush-mode, Transaction.commit() calls this method).
or the javadoc for FlushMode#MANUAL
The Session is only ever flushed when Session.flush() is explicitly
called by the application. This mode is very efficient for read only
transactions.
Setting a FlushMode simply defines when flush() will happen automatically (all but MANUAL). If you call flush() yourself, manually, you're overriding that behavior.

Related

Hibernate persist() method

Is the below statement a valid one?
persist() also guarantees that it will not execute an INSERT statement if it is called outside of transaction boundaries
When I try the below code using persist; then the row is getting inserted without any transaction (It is commented out).
SessionFactory sessionFactory = new Configuration().configure("student.cfg.xml").buildSessionFactory();
Session session = sessionFactory.openSession();
//Transaction tran = session.beginTransaction();
/*
* Persist is working without transaction boundaries ===> why?
*/
Student student = new Student();
student.setFirstName("xxx");
student.setLastName("yyy");
student.setCity("zzz");
student.setState("ppp");
student.setCountry("###");
student.setId("123456");
session.persist(student);
//tran.commit();
session.flush();
session.close();
persist() also guarantees that it will not execute an INSERT statement if it is called outside of transaction boundaries
This statement is correct. When control returns from persist() back to your code, no INSERT statements have been executed. These statements are guaranteed to be deferred until session flushing. Note that persist() would be a pointless method if no insert happened ever.
AFAIK data is saving because of session.flush(), try after removing this, mostly you will get an error.
Hibernate persist
Diff. save & persist

session.persist() method confusion

I read this in doc:
persist() also guarantees that it will not execute an INSERT statement if it is called outside of transaction boundaries.
But when I try this code:
SessionFactory sessionFactory = new Configuration().configure("student.cfg.xml").buildSessionFactory();
Session session = sessionFactory.openSession();
Student student = new Student();
student.setFirstName("XXX");
student.setLastName("YYY");
student.setCity("ZZZ");
student.setState("PPP");
student.setCountry("XXX");
student.setId("NNN");
session.persist(student);
session.flush();
session.close();
The record is getting inserted. As you can see in the above code, I have not used any transaction. Then in that case according to the doc, the data should not be inserted in the DB right?
Transaction boundaries means any operation between Transaction tran = session.beginTransaction(); and tran.commit(); right?
Please let me know where am I making the mistake.
Regards,
If you remove session.flush() and session.close(), you'll observe that no insert statement was executed. The point of that guarantee is that the persist call itself won't execute any statements; it doesn't say anything about the implication on the behavior of flush and close. Indeed, flush flushes all persistent objects to the datastore.
This is old magic!
if (getTransactionIsolation()==Connection.TRANSACTION_NONE
|| getAutoCommit()==true)
The persist() ignores the transaction boundary!
Why? persist() does not know about support transactions or not. In second case its committed because he didnt expected to need a transaction.

Hibernate lock causes dirty collection exception

I'm trying to reattach to the session an object from the HTTP session that was originally retrieved from the DB, I do this by calling session.lock(object, LockMode.None) and even though lock does not cascade this works all right for me because it does not push updates to the DB like merge does (the lock is required to open a detail view in a pop-up and the actual saving would occur later on the main window). Now to my surprise I have found that if my entity has a one to many relation any changes on that collection would cause a HibernateException "Reassociated object has dirty collection".
How am I supposed to reattach objects to the session without updating the DB or discarding the changes on the object then?
Here is the situation as code
EntityA t = createAnEntityA();
Session sess = factory.openSession();
sess.beginTransaction();
sess.save(t);
sess.getTransaction().commit();
sess.close();
// t is now saved on the DB but in dettached state
// change a simple property
sess = factory.openSession();
sess.beginTransaction();
t.setPropertyB("B");
sess.lock(t, LockMode.NONE);
// t is attached again, you won't get LazyInitializationException
// by calling its properties, although you have to be careful
// because the reattachment does not cascade to children
sess.getTransaction().commit();
sess.close();
// no updates went to the DB because setPropertyB was called
// when t was still dettached
// now change a collection
EntityC c = createAnEntityC();
t.getCollectionPropertyC().add(c);
sess = factory.openSession();
sess.beginTransaction();
sess.lock(t, LockMode.NONE);
// Exception is thrown :-(
sess.getTransaction().commit();
sess.close();
I'm afraid currently it's not possible without hitting DB.
Apparently, the exception is happening because of lock command.
There's a Jira reporting this behavior.
https://hibernate.atlassian.net/browse/HHH-511
It has 2 patches to fix the problem. You could try those patches.
But if you problem is only Lazy loading of collection, you could consider using Open Session In View pattern. (maybe not the best pattern, but it can work for your case)

Does session.save() do an immediate insert?

...or does it wait until the associated transaction is committed?
I'm using an HQL query in a loop like this:
tx.begin()
for(...)
{
session.getNamedQuery(...).list()
...
session.save(new MyEntity())
}
tx.commit()
The named query needs to be able to see the entities that were added with the save call. Will it work that way?
It depends on the flush mode of the session.
You can also manually flush it with session.flush()
The flush mode can be set in multiple ways - session.setFlushMode(..), entityManager.setFlushMode(..), or via xml configuration (org.hibernate.FlushMode).
The default value is 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.
Try it, if it doesn't, then call
session.flush()
to send the SQL to the DB. Regardless, it won't be committed until the call to
tx.commit()

declare a transaction in ejb 3.0

how can I force the EJB to not flush everything after every single command, I want to do a transaction. I've read that this is done somehow declaratively. But how exactly?
#Stateless
public class SomeBean{
#PersistenceContext
EntityManager em;
public void doSomeStuffAndThenFlushToTheDb(){
em.persist(entity);
// it's flushed by now, I don't want that
em.persist(somethingElse);
// now I want to flush both
em.flush();
}
}
Hi, how can I force the EJB to not flush everything after every single command, I want to do a transaction.
First of all, you shouldn't get a flush after "every single command" and I'm actually surprised that you get a flush after the persist. Are you showing all the code? Second, EJB methods are transacted by default. But transaction and flush are not really related (the only link is that a flush will be done before the commit). Anyway...
If you would like to control the way a flush() call executes, you can change the default flush mode using EntityManager#setFlushMode(). The flush modes are as follows:
COMMIT - Flushing occurs only at transaction commit, or when flush() is called.
AUTO - (Default) Flushing occurs before any query execution.
So something like that should work:
#Stateless
public class SomeBean {
...
public void doSomeStuffAndThenFlushToTheDb(){
em.setFlushMode(COMMIT);
em.persist(entity);
em.persist(somethingElse);
em.flush(); // Causes flush
}
...
}
But as I said, I'm surprised by your example. The behavior you describe is not what I would expect.
First of all "flush" means only store it the second level cache (db driver). It is not stored directly within the DB. The entity is kept within the cache since the transaction is completed and a commit is sent. "Clear" means detach the entity. It does not mean clear the database. So afterwards you cannot persist this entity anymore. But the persist will still be done at the end of the transaction.

Categories

Resources