/* * 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.test.ops.multiLoad; import java.util.List; import javax.persistence.Cacheable; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.SharedCacheMode; import javax.persistence.Table; import org.hibernate.CacheMode; import org.hibernate.Session; import org.hibernate.annotations.BatchSize; import org.hibernate.boot.MetadataBuilder; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.cache.spi.access.AccessType; import org.hibernate.cfg.AvailableSettings; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; 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.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; /** * @author Steve Ebersole */ public class MultiLoadTest extends BaseNonConfigCoreFunctionalTestCase { @Override protected Class[] getAnnotatedClasses() { return new Class[] { SimpleEntity.class }; } @Override protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) { super.configureStandardServiceRegistryBuilder( ssrb ); ssrb.applySetting( AvailableSettings.USE_SECOND_LEVEL_CACHE, true ); } @Override protected void configureMetadataBuilder(MetadataBuilder metadataBuilder) { super.configureMetadataBuilder( metadataBuilder ); metadataBuilder.applySharedCacheMode( SharedCacheMode.ENABLE_SELECTIVE ); metadataBuilder.applyAccessType( AccessType.READ_WRITE ); } @Before public void before() { Session session = sessionFactory().openSession(); session.getTransaction().begin(); session.setCacheMode( CacheMode.IGNORE ); for ( int i = 1; i <= 60; i++ ) { session.save( new SimpleEntity( i, "Entity #" + i ) ); } session.getTransaction().commit(); session.close(); } @After public void after() { Session session = sessionFactory().openSession(); session.getTransaction().begin(); session.createQuery( "delete SimpleEntity" ).executeUpdate(); session.getTransaction().commit(); session.close(); } @Test public void testBasicMultiLoad() { doInHibernate( this::sessionFactory, session -> { List<SimpleEntity> list = session.byMultipleIds( SimpleEntity.class ).multiLoad( ids(56) ); assertEquals( 56, list.size() ); } ); } @Test @TestForIssue( jiraKey = "HHH-10984" ) public void testUnflushedDeleteAndThenMultiLoad() { doInHibernate( this::sessionFactory, session -> { // delete one of them (but do not flush)... session.delete( session.load( SimpleEntity.class, 5 ) ); // as a baseline, assert based on how load() handles it SimpleEntity s5 = session.load( SimpleEntity.class, 5 ); assertNotNull( s5 ); // and then, assert how get() handles it s5 = session.get( SimpleEntity.class, 5 ); assertNull( s5 ); // finally assert how multiLoad handles it List<SimpleEntity> list = session.byMultipleIds( SimpleEntity.class ).multiLoad( ids(56) ); assertEquals( 56, list.size() ); } ); } @Test @TestForIssue( jiraKey = "HHH-10617" ) public void testDuplicatedRequestedIds() { doInHibernate( this::sessionFactory, session -> { // ordered multiLoad List<SimpleEntity> list = session.byMultipleIds( SimpleEntity.class ).multiLoad( 1, 2, 3, 2, 2 ); assertEquals( 5, list.size() ); assertSame( list.get( 1 ), list.get( 3 ) ); assertSame( list.get( 1 ), list.get( 4 ) ); // un-ordered multiLoad list = session.byMultipleIds( SimpleEntity.class ).enableOrderedReturn( false ).multiLoad( 1, 2, 3, 2, 2 ); assertEquals( 3, list.size() ); } ); } @Test @TestForIssue( jiraKey = "HHH-10617" ) public void testNonExistentIdRequest() { doInHibernate( this::sessionFactory, session -> { // ordered multiLoad List<SimpleEntity> list = session.byMultipleIds( SimpleEntity.class ).multiLoad( 1, 699, 2 ); assertEquals( 3, list.size() ); assertNull( list.get( 1 ) ); // un-ordered multiLoad list = session.byMultipleIds( SimpleEntity.class ).enableOrderedReturn( false ).multiLoad( 1, 699, 2 ); assertEquals( 2, list.size() ); } ); } @Test public void testBasicMultiLoadWithManagedAndNoChecking() { Session session = openSession(); session.getTransaction().begin(); SimpleEntity first = session.byId( SimpleEntity.class ).load( 1 ); List<SimpleEntity> list = session.byMultipleIds( SimpleEntity.class ).multiLoad( ids(56) ); assertEquals( 56, list.size() ); // this check is HIGHLY specific to implementation in the batch loader // which puts existing managed entities first... assertSame( first, list.get( 0 ) ); session.getTransaction().commit(); session.close(); } @Test public void testBasicMultiLoadWithManagedAndChecking() { Session session = openSession(); session.getTransaction().begin(); SimpleEntity first = session.byId( SimpleEntity.class ).load( 1 ); List<SimpleEntity> list = session.byMultipleIds( SimpleEntity.class ).enableSessionCheck( true ).multiLoad( ids(56) ); assertEquals( 56, list.size() ); // this check is HIGHLY specific to implementation in the batch loader // which puts existing managed entities first... assertSame( first, list.get( 0 ) ); session.getTransaction().commit(); session.close(); } @Test public void testMultiLoadWithCacheModeIgnore() { // do the multi-load, telling Hibernate to IGNORE the L2 cache - // the end result should be that the cache is (still) empty afterwards Session session = openSession(); session.getTransaction().begin(); List<SimpleEntity> list = session.byMultipleIds( SimpleEntity.class ) .with( CacheMode.IGNORE ) .multiLoad( ids(56) ); session.getTransaction().commit(); session.close(); assertEquals( 56, list.size() ); for ( SimpleEntity entity : list ) { assertFalse( sessionFactory().getCache().containsEntity( SimpleEntity.class, entity.getId() ) ); } } @Test public void testMultiLoadClearsBatchFetchQueue() { final EntityKey entityKey = new EntityKey( 1, sessionFactory().getEntityPersister( SimpleEntity.class.getName() ) ); Session session = openSession(); session.getTransaction().begin(); // create a proxy, which should add an entry to the BatchFetchQueue SimpleEntity first = session.byId( SimpleEntity.class ).getReference( 1 ); assertTrue( ( (SessionImplementor) session ).getPersistenceContext().getBatchFetchQueue().containsEntityKey( entityKey ) ); // now bulk load, which should clean up the BatchFetchQueue entry List<SimpleEntity> list = session.byMultipleIds( SimpleEntity.class ).enableSessionCheck( true ).multiLoad( ids(56) ); assertEquals( 56, list.size() ); assertFalse( ( (SessionImplementor) session ).getPersistenceContext().getBatchFetchQueue().containsEntityKey( entityKey ) ); session.getTransaction().commit(); session.close(); } private Integer[] ids(int count) { Integer[] ids = new Integer[count]; for ( int i = 1; i <= count; i++ ) { ids[i-1] = i; } return ids; } @Entity( name = "SimpleEntity" ) @Table( name = "SimpleEntity" ) @Cacheable() @BatchSize( size = 15 ) public static class SimpleEntity { Integer id; String text; public SimpleEntity() { } public SimpleEntity(Integer id, String text) { this.id = id; this.text = text; } @Id public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getText() { return text; } public void setText(String text) { this.text = text; } } }