Migrate User Guide Batching chapter extras to test folder

This commit is contained in:
vladmihalcea 2016-01-28 16:50:17 +02:00
parent 03eb9fd276
commit f3386c37da
15 changed files with 486 additions and 267 deletions

View File

@ -1,6 +1,6 @@
[[batch]]
== Batching
:sourcedir: extras
:sourcedir: ../../../../../test/java/org/hibernate/userguide/batch
[[batch-jdbcbatch]]
=== JDBC batching
@ -39,52 +39,63 @@ The following settings control this behavior.
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.
The reason is that Hibernate caches all the newly inserted `Customer` instances in the session-level cache.
There are several problems associated to this example:
There are several ways to avoid this problem.
Before batch processing, enable JDBC batching. To enable JDBC batching, set the property `hibernate.jdbc.batch_size` to an integer between 10 and 50.
. 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.
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]
====
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
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`
====
[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.
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()`
====
[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` 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.
* A stateless session is a lower-level abstraction that is much closer to the underlying JDBC.
[[batch-stateless-session-example]]
.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`.
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
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
====
[source,java]
[source, JAVA, indent=0]
----
UPDATE FROM EntityName e WHERE e.name = ?
@ -143,40 +157,46 @@ DELETE FROM EntityName e WHERE e.name = ?
[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.
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.
[NOTE]
====
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.
====
[[batch-bulk-jpql-update-example]]
.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()`
====
[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.
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
====
[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.
====
[[batch-bulk-jpql-delete-example]]
.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
====
[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
====
[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,
in which case the seed value defined by the org.hibernate.type.VersionType is used.
[[batch-bulk-hql-insert-example]]
.HQL INSERT statement
====
[source,java]
[source, JAVA, indent=0]
----
include::{sourcedir}/hql_insert.java[]
include::{sourcedir}/BatchTest.java[tags=batch-bulk-hql-insert-example]
----
====

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

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

View File

@ -103,10 +103,10 @@ public class ExplicitLockingTest extends BaseEntityManagerFunctionalTestCase {
Person person = entityManager.find( Person.class, id );
Session session = entityManager.unwrap( Session.class );
session
.buildLockRequest( LockOptions.NONE )
.setLockMode( LockMode.PESSIMISTIC_READ )
.setTimeOut( LockOptions.NO_WAIT )
.lock( person );
.buildLockRequest( LockOptions.NONE )
.setLockMode( LockMode.PESSIMISTIC_READ )
.setTimeOut( LockOptions.NO_WAIT )
.lock( person );
//end::locking-buildLockRequest-example[]
} );
@ -116,11 +116,11 @@ public class ExplicitLockingTest extends BaseEntityManagerFunctionalTestCase {
Person person = entityManager.find( Person.class, id );
Session session = entityManager.unwrap( Session.class );
session
.buildLockRequest( LockOptions.NONE )
.setLockMode( LockMode.PESSIMISTIC_READ )
.setTimeOut( LockOptions.NO_WAIT )
.setScope( true )
.lock( person );
.buildLockRequest( LockOptions.NONE )
.setLockMode( LockMode.PESSIMISTIC_READ )
.setTimeOut( LockOptions.NO_WAIT )
.setScope( true )
.lock( person );
//end::locking-buildLockRequest-scope-example[]
} );

View File

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

View File

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