From cb62334f4646353693ddb85ac3960cf54899a8e7 Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Fri, 22 Sep 2017 18:10:19 -0700 Subject: [PATCH] HHH-11970 : @NotFound(IGNORE) and @BatchSize (cherry picked from commit 19087d9f15ca84b98cc32821fc41936ff8419235) --- .../internal/BatchFetchQueueHelper.java | 86 +++++ .../loader/entity/BatchingEntityLoader.java | 10 + .../DynamicBatchingEntityLoaderBuilder.java | 15 +- .../LegacyBatchingEntityLoaderBuilder.java | 18 +- .../PaddedBatchingEntityLoaderBuilder.java | 9 +- .../LegacyBatchingEntityLoaderBuilder.java | 20 +- ...chFetchNotFoundIgnoreDefaultStyleTest.java | 339 ++++++++++++++++++ ...chFetchNotFoundIgnoreDynamicStyleTest.java | 23 ++ ...tchFetchNotFoundIgnorePaddedStyleTest.java | 24 ++ 9 files changed, 539 insertions(+), 5 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/engine/internal/BatchFetchQueueHelper.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/batchfetch/BatchFetchNotFoundIgnoreDefaultStyleTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/batchfetch/BatchFetchNotFoundIgnoreDynamicStyleTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/batchfetch/BatchFetchNotFoundIgnorePaddedStyleTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/BatchFetchQueueHelper.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/BatchFetchQueueHelper.java new file mode 100644 index 0000000000..c54387a09d --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/BatchFetchQueueHelper.java @@ -0,0 +1,86 @@ +/* + * 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 . + */ +package org.hibernate.engine.internal; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.hibernate.engine.spi.BatchFetchQueue; +import org.hibernate.engine.spi.EntityKey; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.internal.CoreMessageLogger; +import org.hibernate.persister.entity.EntityPersister; + +import org.jboss.logging.Logger; + +/** + * @author Gail Badner + */ +public class BatchFetchQueueHelper { + private static final CoreMessageLogger LOG = Logger.getMessageLogger( + CoreMessageLogger.class, + BatchFetchQueueHelper.class.getName() + ); + + private BatchFetchQueueHelper(){ + } + + /** + * Finds the IDs for entities that were not found when the batch was loaded, and removes + * the corresponding entity keys from the {@link BatchFetchQueue}. + * + * @param ids - the IDs for the entities that were batch loaded + * @param results - the results from loading the batch + * @param persister - the entity persister for the entities in batch + * @param session - the session + */ + public static void removeNotFoundBatchLoadableEntityKeys( + Serializable[] ids, + List results, + EntityPersister persister, + SharedSessionContractImplementor session) { + if ( !persister.isBatchLoadable() ) { + return; + } + if ( ids.length == results.size() ) { + return; + } + LOG.debug( "Not all entities were loaded." ); + Set idSet = new HashSet<>( Arrays.asList( ids ) ); + for ( Object result : results ) { + final Serializable id = session.getPersistenceContext().getEntry( result ).getId(); + idSet.remove( id ); + } + assert idSet.size() == ids.length - results.size(); + if ( LOG.isDebugEnabled() ) { + LOG.debug( "Entities of type [" + persister.getEntityName() + "] not found; IDs: " + idSet ); + } + for ( Serializable id : idSet ) { + removeBatchLoadableEntityKey( id, persister, session ); + } + } + + /** + * Remove the entity key with the specified {@code id} and {@code persister} from + * the batch loadable entities {@link BatchFetchQueue}. + * + * @param id - the ID for the entity to be removed + * @param persister - the entity persister + * @param session - the session + */ + public static void removeBatchLoadableEntityKey( + Serializable id, + EntityPersister persister, + SharedSessionContractImplementor session) { + final EntityKey entityKey = session.generateEntityKey( id, persister ); + final BatchFetchQueue batchFetchQueue = session.getPersistenceContext().getBatchFetchQueue(); + batchFetchQueue.removeBatchLoadableEntityKey( entityKey ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/entity/BatchingEntityLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/entity/BatchingEntityLoader.java index ee2232c18f..1af95359f3 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/entity/BatchingEntityLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/entity/BatchingEntityLoader.java @@ -12,6 +12,7 @@ import java.util.Arrays; import java.util.List; import org.hibernate.LockOptions; +import org.hibernate.engine.internal.BatchFetchQueueHelper; import org.hibernate.engine.spi.QueryParameters; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.loader.Loader; @@ -97,6 +98,15 @@ public abstract class BatchingEntityLoader implements UniqueEntityLoader { try { final List results = loaderToUse.doQueryAndInitializeNonLazyCollections( session, qp, false ); log.debug( "Done entity batch load" ); + // The EntityKey for any entity that is not found will remain in the batch. + // Explicitly remove the EntityKeys for entities that were not found to + // avoid including them in future batches that get executed. + BatchFetchQueueHelper.removeNotFoundBatchLoadableEntityKeys( + ids, + results, + persister(), + session + ); return getObjectFromList(results, id, session); } catch ( SQLException sqle ) { diff --git a/hibernate-core/src/main/java/org/hibernate/loader/entity/DynamicBatchingEntityLoaderBuilder.java b/hibernate-core/src/main/java/org/hibernate/loader/entity/DynamicBatchingEntityLoaderBuilder.java index de6261e3d1..f0f54f9152 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/entity/DynamicBatchingEntityLoaderBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/entity/DynamicBatchingEntityLoaderBuilder.java @@ -18,6 +18,7 @@ import java.util.List; import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.dialect.pagination.LimitHelper; +import org.hibernate.engine.internal.BatchFetchQueueHelper; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.LoadQueryInfluencers; @@ -343,7 +344,13 @@ public class DynamicBatchingEntityLoaderBuilder extends BatchingEntityLoaderBuil final int numberOfIds = ArrayHelper.countNonNull( batch ); if ( numberOfIds <= 1 ) { - return singleKeyLoader.load( id, optionalObject, session ); + final Object result = singleKeyLoader.load( id, optionalObject, session ); + if ( result == null ) { + // There was no entity with the specified ID. Make sure the EntityKey does not remain + // in the batch to avoid including it in future batches that get executed. + BatchFetchQueueHelper.removeBatchLoadableEntityKey( id, persister(), session ); + } + return result; } final Serializable[] idsToLoad = new Serializable[numberOfIds]; @@ -355,6 +362,12 @@ public class DynamicBatchingEntityLoaderBuilder extends BatchingEntityLoaderBuil QueryParameters qp = buildQueryParameters( id, idsToLoad, optionalObject, lockOptions ); List results = dynamicLoader.doEntityBatchFetch( session, qp, idsToLoad ); + + // The EntityKey for any entity that is not found will remain in the batch. + // Explicitly remove the EntityKeys for entities that were not found to + // avoid including them in future batches that get executed. + BatchFetchQueueHelper.removeNotFoundBatchLoadableEntityKeys( idsToLoad, results, persister(), session ); + return getObjectFromList( results, id, session ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/entity/LegacyBatchingEntityLoaderBuilder.java b/hibernate-core/src/main/java/org/hibernate/loader/entity/LegacyBatchingEntityLoaderBuilder.java index 5494c75acf..a3017fd289 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/entity/LegacyBatchingEntityLoaderBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/entity/LegacyBatchingEntityLoaderBuilder.java @@ -11,6 +11,7 @@ import java.util.List; import org.hibernate.LockMode; import org.hibernate.LockOptions; +import org.hibernate.engine.internal.BatchFetchQueueHelper; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; @@ -100,10 +101,25 @@ public class LegacyBatchingEntityLoaderBuilder extends BatchingEntityLoaderBuild persister(), lockOptions ); + // The EntityKey for any entity that is not found will remain in the batch. + // Explicitly remove the EntityKeys for entities that were not found to + // avoid including them in future batches that get executed. + BatchFetchQueueHelper.removeNotFoundBatchLoadableEntityKeys( + smallBatch, + results, + persister(), + session + ); return getObjectFromList(results, id, session); //EARLY EXIT } } - return ( (UniqueEntityLoader) loaders[batchSizes.length-1] ).load(id, optionalObject, session); + final Object result = ( (UniqueEntityLoader) loaders[batchSizes.length-1] ).load(id, optionalObject, session); + if ( result == null ) { + // There was no entity with the specified ID. Make sure the EntityKey does not remain + // in the batch to avoid including it in future batches that get executed. + BatchFetchQueueHelper.removeBatchLoadableEntityKey( id, persister(), session ); + } + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/entity/PaddedBatchingEntityLoaderBuilder.java b/hibernate-core/src/main/java/org/hibernate/loader/entity/PaddedBatchingEntityLoaderBuilder.java index ff0d8a385f..e9ffb9e96a 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/entity/PaddedBatchingEntityLoaderBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/entity/PaddedBatchingEntityLoaderBuilder.java @@ -11,6 +11,7 @@ import java.io.Serializable; import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.LockOptions; +import org.hibernate.engine.internal.BatchFetchQueueHelper; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; @@ -96,7 +97,13 @@ class PaddedBatchingEntityLoaderBuilder extends BatchingEntityLoaderBuilder { final int numberOfIds = ArrayHelper.countNonNull( batch ); if ( numberOfIds <= 1 ) { - return ( (UniqueEntityLoader) loaders[batchSizes.length-1] ).load( id, optionalObject, session ); + final Object result = ( (UniqueEntityLoader) loaders[batchSizes.length-1] ).load( id, optionalObject, session ); + if ( result == null ) { + // There was no entity with the specified ID. Make sure the EntityKey does not remain + // in the batch to avoid including it in future batches that get executed. + BatchFetchQueueHelper.removeBatchLoadableEntityKey( id, persister(), session ); + } + return result; } // Uses the first batch-size bigger than the number of actual ids in the batch diff --git a/hibernate-core/src/main/java/org/hibernate/loader/entity/plan/LegacyBatchingEntityLoaderBuilder.java b/hibernate-core/src/main/java/org/hibernate/loader/entity/plan/LegacyBatchingEntityLoaderBuilder.java index 2f204e86dc..9580f8e0ad 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/entity/plan/LegacyBatchingEntityLoaderBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/entity/plan/LegacyBatchingEntityLoaderBuilder.java @@ -11,6 +11,7 @@ import java.util.List; import org.hibernate.LockMode; import org.hibernate.LockOptions; +import org.hibernate.engine.internal.BatchFetchQueueHelper; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; @@ -106,12 +107,27 @@ public class LegacyBatchingEntityLoaderBuilder extends AbstractBatchingEntityLoa persister(), lockOptions ); + // The EntityKey for any entity that is not found will remain in the batch. + // Explicitly remove the EntityKeys for entities that were not found to + // avoid including them in future batches that get executed. + BatchFetchQueueHelper.removeNotFoundBatchLoadableEntityKeys( + smallBatch, + results, + persister(), + session + ); + //EARLY EXIT return getObjectFromList( results, id, session ); } } - return ( loaders[batchSizes.length-1] ).load( id, optionalObject, session, lockOptions ); + final Object result = ( loaders[batchSizes.length-1] ).load( id, optionalObject, session, lockOptions ); + if ( result == null ) { + // There was no entity with the specified ID. Make sure the EntityKey does not remain + // in the batch to avoid including it in future batches that get executed. + BatchFetchQueueHelper.removeBatchLoadableEntityKey( id, persister(), session ); + } + return result; } } - } diff --git a/hibernate-core/src/test/java/org/hibernate/test/batchfetch/BatchFetchNotFoundIgnoreDefaultStyleTest.java b/hibernate-core/src/test/java/org/hibernate/test/batchfetch/BatchFetchNotFoundIgnoreDefaultStyleTest.java new file mode 100644 index 0000000000..01871c44c2 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/batchfetch/BatchFetchNotFoundIgnoreDefaultStyleTest.java @@ -0,0 +1,339 @@ +/* + * 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 . + */ +package org.hibernate.test.batchfetch; + +import java.util.ArrayList; +import java.util.List; +import javax.persistence.ConstraintMode; +import javax.persistence.Entity; +import javax.persistence.ForeignKey; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.OneToOne; + +import org.hibernate.Session; +import org.hibernate.annotations.BatchSize; +import org.hibernate.annotations.NotFound; +import org.hibernate.annotations.NotFoundAction; +import org.hibernate.cfg.Configuration; +import org.hibernate.cfg.Environment; +import org.hibernate.engine.spi.BatchFetchQueue; +import org.hibernate.engine.spi.EntityKey; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.resource.jdbc.spi.StatementInspector; + +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +/** + * @author Gail Badner + * @author Stephen Fikes + */ +public class BatchFetchNotFoundIgnoreDefaultStyleTest extends BaseCoreFunctionalTestCase { + private static final AStatementInspector statementInspector = new AStatementInspector(); + private static final int NUMBER_OF_EMPLOYEES = 8; + + private List tasks = new ArrayList<>(); + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Employee.class, Task.class }; + } + + @Override + protected void configure(Configuration configuration) { + super.configure( configuration ); + configuration.getProperties().put( Environment.STATEMENT_INSPECTOR, statementInspector ); + } + + @Before + public void createData() { + tasks.clear(); + tasks = doInHibernate( + this::sessionFactory, session -> { + for (int i = 0 ; i < NUMBER_OF_EMPLOYEES ; i++) { + Task task = new Task(); + task.id = i; + tasks.add( task ); + session.persist( task ); + Employee e = new Employee("employee0" + i); + e.task = task; + session.persist(e); + } + return tasks; + } + ); + } + + @After + public void deleteData() { + doInHibernate( + this::sessionFactory, session -> { + session.createQuery( "delete from Task" ).executeUpdate(); + session.createQuery( "delete from Employee" ).executeUpdate(); + } + ); + } + + @Test + public void testSeveralNotFoundFromQuery() { + + doInHibernate( + this::sessionFactory, session -> { + // delete 2nd and 8th Task so that the non-found Task entities will be queried + // in 2 different batches. + session.delete( tasks.get( 1 ) ); + session.delete( tasks.get( 7 ) ); + } + ); + + statementInspector.clear(); + + final List employees = doInHibernate( + this::sessionFactory, session -> { + List results = + session.createQuery( "from Employee e order by e.id", Employee.class ).getResultList(); + for ( int i = 0 ; i < tasks.size() ; i++ ) { + checkInBatchFetchQueue( tasks.get( i ).id, session, false ); + } + return results; + } + ); + + final List paramterCounts = statementInspector.parameterCounts; + + // there should be 4 SQL statements executed + assertEquals( 4, paramterCounts.size() ); + + // query loading Employee entities shouldn't have any parameters + assertEquals( 0, paramterCounts.get( 0 ).intValue() ); + + // query specifically for Task with ID == 0 will result in 1st batch; + // query should have 5 parameters for [0,1,2,3,4]; + // Task with ID == 1 won't be found; the rest will be found. + assertEquals( 5, paramterCounts.get( 1 ).intValue() ); + + // query specifically for Task with ID == 1 will result in 2nd batch; + // query should have 4 parameters [1,5,6,7]; + // Task with IDs == [1,7] won't be found; the rest will be found. + assertEquals( 4, paramterCounts.get( 2 ).intValue() ); + + // no extra queries required to load entities with IDs [2,3,4] because they + // were already loaded from 1st batch + + // no extra queries required to load entities with IDs [5,6] because they + // were already loaded from 2nd batch + + // query specifically for Task with ID == 7 will result in just querying + // Task with ID == 7 (because the batch is empty). + // query should have 1 parameter [7]; + // Task with ID == 7 won't be found. + assertEquals( 1, paramterCounts.get( 3 ).intValue() ); + + assertEquals( NUMBER_OF_EMPLOYEES, employees.size() ); + for ( int i = 0 ; i < NUMBER_OF_EMPLOYEES ; i++ ) { + if ( i == 1 || i == 7 ) { + assertNull( employees.get( i ).task ); + } + else { + assertEquals( tasks.get( i ).id, employees.get( i ).task.id ); + } + } + } + + @Test + public void testMostNotFoundFromQuery() { + + doInHibernate( + this::sessionFactory, session -> { + // delete all but last Task entity + for ( int i = 0; i < 7; i++ ) { + session.delete( tasks.get( i ) ); + } + } + ); + + statementInspector.clear(); + + final List employees = doInHibernate( + this::sessionFactory, session -> { + List results = + session.createQuery( "from Employee e order by e.id", Employee.class ).getResultList(); + for ( int i = 0 ; i < tasks.size() ; i++ ) { + checkInBatchFetchQueue( tasks.get( i ).id, session, false ); + } + return results; + } + ); + + final List paramterCounts = statementInspector.parameterCounts; + + // there should be 8 SQL statements executed + assertEquals( 8, paramterCounts.size() ); + + // query loading Employee entities shouldn't have any parameters + assertEquals( 0, paramterCounts.get( 0 ).intValue() ); + + // query specifically for Task with ID == 0 will result in 1st batch; + // query should have 5 parameters for [0,1,2,3,4]; + // Task with IDs == [0,1,2,3,4] won't be found + assertEquals( 5, paramterCounts.get( 1 ).intValue() ); + + // query specifically for Task with ID == 1 will result in 2nd batch; + // query should have 4 parameters [1,5,6,7]; + // Task with IDs == [1,5,6] won't be found; Task with ID == 7 will be found. + assertEquals( 4, paramterCounts.get( 2 ).intValue() ); + + // query specifically for Task with ID == 2 will result in just querying + // Task with ID == 2 (because the batch is empty). + // query should have 1 parameter [2]; + // Task with ID == 2 won't be found. + assertEquals( 1, paramterCounts.get( 3 ).intValue() ); + + // query specifically for Task with ID == 3 will result in just querying + // Task with ID == 3 (because the batch is empty). + // query should have 1 parameter [3]; + // Task with ID == 3 won't be found. + assertEquals( 1, paramterCounts.get( 4 ).intValue() ); + + // query specifically for Task with ID == 4 will result in just querying + // Task with ID == 4 (because the batch is empty). + // query should have 1 parameter [4]; + // Task with ID == 4 won't be found. + assertEquals( 1, paramterCounts.get( 5 ).intValue() ); + + // query specifically for Task with ID == 5 will result in just querying + // Task with ID == 5 (because the batch is empty). + // query should have 1 parameter [5]; + // Task with ID == 5 won't be found. + assertEquals( 1, paramterCounts.get( 6 ).intValue() ); + + // query specifically for Task with ID == 6 will result in just querying + // Task with ID == 6 (because the batch is empty). + // query should have 1 parameter [6]; + // Task with ID == 6 won't be found. + assertEquals( 1, paramterCounts.get( 7 ).intValue() ); + + // no extra queries required to load entity with ID == 7 because it + // was already loaded from 2nd batch + + assertEquals( NUMBER_OF_EMPLOYEES, employees.size() ); + + for ( int i = 0 ; i < NUMBER_OF_EMPLOYEES ; i++ ) { + if ( i == 7 ) { + assertEquals( tasks.get( i ).id, employees.get( i ).task.id ); + } + else { + assertNull( employees.get( i ).task ); + } + } + } + + @Test + public void testNotFoundFromGet() { + + doInHibernate( + this::sessionFactory, session -> { + // delete task so it is not found later when getting the Employee. + session.delete( tasks.get( 0 ) ); + } + ); + + statementInspector.clear(); + + doInHibernate( + this::sessionFactory, session -> { + Employee employee = session.get( Employee.class, "employee00" ); + checkInBatchFetchQueue( tasks.get( 0 ).id, session, false ); + assertNotNull( employee ); + assertNull( employee.task ); + } + ); + + final List paramterCounts = statementInspector.parameterCounts; + + // there should be 2 SQL statements executed + // 1) query to load Employee entity by ID (associated Tasks is registered for batch loading) + // 2) batch will only contain the ID for the associated Task (which will not be found) + assertEquals( 2, paramterCounts.size() ); + + // query loading Employee entities shouldn't have any parameters + assertEquals( 1, paramterCounts.get( 0 ).intValue() ); + + // Will result in just querying a single Task (because the batch is empty). + // query should have 1 parameter; + // Task won't be found. + assertEquals( 1, paramterCounts.get( 1 ).intValue() ); + } + + private static void checkInBatchFetchQueue(long id, Session session, boolean expected) { + final SessionImplementor sessionImplementor = (SessionImplementor) session; + final EntityPersister persister = + sessionImplementor.getFactory().getMetamodel().entityPersister( Task.class ); + final BatchFetchQueue batchFetchQueue = + sessionImplementor.getPersistenceContext().getBatchFetchQueue(); + assertEquals( expected, batchFetchQueue.containsEntityKey( new EntityKey( id, persister ) ) ); + } + + @Entity(name = "Employee") + public static class Employee { + @Id + private String name; + + @OneToOne(optional = true) + @JoinColumn(foreignKey = @ForeignKey(value = ConstraintMode.NO_CONSTRAINT)) + @NotFound(action = NotFoundAction.IGNORE) + private Task task; + + private Employee() { + } + + private Employee(String name) { + this.name = name; + } + } + + @Entity(name = "Task") + @BatchSize(size = 5) + public static class Task { + @Id + private long id; + + public Task() { + } + } + + public static class AStatementInspector implements StatementInspector { + private List parameterCounts = new ArrayList<>(); + + public String inspect(String sql) { + parameterCounts.add( countParameters( sql ) ); + return sql; + } + private void clear() { + parameterCounts.clear(); + } + private int countParameters(String sql) { + int count = 0; + int parameterIndex = sql.indexOf( '?' ); + while ( parameterIndex >= 0 ) { + count++; + parameterIndex = sql.indexOf( '?', parameterIndex + 1 ); + } + return count; + } + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/batchfetch/BatchFetchNotFoundIgnoreDynamicStyleTest.java b/hibernate-core/src/test/java/org/hibernate/test/batchfetch/BatchFetchNotFoundIgnoreDynamicStyleTest.java new file mode 100644 index 0000000000..18aebd94a1 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/batchfetch/BatchFetchNotFoundIgnoreDynamicStyleTest.java @@ -0,0 +1,23 @@ +/* + * 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 . + */ +package org.hibernate.test.batchfetch; + +import org.hibernate.cfg.Configuration; +import org.hibernate.cfg.Environment; +import org.hibernate.loader.BatchFetchStyle; + +/** + * @author Gail Badner + */ +public class BatchFetchNotFoundIgnoreDynamicStyleTest extends BatchFetchNotFoundIgnoreDefaultStyleTest { + + @Override + protected void configure(Configuration configuration) { + super.configure( configuration ); + configuration.getProperties().put( Environment.BATCH_FETCH_STYLE, BatchFetchStyle.DYNAMIC ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/batchfetch/BatchFetchNotFoundIgnorePaddedStyleTest.java b/hibernate-core/src/test/java/org/hibernate/test/batchfetch/BatchFetchNotFoundIgnorePaddedStyleTest.java new file mode 100644 index 0000000000..e02a726612 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/batchfetch/BatchFetchNotFoundIgnorePaddedStyleTest.java @@ -0,0 +1,24 @@ +/* + * 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 . + */ +package org.hibernate.test.batchfetch; + +import org.hibernate.cfg.Configuration; +import org.hibernate.cfg.Environment; +import org.hibernate.loader.BatchFetchStyle; + +/** + * @author Gail Badner + */ +public class BatchFetchNotFoundIgnorePaddedStyleTest extends BatchFetchNotFoundIgnoreDefaultStyleTest { + + @Override + protected void configure(Configuration configuration) { + super.configure( configuration ); + configuration.getProperties().put( Environment.BATCH_FETCH_STYLE, BatchFetchStyle.PADDED ); + } + +}