HHH-10971 - Document flush operation order
This commit is contained in:
parent
1b83be8a75
commit
a27a13313d
|
@ -235,3 +235,49 @@ The `INSERT` statement was not executed because the persistence context because
|
|||
====
|
||||
This mode is useful when using multi-request logical transactions and only the last request should flush the persistence context.
|
||||
====
|
||||
|
||||
[[flushing-order]]
|
||||
=== Flush operation order
|
||||
|
||||
From a database perspective, a row state can be altered using either an `INSERT`, an `UPDATE` or a `DELETE` statement.
|
||||
Because https://vladmihalcea.com/2014/07/30/a-beginners-guide-to-jpahibernate-entity-state-transitions/[entity state changes] are automatically converted to SQL statements, it's important to know which entity actions are associated to a given SQL statement.
|
||||
|
||||
`INSERT`:: The `INSERT` statement is generated either by the `EntityInsertAction` or `EntityIdentityInsertAction`. These actions are scheduled by the `persist` operation, either explicitly or through cascading the `PersistEvent` from a parent to a child entity.
|
||||
`DELETE`:: The `DELETE` statement is generated by the `EntityDeleteAction` or `OrphanRemovalAction`.
|
||||
`UPDATE`:: The `UPDATE` statement is generated by `EntityUpdateAction` during flushing if the managed entity has been marked modified. The https://vladmihalcea.com/2014/08/21/the-anatomy-of-hibernate-dirty-checking/[dirty checking mechanism] is responsible for determining if a managed entity has been modified since it was first loaded.
|
||||
|
||||
Hibernate does not execute the SQL statements in the order of their associated entity state operations.
|
||||
|
||||
To visualize how this works, consider the following example:
|
||||
|
||||
[[flushing-order-example]]
|
||||
.Flush operation order
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/FlushOrderTest.java[tags=flushing-order-example]
|
||||
----
|
||||
|
||||
[source, SQL, indent=0]
|
||||
----
|
||||
include::{extrasdir}/flushing-order-example.sql[]
|
||||
----
|
||||
====
|
||||
|
||||
Even if we removed the first entity and then persist a new one, Hibernate is going to execute the `DELETE` statement after the `INSERT`.
|
||||
|
||||
[TIP]
|
||||
====
|
||||
The order in which SQL statements are executed is given by the `ActionQueue` and not by the order in which entity state operations have been previously defined.
|
||||
====
|
||||
|
||||
The `ActionQueue` executes all operations in the following order:
|
||||
|
||||
. `OrphanRemovalAction`
|
||||
. `EntityInsertAction` or `EntityIdentityInsertAction`
|
||||
. `EntityUpdateAction`
|
||||
. `CollectionRemoveAction`
|
||||
. `CollectionUpdateAction`
|
||||
. `CollectionRecreateAction`
|
||||
. `EntityDeleteAction`
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
INSERT INTO Person (name, id)
|
||||
VALUES ('John Doe', 2L)
|
||||
|
||||
DELETE FROM Person WHERE id = 1
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.userguide.flush;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import static org.hibernate.userguide.util.TransactionUtil.doInJPA;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class FlushOrderTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
private static final Logger log = Logger.getLogger( FlushOrderTest.class);
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[]{
|
||||
Person.class
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOrder() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
entityManager.createNativeQuery("delete from Person").executeUpdate();
|
||||
});
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Person person = new Person("John Doe");
|
||||
person.id = 1L;
|
||||
entityManager.persist(person);
|
||||
});
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
log.info("testFlushSQL");
|
||||
//tag::flushing-order-example[]
|
||||
Person person = entityManager.find( Person.class, 1L);
|
||||
entityManager.remove(person);
|
||||
|
||||
Person newPerson = new Person( );
|
||||
newPerson.setId( 2L );
|
||||
newPerson.setName( "John Doe" );
|
||||
entityManager.persist( newPerson );
|
||||
//end::flushing-order-example[]
|
||||
});
|
||||
}
|
||||
|
||||
@Entity(name = "Person")
|
||||
public static class Person {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
public Person() {
|
||||
}
|
||||
|
||||
public Person(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue