Migrate User Guide Batching chapter extras to test folder
This commit is contained in:
parent
03eb9fd276
commit
f3386c37da
|
@ -1,6 +1,6 @@
|
||||||
[[batch]]
|
[[batch]]
|
||||||
== Batching
|
== Batching
|
||||||
:sourcedir: extras
|
:sourcedir: ../../../../../test/java/org/hibernate/userguide/batch
|
||||||
|
|
||||||
[[batch-jdbcbatch]]
|
[[batch-jdbcbatch]]
|
||||||
=== JDBC batching
|
=== JDBC batching
|
||||||
|
@ -39,52 +39,63 @@ The following settings control this behavior.
|
||||||
|
|
||||||
The following example shows an anti-pattern for batch inserts.
|
The following example shows an anti-pattern for batch inserts.
|
||||||
|
|
||||||
.Naive way to insert 100000 lines with Hibernate
|
[[batch-session-batch-example]]
|
||||||
|
.Naive way to insert 100 000 entities with Hibernate
|
||||||
====
|
====
|
||||||
[source,java]
|
[source, JAVA, indent=0]
|
||||||
----
|
----
|
||||||
include::{sourcedir}/batch_insert.java[]
|
include::{sourcedir}/BatchTest.java[tags=batch-session-batch-example]
|
||||||
----
|
----
|
||||||
====
|
====
|
||||||
|
|
||||||
This fails with an `OutOfMemoryException` after around 50000 rows on most systems.
|
There are several problems associated to this example:
|
||||||
The reason is that Hibernate caches all the newly inserted `Customer` instances in the session-level cache.
|
|
||||||
|
|
||||||
There are several ways to avoid this problem.
|
. Hibernate caches all the newly inserted `Customer` instances in the session-level c1ache, so, when the transaction ends, 100 000 entities are managed by the persistence context.
|
||||||
Before batch processing, enable JDBC batching. To enable JDBC batching, set the property `hibernate.jdbc.batch_size` to an integer between 10 and 50.
|
If the maximum memory allocated to the JVM is rather low, this example could fails with an `OutOfMemoryException`.
|
||||||
|
The Java 1.8 JVM allocated either 1/4 of available RAM or 1Gb, which can easily accommodate 100 000 objects on the heap.
|
||||||
|
. long-running transactions can deplete a connection pool so other transactions don't get a chance to proceed.
|
||||||
|
. JDBC batching is not enabled by default, so every insert statement requires a database roundtrip.
|
||||||
|
To enable JDBC batching, set the property `hibernate.jdbc.batch_size` to an integer between 10 and 50.
|
||||||
|
|
||||||
[NOTE]
|
[NOTE]
|
||||||
====
|
====
|
||||||
Hibernate disables insert batching at the JDBC level transparently if you use an identity identifier generator.
|
Hibernate disables insert batching at the JDBC level transparently if you use an identity identifier generator.
|
||||||
====
|
====
|
||||||
|
|
||||||
If the above approach is not appropriate, you can disable the second-level cache, by setting `hibernate.cache.use_second_level_cache` to `false`.
|
[[batch-session-batch-insert]]
|
||||||
|
|
||||||
==== Batch inserts
|
==== Batch inserts
|
||||||
|
|
||||||
When you make new objects persistent, employ methods `flush()` and `clear()` to the session regularly, to control the size of the first-level cache.
|
When you make new objects persistent, employ methods `flush()` and `clear()` to the session regularly, to control the size of the first-level cache.
|
||||||
|
|
||||||
|
[[batch-session-batch-insert-example]]
|
||||||
.Flushing and clearing the `Session`
|
.Flushing and clearing the `Session`
|
||||||
====
|
====
|
||||||
[source,java]
|
[source, JAVA, indent=0]
|
||||||
----
|
----
|
||||||
include::{sourcedir}/flush_and_clear_session.java[]
|
include::{sourcedir}/BatchTest.java[tags=batch-session-batch-insert-example]
|
||||||
----
|
----
|
||||||
====
|
====
|
||||||
|
|
||||||
==== Batch updates
|
[[batch-session-scroll]]
|
||||||
|
==== Session scroll
|
||||||
|
|
||||||
When you retrieve and update data, `flush()` and `clear()` the session regularly.
|
When you retrieve and update data, `flush()` and `clear()` the session regularly.
|
||||||
In addition, use method `scroll()` to take advantage of server-side cursors for queries that return many rows of data.
|
In addition, use method `scroll()` to take advantage of server-side cursors for queries that return many rows of data.
|
||||||
|
|
||||||
|
[[batch-session-scroll-example]]
|
||||||
.Using `scroll()`
|
.Using `scroll()`
|
||||||
====
|
====
|
||||||
[source,java]
|
[source, JAVA, indent=0]
|
||||||
----
|
----
|
||||||
include::{sourcedir}/using_scroll.java[]
|
include::{sourcedir}/BatchTest.java[tags=batch-session-scroll-example]
|
||||||
----
|
----
|
||||||
====
|
====
|
||||||
|
|
||||||
|
[IMPORTANT]
|
||||||
|
====
|
||||||
|
You should always close the `ScrollableResults`, to deallocate the associated `PreparedStatement` and avoid resource leaking.
|
||||||
|
====
|
||||||
|
|
||||||
==== StatelessSession
|
==== StatelessSession
|
||||||
|
|
||||||
`StatelessSession` is a command-oriented API provided by Hibernate.
|
`StatelessSession` is a command-oriented API provided by Hibernate.
|
||||||
|
@ -106,11 +117,12 @@ Limitations of `StatelessSession`:
|
||||||
* Due to the lack of a first-level cache, Stateless sessions are vulnerable to data aliasing effects.
|
* Due to the lack of a first-level cache, Stateless sessions are vulnerable to data aliasing effects.
|
||||||
* A stateless session is a lower-level abstraction that is much closer to the underlying JDBC.
|
* A stateless session is a lower-level abstraction that is much closer to the underlying JDBC.
|
||||||
|
|
||||||
|
[[batch-stateless-session-example]]
|
||||||
.Using a `StatelessSession`
|
.Using a `StatelessSession`
|
||||||
====
|
====
|
||||||
[source,java]
|
[source, JAVA, indent=0]
|
||||||
----
|
----
|
||||||
include::{sourcedir}/using_a_StatelessSession.java[]
|
include::{sourcedir}/BatchTest.java[tags=batch-stateless-session-example]
|
||||||
----
|
----
|
||||||
====
|
====
|
||||||
|
|
||||||
|
@ -127,13 +139,15 @@ 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`.
|
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).
|
Hibernate provides methods for bulk SQL-style DML statement execution, in the form of Hibernate Query Language (HQL).
|
||||||
|
|
||||||
|
[[batch-bulk-hql-update-delete]]
|
||||||
==== HQL/JPQL 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.
|
Both the Hibernate native Query Language and JPQL (Java Persistence Query Language) provide support for bulk UPDATE and DELETE.
|
||||||
|
|
||||||
|
[[batch-bulk-hql-update-delete-example]]
|
||||||
.Psuedo-syntax for UPDATE and DELETE statements using HQL
|
.Psuedo-syntax for UPDATE and DELETE statements using HQL
|
||||||
====
|
====
|
||||||
[source,java]
|
[source, JAVA, indent=0]
|
||||||
----
|
----
|
||||||
UPDATE FROM EntityName e WHERE e.name = ?
|
UPDATE FROM EntityName e WHERE e.name = ?
|
||||||
|
|
||||||
|
@ -143,40 +157,46 @@ DELETE FROM EntityName e WHERE e.name = ?
|
||||||
|
|
||||||
[NOTE]
|
[NOTE]
|
||||||
====
|
====
|
||||||
The `FROM` and `WHERE` clauses are each optional.
|
The `FROM` and `WHERE` clauses are each optional, but it's good practice to use them.
|
||||||
====
|
====
|
||||||
|
|
||||||
The `FROM` clause can only refer to a single entity, which can be aliased.
|
The `FROM` clause can only refer to a single entity, which can be aliased.
|
||||||
If the entity name is aliased, any property references must be qualified using that alias.
|
If the entity name is aliased, any property references must be qualified using that alias.
|
||||||
If the entity name is not aliased, then it is illegal for any property references to be qualified.
|
If the entity name is not aliased, then it is illegal for any property references to be qualified.
|
||||||
|
|
||||||
|
[NOTE]
|
||||||
|
====
|
||||||
Joins, either implicit or explicit, are prohibited in a bulk HQL query.
|
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.
|
You can use sub-queries in the `WHERE` clause, and the sub-queries themselves can contain joins.
|
||||||
|
====
|
||||||
|
|
||||||
|
[[batch-bulk-jpql-update-example]]
|
||||||
.Executing a JPQL `UPDATE`, using the `Query.executeUpdate()`
|
.Executing a JPQL `UPDATE`, using the `Query.executeUpdate()`
|
||||||
====
|
====
|
||||||
[source,java]
|
[source, JAVA, indent=0]
|
||||||
----
|
----
|
||||||
include::{sourcedir}/jpql_update.java[]
|
include::{sourcedir}/BatchTest.java[tags=batch-bulk-jpql-update-example]
|
||||||
----
|
----
|
||||||
====
|
====
|
||||||
|
|
||||||
|
[[batch-bulk-hql-update-example]]
|
||||||
.Executing an HQL `UPDATE`, using the `Query.executeUpdate()`
|
.Executing an HQL `UPDATE`, using the `Query.executeUpdate()`
|
||||||
====
|
====
|
||||||
[source,java]
|
[source, JAVA, indent=0]
|
||||||
----
|
----
|
||||||
include::{sourcedir}/hql_update.java[]
|
include::{sourcedir}/BatchTest.java[tags=batch-bulk-hql-update-example]
|
||||||
----
|
----
|
||||||
====
|
====
|
||||||
|
|
||||||
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.
|
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.
|
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.
|
||||||
|
|
||||||
|
[[batch-bulk-hql-update-version-example]]
|
||||||
.Updating the version of timestamp
|
.Updating the version of timestamp
|
||||||
====
|
====
|
||||||
[source,java]
|
[source, JAVA, indent=0]
|
||||||
----
|
----
|
||||||
include::{sourcedir}/updating_version.java[]
|
include::{sourcedir}/BatchTest.java[tags=batch-bulk-hql-update-version-example]
|
||||||
----
|
----
|
||||||
====
|
====
|
||||||
|
|
||||||
|
@ -187,19 +207,21 @@ If you use the `VERSIONED` statement, you cannot use custom version types, which
|
||||||
This feature is only available in HQL since it's not standardized by JPA.
|
This feature is only available in HQL since it's not standardized by JPA.
|
||||||
====
|
====
|
||||||
|
|
||||||
|
[[batch-bulk-jpql-delete-example]]
|
||||||
.A JPQL `DELETE` statement
|
.A JPQL `DELETE` statement
|
||||||
====
|
====
|
||||||
[source,java]
|
[source, JAVA, indent=0]
|
||||||
----
|
----
|
||||||
include::{sourcedir}/jpql_delete.java[]
|
include::{sourcedir}/BatchTest.java[tags=batch-bulk-jpql-delete-example]
|
||||||
----
|
----
|
||||||
====
|
====
|
||||||
|
|
||||||
|
[[batch-bulk-hql-delete-example]]
|
||||||
.An HQL `DELETE` statement
|
.An HQL `DELETE` statement
|
||||||
====
|
====
|
||||||
[source,java]
|
[source, JAVA, indent=0]
|
||||||
----
|
----
|
||||||
include::{sourcedir}/hql_delete.java[]
|
include::{sourcedir}/BatchTest.java[tags=batch-bulk-hql-delete-example]
|
||||||
----
|
----
|
||||||
====
|
====
|
||||||
|
|
||||||
|
@ -212,9 +234,12 @@ In the example of joined-subclass, a `DELETE` against one of the subclasses may
|
||||||
|
|
||||||
.Pseudo-syntax for INSERT statements
|
.Pseudo-syntax for INSERT statements
|
||||||
====
|
====
|
||||||
[source,java]
|
[source, JAVA, indent=0]
|
||||||
----
|
----
|
||||||
INSERT INTO EntityName properties_list SELECT properties_list FROM ...
|
INSERT INTO EntityName
|
||||||
|
properties_list
|
||||||
|
SELECT properties_list
|
||||||
|
FROM ...
|
||||||
----
|
----
|
||||||
====
|
====
|
||||||
|
|
||||||
|
@ -243,11 +268,12 @@ For properties mapped as either version or timestamp, the insert statement gives
|
||||||
You can either specify the property in the properties_list, in which case its value is taken from the corresponding select expressions, or omit it from the properties_list,
|
You can either specify the property in the properties_list, in which case its value is taken from the corresponding select expressions, or omit it from the properties_list,
|
||||||
in which case the seed value defined by the org.hibernate.type.VersionType is used.
|
in which case the seed value defined by the org.hibernate.type.VersionType is used.
|
||||||
|
|
||||||
|
[[batch-bulk-hql-insert-example]]
|
||||||
.HQL INSERT statement
|
.HQL INSERT statement
|
||||||
====
|
====
|
||||||
[source,java]
|
[source, JAVA, indent=0]
|
||||||
----
|
----
|
||||||
include::{sourcedir}/hql_insert.java[]
|
include::{sourcedir}/BatchTest.java[tags=batch-bulk-hql-insert-example]
|
||||||
----
|
----
|
||||||
====
|
====
|
||||||
|
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
Session session = sessionFactory.openSession();
|
|
||||||
Transaction tx = session.beginTransaction();
|
|
||||||
for ( int i=0; i<100000; i++ ) {
|
|
||||||
Customer customer = new Customer(.....);
|
|
||||||
session.save(customer);
|
|
||||||
}
|
|
||||||
tx.commit();
|
|
||||||
session.close();
|
|
|
@ -1,15 +0,0 @@
|
||||||
Session session = sessionFactory.openSession();
|
|
||||||
Transaction tx = session.beginTransaction();
|
|
||||||
|
|
||||||
for ( int i=0; i<100000; i++ ) {
|
|
||||||
Customer customer = new Customer(.....);
|
|
||||||
session.save(customer);
|
|
||||||
if ( i % 20 == 0 ) { //20, same as the JDBC batch size
|
|
||||||
//flush a batch of inserts and release memory:
|
|
||||||
session.flush();
|
|
||||||
session.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tx.commit();
|
|
||||||
session.close();
|
|
|
@ -1,11 +0,0 @@
|
||||||
Session session = sessionFactory.openSession();
|
|
||||||
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();
|
|
||||||
|
|
||||||
tx.commit();
|
|
||||||
session.close();
|
|
|
@ -1,9 +0,0 @@
|
||||||
Session session = sessionFactory.openSession();
|
|
||||||
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();
|
|
||||||
|
|
||||||
tx.commit();
|
|
||||||
session.close();
|
|
|
@ -1,12 +0,0 @@
|
||||||
Session session = sessionFactory.openSession();
|
|
||||||
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();
|
|
||||||
|
|
||||||
tx.commit();
|
|
||||||
session.close();
|
|
|
@ -1,11 +0,0 @@
|
||||||
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();
|
|
|
@ -1,12 +0,0 @@
|
||||||
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();
|
|
|
@ -1,9 +0,0 @@
|
||||||
Session session = sessionFactory.openSession();
|
|
||||||
Transaction tx = session.beginTransaction();
|
|
||||||
String hqlVersionedUpdate = "update versioned Customer set name = :newName where name = :oldName";
|
|
||||||
int updatedEntities = session.createQuery( hqlUpdate )
|
|
||||||
.setString( "newName", newName )
|
|
||||||
.setString( "oldName", oldName )
|
|
||||||
.executeUpdate();
|
|
||||||
tx.commit();
|
|
||||||
session.close();
|
|
|
@ -1,13 +0,0 @@
|
||||||
StatelessSession session = sessionFactory.openStatelessSession();
|
|
||||||
Transaction tx = session.beginTransaction();
|
|
||||||
|
|
||||||
ScrollableResults customers = session.getNamedQuery("GetCustomers")
|
|
||||||
.scroll(ScrollMode.FORWARD_ONLY);
|
|
||||||
while ( customers.next() ) {
|
|
||||||
Customer customer = (Customer) customers.get(0);
|
|
||||||
customer.updateStuff(...);
|
|
||||||
session.update(customer);
|
|
||||||
}
|
|
||||||
|
|
||||||
tx.commit();
|
|
||||||
session.close();
|
|
|
@ -1,19 +0,0 @@
|
||||||
Session session = sessionFactory.openSession();
|
|
||||||
Transaction tx = session.beginTransaction();
|
|
||||||
|
|
||||||
ScrollableResults customers = session.getNamedQuery("GetCustomers")
|
|
||||||
.setCacheMode(CacheMode.IGNORE)
|
|
||||||
.scroll(ScrollMode.FORWARD_ONLY);
|
|
||||||
int count=0;
|
|
||||||
while ( customers.next() ) {
|
|
||||||
Customer customer = (Customer) customers.get(0);
|
|
||||||
customer.updateStuff(...);
|
|
||||||
if ( ++count % 20 == 0 ) {
|
|
||||||
//flush a batch of updates and release memory:
|
|
||||||
session.flush();
|
|
||||||
session.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tx.commit();
|
|
||||||
session.close();
|
|
|
@ -0,0 +1,366 @@
|
||||||
|
/*
|
||||||
|
* 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.batch;
|
||||||
|
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.EntityTransaction;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.Version;
|
||||||
|
|
||||||
|
import org.hibernate.CacheMode;
|
||||||
|
import org.hibernate.ScrollMode;
|
||||||
|
import org.hibernate.ScrollableResults;
|
||||||
|
import org.hibernate.Session;
|
||||||
|
import org.hibernate.SessionFactory;
|
||||||
|
import org.hibernate.StatelessSession;
|
||||||
|
import org.hibernate.Transaction;
|
||||||
|
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||||
|
import org.hibernate.resource.transaction.spi.TransactionStatus;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
import static org.hibernate.userguide.util.TransactionUtil.doInJPA;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Vlad Mihalcea
|
||||||
|
*/
|
||||||
|
public class BatchTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
|
|
||||||
|
private static final Logger log = Logger.getLogger( BatchTest.class );
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class<?>[] getAnnotatedClasses() {
|
||||||
|
return new Class<?>[] {
|
||||||
|
Customer.class,
|
||||||
|
Partner.class
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testScroll() {
|
||||||
|
withScroll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStatelessSession() {
|
||||||
|
withStatelessSession();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBulk() {
|
||||||
|
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||||
|
entityManager.persist( new Customer( "Vlad" ) );
|
||||||
|
entityManager.persist( new Customer( "Mihalcea" ) );
|
||||||
|
} );
|
||||||
|
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||||
|
String oldName = "Vlad";
|
||||||
|
String newName = "Alexandru";
|
||||||
|
//tag::batch-bulk-jpql-update-example[]
|
||||||
|
int updatedEntities = entityManager.createQuery(
|
||||||
|
"update Customer c " +
|
||||||
|
"set c.name = :newName " +
|
||||||
|
"where c.name = :oldName" )
|
||||||
|
.setParameter( "oldName", oldName )
|
||||||
|
.setParameter( "newName", newName )
|
||||||
|
.executeUpdate();
|
||||||
|
//end::batch-bulk-jpql-update-example[]
|
||||||
|
assertEquals(1, updatedEntities);
|
||||||
|
} );
|
||||||
|
|
||||||
|
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||||
|
String oldName = "Alexandru";
|
||||||
|
String newName = "Vlad";
|
||||||
|
|
||||||
|
Session session = entityManager.unwrap( Session.class );
|
||||||
|
//tag::batch-bulk-hql-update-example[]
|
||||||
|
int updatedEntities = session.createQuery(
|
||||||
|
"update Customer " +
|
||||||
|
"set name = :newName " +
|
||||||
|
"where name = :oldName" )
|
||||||
|
.setParameter( "oldName", oldName )
|
||||||
|
.setParameter( "newName", newName )
|
||||||
|
.executeUpdate();
|
||||||
|
//end::batch-bulk-hql-update-example[]
|
||||||
|
assertEquals(1, updatedEntities);
|
||||||
|
} );
|
||||||
|
|
||||||
|
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||||
|
String oldName = "Vlad";
|
||||||
|
String newName = "Alexandru";
|
||||||
|
|
||||||
|
Session session = entityManager.unwrap( Session.class );
|
||||||
|
//tag::batch-bulk-hql-update-version-example[]
|
||||||
|
int updatedEntities = session.createQuery(
|
||||||
|
"update versioned Customer " +
|
||||||
|
"set name = :newName " +
|
||||||
|
"where name = :oldName" )
|
||||||
|
.setParameter( "oldName", oldName )
|
||||||
|
.setParameter( "newName", newName )
|
||||||
|
.executeUpdate();
|
||||||
|
//end::batch-bulk-hql-update-version-example[]
|
||||||
|
assertEquals(1, updatedEntities);
|
||||||
|
} );
|
||||||
|
|
||||||
|
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||||
|
String name = "Alexandru";
|
||||||
|
|
||||||
|
//tag::batch-bulk-jpql-delete-example[]
|
||||||
|
int deletedEntities = entityManager.createQuery(
|
||||||
|
"delete Customer c " +
|
||||||
|
"where c.name = :name" )
|
||||||
|
.setParameter( "name", name )
|
||||||
|
.executeUpdate();
|
||||||
|
//end::batch-bulk-jpql-delete-example[]
|
||||||
|
assertEquals(1, deletedEntities);
|
||||||
|
} );
|
||||||
|
|
||||||
|
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||||
|
String name = "Mihalcea";
|
||||||
|
|
||||||
|
Session session = entityManager.unwrap( Session.class );
|
||||||
|
//tag::batch-bulk-hql-insert-example[]
|
||||||
|
int insertedEntities = session.createQuery(
|
||||||
|
"insert into Partner (id, name) " +
|
||||||
|
"select c.id, c.name " +
|
||||||
|
"from Customer c " +
|
||||||
|
"where name = :name" )
|
||||||
|
.setParameter( "name", name )
|
||||||
|
.executeUpdate();
|
||||||
|
//end::batch-bulk-hql-insert-example[]
|
||||||
|
assertEquals(1, insertedEntities);
|
||||||
|
} );
|
||||||
|
|
||||||
|
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||||
|
String name = "Mihalcea";
|
||||||
|
|
||||||
|
Session session = entityManager.unwrap( Session.class );
|
||||||
|
//tag::batch-bulk-hql-delete-example[]
|
||||||
|
int deletedEntities = session.createQuery(
|
||||||
|
"delete Customer " +
|
||||||
|
"where name = :name" )
|
||||||
|
.setParameter( "name", name )
|
||||||
|
.executeUpdate();
|
||||||
|
//end::batch-bulk-hql-delete-example[]
|
||||||
|
assertEquals(1, deletedEntities);
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void withoutBatch() {
|
||||||
|
//tag::batch-session-batch-example[]
|
||||||
|
EntityManager entityManager = null;
|
||||||
|
EntityTransaction txn = null;
|
||||||
|
try {
|
||||||
|
entityManager = entityManagerFactory().createEntityManager();
|
||||||
|
|
||||||
|
txn = entityManager.getTransaction();
|
||||||
|
txn.begin();
|
||||||
|
|
||||||
|
for ( int i = 0; i < 100_000; i++ ) {
|
||||||
|
Customer customer = new Customer( String.format( "Customer %d", i ) );
|
||||||
|
entityManager.persist( customer );
|
||||||
|
}
|
||||||
|
|
||||||
|
txn.commit();
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
if ( txn != null && txn.isActive()) txn.rollback();
|
||||||
|
throw e;
|
||||||
|
} finally {
|
||||||
|
if (entityManager != null) {
|
||||||
|
entityManager.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//end::batch-session-batch-example[]
|
||||||
|
}
|
||||||
|
|
||||||
|
private void withBatch() {
|
||||||
|
int entityCount = 100;
|
||||||
|
//tag::batch-session-batch-insert-example[]
|
||||||
|
EntityManager entityManager = null;
|
||||||
|
EntityTransaction txn = null;
|
||||||
|
try {
|
||||||
|
entityManager = entityManagerFactory().createEntityManager();
|
||||||
|
|
||||||
|
txn = entityManager.getTransaction();
|
||||||
|
txn.begin();
|
||||||
|
|
||||||
|
int batchSize = 25;
|
||||||
|
|
||||||
|
for ( int i = 0; i < entityCount; ++i ) {
|
||||||
|
Customer customer = new Customer( String.format( "Customer %d", i ) );
|
||||||
|
entityManager.persist( customer );
|
||||||
|
|
||||||
|
if ( i % batchSize == 0 ) {
|
||||||
|
//flush a batch of inserts and release memory
|
||||||
|
entityManager.flush();
|
||||||
|
entityManager.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
txn.commit();
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
if ( txn != null && txn.isActive()) txn.rollback();
|
||||||
|
throw e;
|
||||||
|
} finally {
|
||||||
|
if (entityManager != null) {
|
||||||
|
entityManager.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//end::batch-session-batch-insert-example[]
|
||||||
|
}
|
||||||
|
|
||||||
|
private void withScroll() {
|
||||||
|
withBatch();
|
||||||
|
|
||||||
|
//tag::batch-session-scroll-example[]
|
||||||
|
EntityManager entityManager = null;
|
||||||
|
EntityTransaction txn = null;
|
||||||
|
ScrollableResults scrollableResults = null;
|
||||||
|
try {
|
||||||
|
entityManager = entityManagerFactory().createEntityManager();
|
||||||
|
|
||||||
|
txn = entityManager.getTransaction();
|
||||||
|
txn.begin();
|
||||||
|
|
||||||
|
int batchSize = 25;
|
||||||
|
|
||||||
|
Session session = entityManager.unwrap( Session.class );
|
||||||
|
|
||||||
|
scrollableResults = session
|
||||||
|
.createQuery( "select c from Customer c" )
|
||||||
|
.setCacheMode( CacheMode.IGNORE )
|
||||||
|
.scroll( ScrollMode.FORWARD_ONLY );
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
while ( scrollableResults.next() ) {
|
||||||
|
Customer customer = (Customer) scrollableResults.get( 0 );
|
||||||
|
processCustomer(customer);
|
||||||
|
if ( ++count % batchSize == 0 ) {
|
||||||
|
//flush a batch of updates and release memory:
|
||||||
|
entityManager.flush();
|
||||||
|
entityManager.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
txn.commit();
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
if ( txn != null && txn.isActive()) txn.rollback();
|
||||||
|
throw e;
|
||||||
|
} finally {
|
||||||
|
if (scrollableResults != null) {
|
||||||
|
scrollableResults.close();
|
||||||
|
}
|
||||||
|
if (entityManager != null) {
|
||||||
|
entityManager.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//end::batch-session-scroll-example[]
|
||||||
|
}
|
||||||
|
|
||||||
|
private void withStatelessSession() {
|
||||||
|
withBatch();
|
||||||
|
|
||||||
|
//tag::batch-stateless-session-example[]
|
||||||
|
StatelessSession statelessSession = null;
|
||||||
|
Transaction txn = null;
|
||||||
|
ScrollableResults scrollableResults = null;
|
||||||
|
try {
|
||||||
|
SessionFactory sessionFactory = entityManagerFactory().unwrap( SessionFactory.class );
|
||||||
|
statelessSession = sessionFactory.openStatelessSession();
|
||||||
|
|
||||||
|
txn = statelessSession.getTransaction();
|
||||||
|
txn.begin();
|
||||||
|
|
||||||
|
scrollableResults = statelessSession
|
||||||
|
.createQuery( "select c from Customer c" )
|
||||||
|
.scroll(ScrollMode.FORWARD_ONLY);
|
||||||
|
|
||||||
|
while ( scrollableResults.next() ) {
|
||||||
|
Customer customer = (Customer) scrollableResults.get( 0 );
|
||||||
|
processCustomer(customer);
|
||||||
|
statelessSession.update( customer );
|
||||||
|
}
|
||||||
|
|
||||||
|
txn.commit();
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
if ( txn != null && txn.getStatus() == TransactionStatus.ACTIVE) txn.rollback();
|
||||||
|
throw e;
|
||||||
|
} finally {
|
||||||
|
if (scrollableResults != null) {
|
||||||
|
scrollableResults.close();
|
||||||
|
}
|
||||||
|
if (statelessSession != null) {
|
||||||
|
statelessSession.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//end::batch-stateless-session-example[]
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processCustomer(Customer customer) {
|
||||||
|
if ( customer.getId() % 1000 == 0 ) {
|
||||||
|
log.infof( "Processing [%s]", customer.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Customer")
|
||||||
|
public static class Customer {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Version
|
||||||
|
private int version;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public Customer() {}
|
||||||
|
|
||||||
|
public Customer(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Partner")
|
||||||
|
public static class Partner {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Version
|
||||||
|
private int version;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public Partner() {}
|
||||||
|
|
||||||
|
public Partner(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -103,10 +103,10 @@ public class ExplicitLockingTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
Person person = entityManager.find( Person.class, id );
|
Person person = entityManager.find( Person.class, id );
|
||||||
Session session = entityManager.unwrap( Session.class );
|
Session session = entityManager.unwrap( Session.class );
|
||||||
session
|
session
|
||||||
.buildLockRequest( LockOptions.NONE )
|
.buildLockRequest( LockOptions.NONE )
|
||||||
.setLockMode( LockMode.PESSIMISTIC_READ )
|
.setLockMode( LockMode.PESSIMISTIC_READ )
|
||||||
.setTimeOut( LockOptions.NO_WAIT )
|
.setTimeOut( LockOptions.NO_WAIT )
|
||||||
.lock( person );
|
.lock( person );
|
||||||
//end::locking-buildLockRequest-example[]
|
//end::locking-buildLockRequest-example[]
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
@ -116,11 +116,11 @@ public class ExplicitLockingTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
Person person = entityManager.find( Person.class, id );
|
Person person = entityManager.find( Person.class, id );
|
||||||
Session session = entityManager.unwrap( Session.class );
|
Session session = entityManager.unwrap( Session.class );
|
||||||
session
|
session
|
||||||
.buildLockRequest( LockOptions.NONE )
|
.buildLockRequest( LockOptions.NONE )
|
||||||
.setLockMode( LockMode.PESSIMISTIC_READ )
|
.setLockMode( LockMode.PESSIMISTIC_READ )
|
||||||
.setTimeOut( LockOptions.NO_WAIT )
|
.setTimeOut( LockOptions.NO_WAIT )
|
||||||
.setScope( true )
|
.setScope( true )
|
||||||
.lock( person );
|
.lock( person );
|
||||||
//end::locking-buildLockRequest-scope-example[]
|
//end::locking-buildLockRequest-scope-example[]
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
|
|
@ -1,105 +0,0 @@
|
||||||
package org.hibernate.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.userguide.util.TransactionUtil.doInJPA;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
#
|
||||||
|
# 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>.
|
||||||
|
#
|
||||||
|
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||||
|
log4j.appender.stdout.Target=System.out
|
||||||
|
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||||
|
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
|
||||||
|
|
||||||
|
log4j.rootLogger=info, stdout
|
||||||
|
|
||||||
|
log4j.logger.org.hibernate=info
|
||||||
|
#log4j.logger.org.hibernate=warn
|
||||||
|
|
||||||
|
log4j.logger.org.hibernate.ejb=info
|
||||||
|
log4j.logger.org.hibernate.ejb.packaging=info
|
||||||
|
log4j.logger.org.hibernate.reflection=info
|
||||||
|
|
||||||
|
#log4j.logger.org.hibernate.engine.Cascades=warn
|
||||||
|
#log4j.logger.org.hibernate.hql=warn
|
||||||
|
|
||||||
|
### log just the SQL
|
||||||
|
log4j.logger.org.hibernate.SQL=debug
|
||||||
|
|
||||||
|
### log JDBC bind parameters ###
|
||||||
|
#log4j.logger.org.hibernate.type=info
|
||||||
|
log4j.logger.org.hibernate.type=trace
|
||||||
|
|
||||||
|
### log schema export/update ###
|
||||||
|
log4j.logger.org.hibernate.tool.hbm2ddl=info
|
||||||
|
|
||||||
|
### log HQL parse trees
|
||||||
|
#log4j.logger.org.hibernate.hql=warn
|
||||||
|
|
||||||
|
### log cache activity ###
|
||||||
|
#log4j.logger.org.hibernate.cache=warn
|
||||||
|
|
||||||
|
### log JDBC resource acquisition
|
||||||
|
#log4j.logger.org.hibernate.jdbc=warn
|
||||||
|
|
||||||
|
### enable the following line if you want to track down connection ###
|
||||||
|
### leakages when using DriverManagerConnectionProvider ###
|
||||||
|
#log4j.logger.org.hibernate.connection.DriverManagerConnectionProvider=trace
|
||||||
|
|
||||||
|
### When entity copy merge functionality is enabled using:
|
||||||
|
### hibernate.event.merge.entity_copy_observer=log, the following will
|
||||||
|
### provide information about merged entity copies.
|
||||||
|
#log4j.logger.org.hibernate.event.internal.EntityCopyAllowedLoggedObserver=warn
|
||||||
|
|
Loading…
Reference in New Issue