Add Flushing chapter and JPQL section for batch updates

This commit is contained in:
vladmihalcea 2016-01-19 15:42:44 +02:00
parent 97ba351c2a
commit 90b7f9e07c
23 changed files with 1169 additions and 26 deletions

View File

@ -10,6 +10,7 @@ include::chapters/architecture/Architecture.adoc[]
include::chapters/domain/DomainModel.adoc[]
include::chapters/bootstrap/Bootstrap.adoc[]
include::chapters/pc/PersistenceContext.adoc[]
include::chapters/flushing/Flushing.adoc[]
include::chapters/jdbc/Database_Access.adoc[]
include::chapters/transactions/Transactions.adoc[]
include::chapters/jndi/JNDI.adoc[]

View File

@ -122,7 +122,9 @@ They have different semantics from the `save()`, `saveOrUpdate()`, and `delete()
DML, or Data Manipulation Language, refers to SQL statements such as `INSERT`, `UPDATE`, and `DELETE`.
Hibernate provides methods for bulk SQL-style DML statement execution, in the form of Hibernate Query Language (HQL).
==== HQL for UPDATE and DELETE
==== HQL/JPQL for UPDATE and DELETE
Both the Hibernate native Query Language and JPQL (Java Persistence Query Language) provide support for bulk UPDATE and DELETE.
.Psuedo-syntax for UPDATE and DELETE statements using HQL
====
@ -146,15 +148,23 @@ If the entity name is not aliased, then it is illegal for any property reference
Joins, either implicit or explicit, are prohibited in a bulk HQL query.
You can use sub-queries in the `WHERE` clause, and the sub-queries themselves can contain joins.
.Executing an HQL UPDATE, using the `Query.executeUpdate()`
.Executing a JPQL `UPDATE`, using the `Query.executeUpdate()`
====
[source,java]
----
include::{sourcedir}/executeUpdate.java[]
include::{sourcedir}/jpql_update.java[]
----
====
In keeping with the EJB3 specification, HQL UPDATE statements, by default, do not effect the version or the timestamp property values for the affected entities.
.Executing an HQL `UPDATE`, using the `Query.executeUpdate()`
====
[source,java]
----
include::{sourcedir}/hql_update.java[]
----
====
In keeping with the EJB3 specification, HQL `UPDATE` statements, by default, do not effect the version or the timestamp property values for the affected entities.
You can use a versioned update to force Hibernate to reset the version or timestamp property values, by adding the `VERSIONED` keyword after the `UPDATE` keyword.
.Updating the version of timestamp
@ -168,6 +178,16 @@ include::{sourcedir}/updating_version.java[]
[NOTE]
====
If you use the `VERSIONED` statement, you cannot use custom version types, which use class `org.hibernate.usertype.UserVersionType`.
This feature is only available in HQL since it's not standardized by JPA.
====
.A JPQL `DELETE` statement
====
[source,java]
----
include::{sourcedir}/jpql_delete.java[]
----
====
.An HQL `DELETE` statement
@ -180,7 +200,7 @@ include::{sourcedir}/hql_delete.java[]
Method `Query.executeUpdate()` returns an `int` value, which indicates the number of entities effected by the operation.
This may or may not correlate to the number of rows effected in the database.
An HQL bulk operation might result in multiple SQL statements being executed, such as for joined-subclass.
An JPQL/HQL bulk operation might result in multiple SQL statements being executed, such as for joined-subclass.
In the example of joined-subclass, a `DELETE` against one of the subclasses may actually result in deletes in the tables underlying the join, or further down the inheritance hierarchy.
==== HQL syntax for INSERT
@ -226,9 +246,4 @@ include::{sourcedir}/hql_insert.java[]
----
====
This section is only a brief overview of HQL. For more information, see <<chapters/query-hql/HQL.adoc#hql,HQL>>.
[[batch-bulk-jpql]]
=== Java Persistence Query Language for DML
TODO
This section is only a brief overview of HQL. For more information, see <<chapters/query-hql/HQL.adoc#hql,HQL>>.

View File

@ -4,7 +4,8 @@ Transaction tx = session.beginTransaction();
String hqlDelete = "delete Customer c where c.name = :oldName";
// or String hqlDelete = "delete Customer where name = :oldName";
int deletedEntities = session.createQuery( hqlDelete )
.setString( "oldName", oldName )
.executeUpdate();
.setString( "oldName", oldName )
.executeUpdate();
tx.commit();
session.close();

View File

@ -3,6 +3,7 @@ Transaction tx = session.beginTransaction();
String hqlInsert = "insert into DelinquentAccount (id, name) select c.id, c.name from Customer c where ...";
int createdEntities = session.createQuery( hqlInsert )
.executeUpdate();
.executeUpdate();
tx.commit();
session.close();

View File

@ -4,8 +4,9 @@ Transaction tx = session.beginTransaction();
String hqlUpdate = "update Customer c set c.name = :newName where c.name = :oldName";
// or String hqlUpdate = "update Customer set name = :newName where name = :oldName";
int updatedEntities = session.createQuery( hqlUpdate )
.setString( "newName", newName )
.setString( "oldName", oldName )
.executeUpdate();
.setString( "newName", newName )
.setString( "oldName", oldName )
.executeUpdate();
tx.commit();
session.close();

View File

@ -0,0 +1,11 @@
EntityManager entityManager = entityManagerFactory.createEntityManager();
EntityTransaction tx = entityManager.getTransaction();
String jpqlDelete = "delete Customer c where c.name = :oldName";
// or String jpqlDelete = "delete Customer where name = :oldName";
int updatedEntities = entityManager.createQuery( jpqlDelete )
.setParameter( "oldName", oldName )
.executeUpdate();
tx.commit();
entityManager.close();

View File

@ -0,0 +1,12 @@
EntityManager entityManager = entityManagerFactory.createEntityManager();
EntityTransaction tx = entityManager.getTransaction();
String jpqlUpdate = "update Customer c set c.name = :newName where c.name = :oldName";
// or String query = "update Customer set name = :newName where name = :oldName";
int updatedEntities = entityManager.createQuery( jpqlUpdate )
.setParameter( "oldName", oldName )
.setParameter( "newName", newName )
.executeUpdate();
tx.commit();
entityManager.close();

View File

@ -524,8 +524,8 @@ Note that this can cause difficulty as the driver chooses to map many different
==== UUID as identifier
Hibernate supports using UUID values as identifiers, and they can even be generated on user'sbehalf.
For details see the discussion of generators in <<chapters/domain/identifiers.adoc#identifiers,_Identifier generators_>>
Hibernate supports using UUID values as identifiers, and they can even be generated on user's behalf.
For details, see the discussion of generators in <<chapters/domain/identifiers.adoc#identifiers,_Identifier generators_>>.
[[basic-datetime]]
==== Mapping Date/Time Values

View File

@ -1,12 +1,236 @@
[[flushing]]
== Flushing
:sourcedir: ../../../../../test/java/org/hibernate/jpa/test/userguide/flush
* <<chapters/pc/PersistenceContext.adoc#pc,Persistence Contexts>>
* <<chapters/query-hql/HQL.adoc#hql,HQL and JPQL>>
* link:#criteria[???]
* link:#querynative[???]
Flushing is the process of synchronizing the state of the persistence context with the underlying database.
The `EntityManager` and the Hibernate `Session` expose a set of methods, through which the application developer can change the persistent state of an entity.
Flushing is the process of synchronizing the state of the persistence
context to the database.
The persistence context acts as a transactional write-behind cache, queuing any entity state change.
Like any write-behind cache, changes are first applied in-memory and synchronized with the database during flush time.
The flush operation takes every entity state change and translates it to an `INSERT`, `UPDATE` or `DELETE` statement.
TODO
[NOTE]
====
Because DML statements are grouped together, Hibernate can apply batching transparently.
See the <<chapters/batch/Batching.adoc#batch,Batching chapter>> for more information.
====
The flushing strategy is given by the http://docs.jboss.org/hibernate/stable/orm/javadocs/org/hibernate/Session.html#getFlushMode--[`flushMode`] of the current running Hibernate `Session`.
Although JPA defines only two flushing strategies (https://docs.oracle.com/javaee/7/api/javax/persistence/FlushModeType.html#AUTO[`AUTO`] and https://docs.oracle.com/javaee/7/api/javax/persistence/FlushModeType.html#COMMIT[`COMMIT`]),
Hibernate has a much broader spectrum of flush types:
ALWAYS:: Flushes the `Session` before every query.
AUTO:: This is the default mode and it flushes the `Session` only if necessary.
COMMIT:: The `Session` tries to delay the flush until the current `Transaction` is committed, although it might flush prematurely too.
MANUAL:: The `Session` flushing is delegated to the application, which must call `Session.flush()` explicitly in order to apply the persistence context changes.
[[flushing-auto]]
== `AUTO` flush
By default, Hibernate uses the `AUTO` flush mode which triggers a flush in the following circumstances:
* prior to committing a `Transaction`
* prior to executing a JPQL/HQL query that overlaps with the queued entity actions
* before executing any native SQL query that has no registered synchronization
=== `AUTO` flush on commit
In the following example, an entity is persisted and then the transaction is committed.
[[flushing-auto-flush-commit-example]]
.Automatic flushing on commit
====
[source, JAVA, indent=0]
----
include::{sourcedir}/AutoFlushTest.java[tags=flushing-auto-flush-commit-example]
----
[source, SQL, indent=0]
----
include::{sourcedir}/flushing-auto-flush-commit-example.sql[]
----
====
Hibernate logs the message prior to inserting the entity because the flush only occurred during transaction commit.
[NOTE]
====
This is valid for the `SEQUENCE` and `TABLE` identifier generators.
The `IDENTITY` generator must execute the insert right after calling `persist()`.
For details, see the discussion of generators in <<chapters/domain/identifiers.adoc#identifiers,_Identifier generators_>>.
====
=== `AUTO` flush on JPQL/HQL query
A flush may also be triggered when executing an entity query.
[[flushing-auto-flush-jpql-example]]
.Automatic flushing on JPQL/HQL
====
[source, JAVA, indent=0]
----
include::{sourcedir}/AutoFlushTest.java[tags=flushing-auto-flush-jpql-example]
----
[source, SQL, indent=0]
----
include::{sourcedir}/flushing-auto-flush-jpql-example.sql[]
----
====
The reason why the `Advertisement` entity query didn't trigger a flush is because there's no overlapping between the `Advertisement` and the `Person` tables:
[[flushing-auto-flush-jpql-entity-example]]
.Automatic flushing on JPQL/HQL entities
====
[source, JAVA, indent=0]
----
include::{sourcedir}/AutoFlushTest.java[tags=flushing-auto-flush-jpql-entity-example]
----
====
When querying for a `Person` entity, the flush is triggered prior to executing the entity query.
[[flushing-auto-flush-jpql-overlap-example]]
.Automatic flushing on JPQL/HQL
====
[source, JAVA, indent=0]
----
include::{sourcedir}/AutoFlushTest.java[tags=flushing-auto-flush-jpql-overlap-example]
----
[source, SQL, indent=0]
----
include::{sourcedir}/flushing-auto-flush-jpql-overlap-example.sql[]
----
====
This time, the flush was triggered by a JPQL query because the pending entity persist action overlaps with the query being executed.
=== `AUTO` flush on native SQL query
When executing a native SQL query, a flush is always triggered when using the `EntityManager` API.
[[flushing-auto-flush-sql-example]]
.Automatic flushing on native SQL using `EntityManager`
====
[source, JAVA, indent=0]
----
include::{sourcedir}/AutoFlushTest.java[tags=flushing-auto-flush-sql-example]
----
====
The `Session` API doesn't trigger an `AUTO` flush when executing a native query
[[flushing-auto-flush-sql-native-example]]
.Automatic flushing on native SQL using `Session`
====
[source, JAVA, indent=0]
----
include::{sourcedir}/AutoFlushTest.java[tags=flushing-auto-flush-sql-native-example]
----
====
To flush the `Session`, the query must use a synchronization:
[[flushing-auto-flush-sql-synchronization-example]]
.Automatic flushing on native SQL with `Session` synchronization
====
[source, JAVA, indent=0]
----
include::{sourcedir}/AutoFlushTest.java[tags=flushing-auto-flush-sql-synchronization-example]
----
====
[[flushing-commit]]
=== `COMMIT` flush
JPA also defines a COMMIT flush mode, which is described as follows:
[quote, Section 3.10.8 of the JPA 2.1 Specification]
____
If `FlushModeType.COMMIT` is set, the effect of updates made to entities in the persistence context upon queries is unspecified.
____
When executing a JPQL query, the persistence context is only flushed when the current running transaction is committed.
[[flushing-commit-flush-jpql-example]]
.`COMMIT` flushing on JPQL
====
[source, JAVA, indent=0]
----
include::{sourcedir}/CommitFlushTest.java[tags=flushing-commit-flush-jpql-example]
----
[source, SQL, indent=0]
----
include::{sourcedir}/flushing-commit-flush-jpql-example.sql[]
----
====
Because the JPA doesn't impose a strict rule on delaying flushing, when executing a native SQL query, the persistence context is going to be flushed.
[[flushing-commit-flush-sql-example]]
.`COMMIT` flushing on SQL
====
[source, JAVA, indent=0]
----
include::{sourcedir}/CommitFlushTest.java[tags=flushing-commit-flush-sql-example]
----
[source, SQL, indent=0]
----
include::{sourcedir}/flushing-commit-flush-sql-example.sql[]
----
====
[[flushing-always]]
=== `ALWAYS` flush
[NOTE]
====
The `ALWAYS` is only available with the native `Session` API.
====
The `ALWAYS` flush mode triggers a persistence context flush even when executing a native SQL query against the `Session` API.
[[flushing-always-flush-sql-example]]
.`COMMIT` flushing on SQL
====
[source, JAVA, indent=0]
----
include::{sourcedir}/AlwaysFlushTest.java[tags=flushing-always-flush-sql-example]
----
[source, SQL, indent=0]
----
include::{sourcedir}/flushing-always-flush-sql-example.sql[]
----
====
[[flushing-manual]]
=== `MANUAL` flush
Both the `EntityManager` and the Hibernate `Session` define a `flush()` method that, when called, triggers a manual flush.
Hibernate also defines a `MANUAL` flush mode, so the persistence context can only be flushed manually.
[[flushing-manual-flush-example]]
.`MANUAL` flushing
====
[source, JAVA, indent=0]
----
include::{sourcedir}/ManualFlushTest.java[tags=flushing-manual-flush-example]
----
[source, SQL, indent=0]
----
include::{sourcedir}/flushing-manual-flush-example.sql[]
----
====
The `INSERT` statement was not executed because the persistence context because there was no manual `flush()` call.
[NOTE]
====
This mode is useful when using multi-request logical transactions and only the last request should flush the persistence context.
====

View File

@ -25,6 +25,7 @@ import static org.hibernate.jpa.test.util.TransactionUtil.doInJPA;
*/
public class OneToManyUnidirectionalTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {

View File

@ -0,0 +1,165 @@
/*
* 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.jpa.test.userguide.flush;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import org.hibernate.FlushMode;
import org.hibernate.Session;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.junit.Test;
import org.jboss.logging.Logger;
import static org.hibernate.jpa.test.util.TransactionUtil.doInJPA;
import static org.junit.Assert.assertTrue;
/**
* <code>AlwaysFlushTest</code> - Always Flush Test
*
* @author Vlad Mihalcea
*/
public class AlwaysFlushTest extends BaseEntityManagerFunctionalTestCase {
private static final Logger log = Logger.getLogger( AlwaysFlushTest.class);
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Person.class,
Phone.class,
Advertisement.class
};
}
@Test
public void testFlushSQL() {
doInJPA( this::entityManagerFactory, entityManager -> {
entityManager.createNativeQuery("delete from Person").executeUpdate();
});
doInJPA( this::entityManagerFactory, entityManager -> {
log.info("testFlushSQL");
//tag::flushing-always-flush-sql-example[]
Person person = new Person("John Doe");
entityManager.persist(person);
Session session = entityManager.unwrap( Session.class);
assertTrue(((Number) session
.createSQLQuery("select count(*) from Person")
.setFlushMode( FlushMode.ALWAYS)
.uniqueResult()).intValue() == 1);
//end::flushing-always-flush-sql-example[]
});
}
@Entity(name = "Person")
public static class Person {
@Id
@GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "person", cascade = CascadeType.ALL)
private List<Phone> phones = new ArrayList<>();
public Person() {
}
public Person(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public List<Phone> getPhones() {
return phones;
}
public void addPhone(Phone phone) {
phones.add(phone);
phone.setPerson(this);
}
}
@Entity(name = "Phone")
public static class Phone {
@Id
@GeneratedValue
private Long id;
@ManyToOne
private Person person;
private String number;
public Phone() {
}
public Phone(String number) {
this.number = number;
}
public Long getId() {
return id;
}
public String getNumber() {
return number;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
}
@Entity(name = "Advertisement")
public static class Advertisement {
@Id
@GeneratedValue
private Long id;
private String title;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
}

View File

@ -0,0 +1,214 @@
/*
* 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.jpa.test.userguide.flush;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import org.hibernate.Session;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.junit.Test;
import org.jboss.logging.Logger;
import static org.hibernate.jpa.test.util.TransactionUtil.doInJPA;
import static org.junit.Assert.assertTrue;
/**
* <code>AlwaysFlushTest</code> - Always Flush Test
*
* @author Vlad Mihalcea
*/
public class AutoFlushTest extends BaseEntityManagerFunctionalTestCase {
private static final Logger log = Logger.getLogger( AutoFlushTest.class );
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Person.class,
Advertisement.class
};
}
@Test
public void testFlushAutoCommit() {
EntityManager entityManager = null;
EntityTransaction txn = null;
try {
//tag::flushing-auto-flush-commit-example[]
entityManager = entityManagerFactory().createEntityManager();
txn = entityManager.getTransaction();
txn.begin();
Person person = new Person( "John Doe" );
entityManager.persist( person );
log.info( "Entity is in persisted state" );
txn.commit();
//end::flushing-auto-flush-commit-example[]
} catch (RuntimeException e) {
if ( txn != null && txn.isActive()) txn.rollback();
throw e;
} finally {
if (entityManager != null) {
entityManager.close();
}
}
}
@Test
public void testFlushAutoJPQL() {
doInJPA( this::entityManagerFactory, entityManager -> {
log.info( "testFlushAutoJPQL" );
//tag::flushing-auto-flush-jpql-example[]
Person person = new Person( "John Doe" );
entityManager.persist( person );
entityManager.createQuery( "select p from Advertisement p" ).getResultList();
entityManager.createQuery( "select p from Person p" ).getResultList();
//end::flushing-auto-flush-jpql-example[]
} );
}
@Test
public void testFlushAutoJPQLOverlap() {
doInJPA( this::entityManagerFactory, entityManager -> {
log.info( "testFlushAutoJPQLOverlap" );
//tag::flushing-auto-flush-jpql-overlap-example[]
Person person = new Person( "John Doe" );
entityManager.persist( person );
entityManager.createQuery( "select p from Person p" ).getResultList();
//end::flushing-auto-flush-jpql-overlap-example[]
} );
}
@Test
public void testFlushAutoSQL() {
doInJPA( this::entityManagerFactory, entityManager -> {
entityManager.createNativeQuery( "delete from Person" ).executeUpdate();;
} );
doInJPA( this::entityManagerFactory, entityManager -> {
log.info( "testFlushAutoSQL" );
//tag::flushing-auto-flush-sql-example[]
assertTrue(((Number) entityManager
.createNativeQuery( "select count(*) from Person")
.getSingleResult()).intValue() == 0 );
Person person = new Person( "John Doe" );
entityManager.persist( person );
assertTrue(((Number) entityManager
.createNativeQuery( "select count(*) from Person")
.getSingleResult()).intValue() == 1 );
//end::flushing-auto-flush-sql-example[]
} );
}
@Test
public void testFlushAutoSQLNativeSession() {
doInJPA( this::entityManagerFactory, entityManager -> {
entityManager.createNativeQuery( "delete from Person" ).executeUpdate();;
} );
doInJPA( this::entityManagerFactory, entityManager -> {
log.info( "testFlushAutoSQLNativeSession" );
//tag::flushing-auto-flush-sql-native-example[]
assertTrue(((Number) entityManager
.createNativeQuery( "select count(*) from Person")
.getSingleResult()).intValue() == 0 );
Person person = new Person( "John Doe" );
entityManager.persist( person );
Session session = entityManager.unwrap(Session.class);
assertTrue(((Number) session
.createSQLQuery( "select count(*) from Person")
.uniqueResult()).intValue() == 0 );
//end::flushing-auto-flush-sql-native-example[]
} );
}
@Test
public void testFlushAutoSQLSynchronization() {
doInJPA( this::entityManagerFactory, entityManager -> {
entityManager.createNativeQuery( "delete from Person" ).executeUpdate();;
} );
doInJPA( this::entityManagerFactory, entityManager -> {
log.info( "testFlushAutoSQLSynchronization" );
//tag::flushing-auto-flush-sql-synchronization-example[]
assertTrue(((Number) entityManager
.createNativeQuery( "select count(*) from Person")
.getSingleResult()).intValue() == 0 );
Person person = new Person( "John Doe" );
entityManager.persist( person );
Session session = entityManager.unwrap( Session.class );
assertTrue(((Number) session
.createSQLQuery( "select count(*) from Person")
.addSynchronizedEntityClass( Person.class )
.uniqueResult()).intValue() == 1 );
//end::flushing-auto-flush-sql-synchronization-example[]
} );
}
//tag::flushing-auto-flush-jpql-entity-example[]
@Entity(name = "Person")
public static class Person {
@Id
@GeneratedValue
private Long id;
private String name;
public Person() {}
public Person(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
}
@Entity(name = "Advertisement")
public static class Advertisement {
@Id
@GeneratedValue
private Long id;
private String title;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
//end::flushing-auto-flush-jpql-entity-example[]
}

View File

@ -0,0 +1,183 @@
/*
* 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.jpa.test.userguide.flush;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FlushModeType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.junit.Test;
import org.jboss.logging.Logger;
import static org.junit.Assert.assertTrue;
import static org.hibernate.jpa.test.util.TransactionUtil.doInJPA;
/**
* <code>CommitFlushTest</code> - Commit Flush Test
*
* @author Vlad Mihalcea
*/
public class CommitFlushTest extends BaseEntityManagerFunctionalTestCase {
private static final Logger log = Logger.getLogger( CommitFlushTest.class);
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[]{
Person.class,
Phone.class,
Advertisement.class,
};
}
@Test
public void testFlushJPQL() {
doInJPA( this::entityManagerFactory, entityManager -> {
log.info("testFlushJPQL");
//tag::flushing-commit-flush-jpql-example[]
Person person = new Person("John Doe");
entityManager.persist(person);
entityManager.createQuery("select p from Advertisement p")
.setFlushMode( FlushModeType.COMMIT)
.getResultList();
entityManager.createQuery("select p from Person p")
.setFlushMode( FlushModeType.COMMIT)
.getResultList();
//end::flushing-commit-flush-jpql-example[]
});
}
@Test
public void testFlushSQL() {
doInJPA( this::entityManagerFactory, entityManager -> {
entityManager.createNativeQuery("delete from Person")
.executeUpdate();
});
doInJPA( this::entityManagerFactory, entityManager -> {
log.info("testFlushSQL");
//tag::flushing-commit-flush-sql-example[]
Person person = new Person("John Doe");
entityManager.persist(person);
assertTrue(((Number) entityManager
.createNativeQuery("select count(*) from Person")
.getSingleResult()).intValue() == 1);
//end::flushing-commit-flush-sql-example[]
});
}
@Entity(name = "Person")
public static class Person {
@Id
@GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "person", cascade = CascadeType.ALL)
private List<Phone> phones = new ArrayList<>();
public Person() {
}
public Person(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public List<Phone> getPhones() {
return phones;
}
public void addPhone(Phone phone) {
phones.add(phone);
phone.setPerson(this);
}
}
@Entity(name = "Phone")
public static class Phone {
@Id
@GeneratedValue
private Long id;
@ManyToOne
private Person person;
private String number;
public Phone() {
}
public Phone(String number) {
this.number = number;
}
public Long getId() {
return id;
}
public String getNumber() {
return number;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
}
@Entity(name = "Advertisement")
public static class Advertisement {
@Id
@GeneratedValue
private Long id;
private String title;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
}

View File

@ -0,0 +1,171 @@
/*
* 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.jpa.test.userguide.flush;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import org.hibernate.FlushMode;
import org.hibernate.Session;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.junit.Test;
import org.jboss.logging.Logger;
import static org.hibernate.jpa.test.util.TransactionUtil.doInJPA;
import static org.junit.Assert.assertTrue;
/**
* <code>ManualFlushTest</code> - Manual Flush Test
*
* @author Vlad Mihalcea
*/
public class ManualFlushTest extends BaseEntityManagerFunctionalTestCase {
private static final Logger log = Logger.getLogger( ManualFlushTest.class);
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[]{
Person.class,
Phone.class,
Advertisement.class,
};
}
@Test
public void testFlushSQL() {
doInJPA( this::entityManagerFactory, entityManager -> {
entityManager.createNativeQuery("delete from Person").executeUpdate();
});
doInJPA( this::entityManagerFactory, entityManager -> {
log.info("testFlushSQL");
//tag::flushing-manual-flush-example[]
Person person = new Person("John Doe");
entityManager.persist(person);
Session session = entityManager.unwrap( Session.class);
session.setFlushMode( FlushMode.MANUAL);
assertTrue(((Number) entityManager
.createQuery("select count(id) from Person")
.getSingleResult()).intValue() == 0);
assertTrue(((Number) session
.createSQLQuery("select count(*) from Person")
.uniqueResult()).intValue() == 0);
//end::flushing-manual-flush-example[]
});
}
@Entity(name = "Person")
public static class Person {
@Id
@GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "person", cascade = CascadeType.ALL)
private List<Phone> phones = new ArrayList<>();
public Person() {
}
public Person(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public List<Phone> getPhones() {
return phones;
}
public void addPhone(Phone phone) {
phones.add(phone);
phone.setPerson(this);
}
}
@Entity(name = "Phone")
public static class Phone {
@Id
@GeneratedValue
private Long id;
@ManyToOne
private Person person;
private String number;
public Phone() {
}
public Phone(String number) {
this.number = number;
}
public Long getId() {
return id;
}
public String getNumber() {
return number;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
}
@Entity(name = "Advertisement")
public static class Advertisement {
@Id
@GeneratedValue
private Long id;
private String title;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
}

View File

@ -0,0 +1,3 @@
INSERT INTO Person (name, id) VALUES ('John Doe', 1)
SELECT COUNT(*) FROM Person

View File

@ -0,0 +1,2 @@
--INFO: Entity is in persisted state
INSERT INTO Person (name, id) VALUES ('John Doe', 1)

View File

@ -0,0 +1,9 @@
SELECT a.id AS id1_0_ ,
a.title AS title2_0_
FROM Advertisement a
INSERT INTO Person (name, id) VALUES ('John Doe', 1)
SELECT p.id AS id1_1_ ,
p.name AS name2_1_
FROM Person p

View File

@ -0,0 +1,5 @@
INSERT INTO Person (name, id) VALUES ('John Doe', 1)
SELECT p.id AS id1_1_ ,
p.name AS name2_1_
FROM Person p

View File

@ -0,0 +1,9 @@
SELECT a.id AS id1_0_ ,
a.title AS title2_0_
FROM Advertisement a
SELECT p.id AS id1_1_ ,
p.name AS name2_1_
FROM Person p
INSERT INTO Person (name, id) VALUES ('John Doe', 1)

View File

@ -0,0 +1,3 @@
INSERT INTO Person (name, id) VALUES ('John Doe', 1)
SELECT COUNT(*) FROM Person

View File

@ -0,0 +1,5 @@
SELECT COUNT(p.id) AS col_0_0_
FROM Person p
SELECT COUNT(*)
FROM Person

View File

@ -0,0 +1,107 @@
package org.hibernate.jpa.test.userguide.ql;
import java.util.Calendar;
import java.util.GregorianCalendar;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.junit.Test;
import static org.hibernate.jpa.test.util.TransactionUtil.doInJPA;
import static org.junit.Assert.assertEquals;
/**
* <code>BulkTest</code> - Bulk JPQL Test
*
* @author Vlad Mihalcea
*/
public class BulkTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Customer.class
};
}
@Test
public void testUpdate() {
final Calendar calendar = new GregorianCalendar();
doInJPA( this::entityManagerFactory, entityManager -> {
entityManager.persist( new Customer( "Vlad" ) );
entityManager.persist( new Customer( "Mihalcea" ) );
} );
doInJPA( this::entityManagerFactory, entityManager -> {
String oldName = "Vlad";
String newName = "Mr. Vlad";
String jpqlUpdate = "update Customer c set c.name = :newName where c.name = :oldName";
int updatedEntities = entityManager.createQuery( jpqlUpdate )
.setParameter( "oldName", oldName )
.setParameter( "newName", newName )
.executeUpdate();
assertEquals(1, updatedEntities);
} );
doInJPA( this::entityManagerFactory, entityManager -> {
String oldName = "Mr. Vlad";
String newName = "Vlad";
String jpqlUpdate = "update Customer set name = :newName where name = :oldName";
int updatedEntities = entityManager.createQuery( jpqlUpdate )
.setParameter( "oldName", oldName )
.setParameter( "newName", newName )
.executeUpdate();
assertEquals(1, updatedEntities);
} );
doInJPA( this::entityManagerFactory, entityManager -> {
String oldName = "Vlad";
String jpqlDelete = "delete Customer c where c.name = :oldName";
int updatedEntities = entityManager.createQuery( jpqlDelete )
.setParameter( "oldName", oldName )
.executeUpdate();
assertEquals(1, updatedEntities);
} );
doInJPA( this::entityManagerFactory, entityManager -> {
String oldName = "Mihalcea";
String jpqlDelete = "delete Customer where name = :oldName";
int updatedEntities = entityManager.createQuery( jpqlDelete )
.setParameter( "oldName", oldName )
.executeUpdate();
assertEquals(1, updatedEntities);
} );
}
@Entity(name = "Customer")
public static class Customer {
@Id
@GeneratedValue
private Long id;
private String name;
public Customer() {
}
public Customer(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
}
}