Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When does Spring Data JPA actually call INSERT on the database?

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
...
like image 364
Chinmay Avatar asked Dec 04 '22 08:12

Chinmay


2 Answers

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:

How JPA handles this

  1. The 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(…).
  2. The entity to be persisted uses an ID generation mechanism.
  3. You manually call the flush method.
  4. You trigger a query and the 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:

Spring Data specifics

  1. For non-ID-auto-generated entities, don't expect them to appear in the database before the 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.
  2. Instead of directly inspecting the object, you can query for it and inspect the returned object. This uses what I described in 4 above. Note: don't use 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).
  3. As a last resort you can use 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.
like image 189
Oliver Drotbohm Avatar answered Feb 09 '23 00:02

Oliver Drotbohm


By default, Hibernate inserts saved entities during flush. It explains why XYs 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.

like image 32
axtavt Avatar answered Feb 09 '23 00:02

axtavt