HHH-10971 - Document flush operation order

This commit is contained in:
Vlad Mihalcea 2016-07-19 18:00:29 +03:00
parent 1b83be8a75
commit a27a13313d
3 changed files with 141 additions and 1 deletions

View File

@ -234,4 +234,50 @@ The `INSERT` statement was not executed because the persistence context because
[NOTE]
====
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`

View File

@ -0,0 +1,5 @@
INSERT INTO Person (name, id)
VALUES ('John Doe', 2L)
DELETE FROM Person WHERE id = 1

View File

@ -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;
}
}
}