ChatGPT解决这个技术问题 Extra ChatGPT

Correct use of flush() in JPA/Hibernate

I was gathering information about the flush() method, but I'm not quite clear when to use it and how to use it correctly. From what I read, my understanding is that the contents of the persistence context will be synchronized with the database, i. e. issuing outstanding statements or refreshing entity data.

Now I got following scenario with two entities A and B (in a one-to-one relationship, but not enforced or modelled by JPA). A has a composite PK, which is manually set, and also has an auto-generated IDENTITY field recordId. This recordId should be written to entity B as a foreign-key to A. I'm saving A and B in a single transaction. The problem is that the auto-generated value A.recordId is not available within the transaction, unless I make an explicit call of em.flush() after calling em.persist() on A. (If I have an auto-generated IDENTITY PK then the value is directly updated in the entity, but that's not the case here.)

Can em.flush() cause any harm when using it within a transaction?


F
Flavio

Probably the exact details of em.flush() are implementation-dependent. In general anyway, JPA providers like Hibernate can cache the SQL instructions they are supposed to send to the database, often until you actually commit the transaction. For example, you call em.persist(), Hibernate remembers it has to make a database INSERT, but does not actually execute the instruction until you commit the transaction. Afaik, this is mainly done for performance reasons.

In some cases anyway you want the SQL instructions to be executed immediately; generally when you need the result of some side effects, like an autogenerated key, or a database trigger.

What em.flush() does is to empty the internal SQL instructions cache, and execute it immediately to the database.

Bottom line: no harm is done, only you could have a (minor) performance hit since you are overriding the JPA provider decisions as regards the best timing to send SQL instructions to the database.


What he said. em.flush() behaviour echoes java.io.Flushable.flush() where all buffered data are sent to whatever destination is appropriate.
if flush() sends data to the database? what happens if an exception is thrown after that? Will the entity manager rollback everything? even the data written in the first flush?
flush() sends SQL instructions to the database like INSERT, UPDATE etc. It will not send a COMMIT, so if you have an exception after a flush(), you can still have a complete rollback.
You can rollback the DB, but it will not rollback any changes to the objects, e.g., auto-incremented 'version', autogenerated ID, etc. Furthermore the entity manager will be closed after a rollback. Just beware that if you try to 'merge' the object to another session, the auto-incremented 'version' in particular may cause an OptimisticLockException.
Apart from triggering side effects, another reason to use flush() is if you want to be able to read the effects of an operation in the database using JPQL/HQL (e.g. in a test). JPA cannot use cached data when executing these queries, so only stuff that's actually in the DB will be read.
G
Gab

Can em.flush() cause any harm when using it within a transaction?

Yes, it may hold locks in the database for a longer duration than necessary.

Generally, When using JPA you delegates the transaction management to the container (a.k.a CMT - using @Transactional annotation on business methods) which means that a transaction is automatically started when entering the method and commited / rolled back at the end. If you let the EntityManager handle the database synchronization, sql statements execution will be only triggered just before the commit, leading to short lived locks in database. Otherwise your manually flushed write operations may retain locks between the manual flush and the automatic commit which can be long according to remaining method execution time.

Notes that some operation automatically triggers a flush : executing a native query against the same session (EM state must be flushed to be reachable by the SQL query), inserting entities using native generated id (generated by the database, so the insert statement must be triggered thus the EM is able to retrieve the generated id and properly manage relationships)


c
clemens

Actually, em.flush(), do more than just sends the cached SQL commands. It tries to synchronize the persistence context to the underlying database. It can cause a lot of time consumption on your processes if your cache contains collections to be synchronized.

Caution on using it.