When you save an object using JPARepository/Hibernate, it first checks if it exists (using Primary Key) in the database, and then Inserts. So we see 2 logs: SELECT & INSERT.
This is what I'm trying to do through spring data: (X and XY are 2 distinct objects)
Call Save on XRepository for Object X1
Call Save on XYRepository for Object X1Y1
Call Save on XYRepository for Object X1Y2
Call Save on XYRepository for Object X1Y3
Call Save on XRepository for Object X2
Call Save on XYRepository for Object X2Y1
Call Save on XYRepository for Object X2Y2
Call Save on XYRepository for Object X2Y3
This is my observation in the logs:
SELECT X1
INSERT X1
SELECT X1Y1
SELECT X1Y2
SELECT X1Y3
INSERT X1Y1
INSERT X1Y2
INSERT X1Y3
SELECT X2
INSERT X2
SELECT X2Y1
SELECT X2Y2
SELECT X2Y3
INSERT X2Y1
INSERT X2Y2
INSERT X2Y3
So when does Spring Data actually call the insert? How does this work?
This is what i had expected:
SELECT X1
INSERT X1
SELECT X1Y1
INSERT X1Y1
SELECT X1Y2
INSERT X1Y2
SELECT X1Y3
INSERT X1Y3
...
Spring Data JPA itself does not control the interaction with the database directly. All it does is interacting with the EntityManager
, so effectively all behavioral effects are defined by JPA and the underlying OR-mapper.
Persistence providers usually try to batch up database interactions as this minimizes the overhead, especially if you're doing a lot of persistence operations in a single session. So usually the EntityManager
will only flush the changes to disk if one of the following scenarios occur:
EntityManager
(read: the Session
in the Hibernate case) gets closed and the dirty-checking mechanism finds pending changes. So you might have changed a property of an attached entity or added one to the persistence context using ….persist(…)
.EntityManager
has pending changes. It will then flush those to the database to make sure the query can potentially already see changed data. As an aside: the last point sometimes results in the weird effect of
DataIntegrityViolationExceptions
being thrown when a query is executed and a lot of people don't get that in the first place. But it's the flushing of pending changes right before the query execution that causes the issue.
That leads to a few important observations:
EntityManager
gets closed. With Spring this is usually bound to the lifecycle of a transaction, so you might need to commit first and check for changed properties on the changed object.findOne(…)
for this approach as this will make use of the EntityManager.find(type, id)
-method which is usually heavily using the first level cache (potentially without flushing first).saveAndFlush(…)
on the JpaRepository
interface but we strongly recommend not to do it as you effectively expose persistence layer internals to your clients. Prefer using auto-ID-generated entities.By default, Hibernate inserts saved entities during flush. It explains why XY
s are inserted in batch (during flush), and why saveAndFlush()
inserts them immediately.
But entities whose primary keys are generated by the database during insert (i.e. primary key is generated and generation strategy requires actual insert in order to generate the key) are inserted immediately, because Hibernate should know ids of saved entities. That's what you observe for X
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With