From 92fe27df85a23f42c37b00d68570b87a57f9de19 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Thu, 20 May 2021 16:17:19 +0200 Subject: [PATCH] HHH-14624 add test (cherry picked from commit 2952b60cc33660c676efa7e55c7811e3431aa21b) --- .../test/pagination/OraclePaginationTest.java | 155 ++++++----- .../OraclePaginationWithLocksTest.java | 253 ++++++++++++++++++ 2 files changed, 343 insertions(+), 65 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/pagination/OraclePaginationWithLocksTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/test/pagination/OraclePaginationTest.java b/hibernate-core/src/test/java/org/hibernate/test/pagination/OraclePaginationTest.java index fda480877b..8e4caf63c8 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/pagination/OraclePaginationTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/pagination/OraclePaginationTest.java @@ -16,6 +16,8 @@ import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; import org.hibernate.testing.RequiresDialect; import org.hibernate.testing.TestForIssue; +import org.junit.After; +import org.junit.Before; import org.junit.Test; import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; @@ -34,6 +36,94 @@ public class OraclePaginationTest extends BaseEntityManagerFunctionalTestCase { }; } + @Before + public void setUp() { + doInJPA( this::entityManagerFactory, entityManager -> { + entityManager.persist( new RootEntity( 1L, 7L, "t40", 2L ) ); + entityManager.persist( new RootEntity( 16L, 1L, "t47", 2L ) ); + entityManager.persist( new RootEntity( 11L, 2L, "t43", 2L ) ); + entityManager.persist( new RootEntity( 6L, 4L, "t31", 2L ) ); + entityManager.persist( new RootEntity( 15L, 1L, "t46", 2L ) ); + entityManager.persist( new RootEntity( 2L, 6L, "t39", 2L ) ); + entityManager.persist( new RootEntity( 14L, 1L, "t45", 2L ) ); + entityManager.persist( new RootEntity( 4L, 5L, "t38", 2L ) ); + entityManager.persist( new RootEntity( 8L, 2L, "t29", 2L ) ); + entityManager.persist( new RootEntity( 17L, 1L, "t48", 2L ) ); + entityManager.persist( new RootEntity( 3L, 3L, "t21", 2L ) ); + entityManager.persist( new RootEntity( 7L, 2L, "t23", 2L ) ); + entityManager.persist( new RootEntity( 9L, 2L, "t30", 2L ) ); + entityManager.persist( new RootEntity( 10L, 3L, "t42", 2L ) ); + entityManager.persist( new RootEntity( 12L, 1L, "t41", 2L ) ); + entityManager.persist( new RootEntity( 5L, 6L, "t37", 1L ) ); + entityManager.persist( new RootEntity( 13L, 1L, "t44", 1L ) ); + } ); + } + + @After + public void tearDown() { + doInJPA( this::entityManagerFactory, entityManager -> { + entityManager.createQuery( "delete from RootEntity" ).executeUpdate(); + } ); + } + + + @Test + @TestForIssue(jiraKey = "HHH-12087") + public void testPagination() { + doInJPA( this::entityManagerFactory, entityManager -> { + List rootEntitiesAllPages = getLimitedRows( entityManager, 0, 10 ); + + List rootEntitiesFirst = getLimitedRows( entityManager, 0, 5 ); + assertEquals( 5, rootEntitiesFirst.size() ); + List rootEntitiesSecond = getLimitedRows( entityManager, 5, 10 ); + assertEquals( 10, rootEntitiesSecond.size() ); + + assertEquals( rootEntitiesAllPages.get( 0 ).getId(), rootEntitiesFirst.get( 0 ).getId() ); + assertEquals( rootEntitiesAllPages.get( 1 ).getId(), rootEntitiesFirst.get( 1 ).getId() ); + assertEquals( rootEntitiesAllPages.get( 2 ).getId(), rootEntitiesFirst.get( 2 ).getId() ); + assertEquals( rootEntitiesAllPages.get( 3 ).getId(), rootEntitiesFirst.get( 3 ).getId() ); + assertEquals( rootEntitiesAllPages.get( 4 ).getId(), rootEntitiesFirst.get( 4 ).getId() ); + + assertEquals( rootEntitiesAllPages.get( 5 ).getId(), rootEntitiesSecond.get( 0 ).getId() ); + assertEquals( rootEntitiesAllPages.get( 6 ).getId(), rootEntitiesSecond.get( 1 ).getId() ); + assertEquals( rootEntitiesAllPages.get( 7 ).getId(), rootEntitiesSecond.get( 2 ).getId() ); + assertEquals( rootEntitiesAllPages.get( 8 ).getId(), rootEntitiesSecond.get( 3 ).getId() ); + assertEquals( rootEntitiesAllPages.get( 9 ).getId(), rootEntitiesSecond.get( 4 ).getId() ); + } ); + } + + @Test + public void testPaginationWithSetMaxResultsOnly() { + doInJPA( this::entityManagerFactory, entityManager -> { + CriteriaBuilder cb = entityManager.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery( RootEntity.class ); + Root c = cq.from( RootEntity.class ); + CriteriaQuery select = cq.select( c ).orderBy( cb.desc( c.get( "status" ) ) ); + TypedQuery typedQuery = entityManager.createQuery( select ); + typedQuery.setMaxResults( 10 ); + List resultList = typedQuery.getResultList(); + assertEquals( 10, resultList.size() ); + } ); + } + + private List getAllRows(EntityManager em) { + CriteriaBuilder cb = em.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery( RootEntity.class ); + Root c = cq.from( RootEntity.class ); + return em.createQuery( cq.select( c ).orderBy( cb.desc( c.get( "status" ) ) ) ).getResultList(); + } + + private List getLimitedRows(EntityManager em, int start, int end) { + CriteriaBuilder cb = em.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery( RootEntity.class ); + Root c = cq.from( RootEntity.class ); + CriteriaQuery select = cq.select( c ).orderBy( cb.desc( c.get( "status" ) ) ); + TypedQuery typedQuery = em.createQuery( select ); + typedQuery.setFirstResult( start ); + typedQuery.setMaxResults( end ); + return typedQuery.getResultList(); + } + @Entity(name = "RootEntity") @Table(name = "V_MYTABLE_LAST") public static class RootEntity implements Serializable { @@ -63,69 +153,4 @@ public class OraclePaginationTest extends BaseEntityManagerFunctionalTestCase { } } - @Test - @TestForIssue(jiraKey = "HHH-12087") - public void testPagination() throws Exception { - - doInJPA( this::entityManagerFactory, entityManager -> { - entityManager.persist( new RootEntity( 1L, 7L, "t40", 2L ) ); - entityManager.persist( new RootEntity( 16L, 1L, "t47", 2L ) ); - entityManager.persist( new RootEntity( 11L, 2L, "t43", 2L ) ); - entityManager.persist( new RootEntity( 6L, 4L, "t31", 2L ) ); - entityManager.persist( new RootEntity( 15L, 1L, "t46", 2L ) ); - entityManager.persist( new RootEntity( 2L, 6L, "t39", 2L ) ); - entityManager.persist( new RootEntity( 14L, 1L, "t45", 2L ) ); - entityManager.persist( new RootEntity( 4L, 5L, "t38", 2L ) ); - entityManager.persist( new RootEntity( 8L, 2L, "t29", 2L ) ); - entityManager.persist( new RootEntity( 17L, 1L, "t48", 2L ) ); - entityManager.persist( new RootEntity( 3L, 3L, "t21", 2L ) ); - entityManager.persist( new RootEntity( 7L, 2L, "t23", 2L ) ); - entityManager.persist( new RootEntity( 9L, 2L, "t30", 2L ) ); - entityManager.persist( new RootEntity( 10L, 3L, "t42", 2L ) ); - entityManager.persist( new RootEntity( 12L, 1L, "t41", 2L ) ); - entityManager.persist( new RootEntity( 5L, 6L, "t37", 1L ) ); - entityManager.persist( new RootEntity( 13L, 1L, "t44", 1L ) ); - } ); - - doInJPA( this::entityManagerFactory, entityManager -> { - List rootEntitiesAllPages = getLimitedRows( entityManager, 0, 10 ); - - List rootEntitiesFirst = getLimitedRows( entityManager, 0, 5 ); - List rootEntitiesSecond = getLimitedRows( entityManager, 5, 10 ); - - assertEquals( rootEntitiesAllPages.get( 0 ).getId(), rootEntitiesFirst.get( 0 ).getId() ); - assertEquals( rootEntitiesAllPages.get( 1 ).getId(), rootEntitiesFirst.get( 1 ).getId() ); - assertEquals( rootEntitiesAllPages.get( 2 ).getId(), rootEntitiesFirst.get( 2 ).getId() ); - assertEquals( rootEntitiesAllPages.get( 3 ).getId(), rootEntitiesFirst.get( 3 ).getId() ); - assertEquals( rootEntitiesAllPages.get( 4 ).getId(), rootEntitiesFirst.get( 4 ).getId() ); - - assertEquals( rootEntitiesAllPages.get( 5 ).getId(), rootEntitiesSecond.get( 0 ).getId() ); - assertEquals( rootEntitiesAllPages.get( 6 ).getId(), rootEntitiesSecond.get( 1 ).getId() ); - assertEquals( rootEntitiesAllPages.get( 7 ).getId(), rootEntitiesSecond.get( 2 ).getId() ); - assertEquals( rootEntitiesAllPages.get( 8 ).getId(), rootEntitiesSecond.get( 3 ).getId() ); - assertEquals( rootEntitiesAllPages.get( 9 ).getId(), rootEntitiesSecond.get( 4 ).getId() ); - } ); - } - - private List getAllRows(EntityManager em) { - CriteriaBuilder cb = em.getCriteriaBuilder(); - CriteriaQuery cq = cb.createQuery( RootEntity.class ); - Root c = cq.from( RootEntity.class ); - return em.createQuery( cq.select( c ).orderBy( cb.desc( c.get( "status" ) ) ) ).getResultList(); - } - - private List getLimitedRows(EntityManager em, int start, int end) { - CriteriaBuilder cb = em.getCriteriaBuilder(); - CriteriaQuery cq = cb.createQuery( RootEntity.class ); - Root c = cq.from( RootEntity.class ); - CriteriaQuery select = cq.select( c ).orderBy( cb.desc( c.get( "status" ) ) ); - TypedQuery typedQuery = em.createQuery( select ); - typedQuery.setFirstResult( start ); - typedQuery.setMaxResults( end ); - return typedQuery.getResultList(); - } - - private void createRootEntity(EntityManager entityManager, Long id, Long version, String caption, String status) { - - } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/pagination/OraclePaginationWithLocksTest.java b/hibernate-core/src/test/java/org/hibernate/test/pagination/OraclePaginationWithLocksTest.java new file mode 100644 index 0000000000..8aa64636ea --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/pagination/OraclePaginationWithLocksTest.java @@ -0,0 +1,253 @@ +package org.hibernate.test.pagination; + +import java.util.List; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Root; + +import org.hibernate.LockMode; +import org.hibernate.LockOptions; +import org.hibernate.cfg.Configuration; +import org.hibernate.cfg.Environment; +import org.hibernate.dialect.Oracle12cDialect; +import org.hibernate.resource.jdbc.spi.StatementInspector; + +import org.hibernate.testing.RequiresDialect; +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +@RequiresDialect(Oracle12cDialect.class) +@TestForIssue(jiraKey = "HHH-14624") +public class OraclePaginationWithLocksTest extends BaseCoreFunctionalTestCase { + private static final MostRecentStatementInspector mostRecentStatementInspector = new MostRecentStatementInspector(); + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Person.class }; + } + + @Override + protected void configure(Configuration configuration) { + super.configure( configuration ); + configuration.getProperties().put( Environment.STATEMENT_INSPECTOR, mostRecentStatementInspector ); + } + + @Before + public void setUp() { + inTransaction( + session -> { + for ( int i = 0; i < 20; i++ ) { + session.persist( new Person( "name" + i ) ); + } + session.persist( new Person( "for update" ) ); + } + ); + } + + @After + public void tearDown() { + inTransaction( + session -> + session.createQuery( "delete from Person" ).executeUpdate() + + ); + } + + @Test + public void testNativeQuery() { + inTransaction( + session -> { + final List people = session.createNativeQuery( "select * from Person for update" ) + .setMaxResults( 10 ) + .list(); + assertEquals( 10, people.size() ); + assertFalse( mostRecentStatementInspector.sqlContains( "fetch" ) ); + } + ); + + inTransaction( + session -> { + final List people = session.createNativeQuery( "select * from Person" ) + .setMaxResults( 10 ) + .list(); + assertEquals( 10, people.size() ); + assertTrue( mostRecentStatementInspector.sqlContains( "fetch" ) ); + } + ); + + inTransaction( + session -> { + final List people = session.createNativeQuery( "select * from Person" ) + .setFirstResult( 3 ) + .setMaxResults( 10 ) + .list(); + assertEquals( 10, people.size() ); + assertTrue( mostRecentStatementInspector.sqlContains( "fetch" ) ); + } + ); + } + + @Test + public void testCriteriaQuery() { + inTransaction( + session -> { + final CriteriaQuery query = session.getCriteriaBuilder().createQuery( Person.class ); + final Root root = query.from( Person.class ); + query.select( root ); + final List people = session.createQuery( query ) + .setMaxResults( 10 ) + .setLockOptions( new LockOptions( LockMode.PESSIMISTIC_WRITE ).setFollowOnLocking( false ) ) + .getResultList(); + assertEquals( 10, people.size() ); + assertFalse( mostRecentStatementInspector.sqlContains( "fetch" ) ); + } + ); + + inTransaction( + session -> { + final CriteriaQuery query = session.getCriteriaBuilder().createQuery( Person.class ); + final Root root = query.from( Person.class ); + query.select( root ); + final List people = session.createQuery( query ) + .setMaxResults( 10 ) + .getResultList(); + assertEquals( 10, people.size() ); + assertTrue( mostRecentStatementInspector.sqlContains( "fetch" ) ); + } + ); + + inTransaction( + session -> { + final CriteriaQuery query = session.getCriteriaBuilder().createQuery( Person.class ); + final Root root = query.from( Person.class ); + query.select( root ); + final List people = session.createQuery( query ) + .setMaxResults( 10 ) + .setFirstResult( 2 ) + .getResultList(); + assertEquals( 10, people.size() ); + assertTrue( mostRecentStatementInspector.sqlContains( "fetch" ) ); + } + ); + + } + + @Test + public void testHqlQuery() { + inTransaction( + session -> { + List people = session.createQuery( + "select p from Person p", Person.class ) + .setMaxResults( 10 ) + .setLockOptions( new LockOptions( LockMode.PESSIMISTIC_WRITE ).setFollowOnLocking( false ) ) + .getResultList(); + assertEquals( 10, people.size() ); + assertFalse( mostRecentStatementInspector.sqlContains( "fetch" ) ); + } + ); + + inTransaction( + session -> { + List people = session.createQuery( + "select p from Person p", Person.class ) + .setMaxResults( 10 ) + .getResultList(); + assertEquals( 10, people.size() ); + assertTrue( mostRecentStatementInspector.sqlContains( "fetch" ) ); + } + ); + + inTransaction( + session -> { + List people = session.createQuery( + "select p from Person p", Person.class ) + .setFirstResult( 2 ) + .setMaxResults( 10 ) + .getResultList(); + assertEquals( 10, people.size() ); + assertEquals( 10, people.size() ); + assertTrue( mostRecentStatementInspector.sqlContains( "fetch" ) ); + } + ); + + inTransaction( + session -> { + List people = session.createQuery( + "select p from Person p where p.name = 'for update'", Person.class ) + .setMaxResults( 10 ) + .setLockOptions( new LockOptions( LockMode.PESSIMISTIC_WRITE ).setFollowOnLocking( false ) ) + .getResultList(); + assertEquals( 1, people.size() ); + assertFalse( mostRecentStatementInspector.sqlContains( "fetch" ) ); + } + ); + + inTransaction( + session -> { + List people = session.createQuery( + "select p from Person p where p.name = 'for update'", Person.class ) + .setMaxResults( 10 ) + .getResultList(); + assertEquals( 1, people.size() ); + assertTrue( mostRecentStatementInspector.sqlContains( "fetch" ) ); + } + ); + + + } + + private static class MostRecentStatementInspector implements StatementInspector { + private String mostRecentSql; + + public String inspect(String sql) { + mostRecentSql = sql; + return sql; + } + + public boolean sqlContains(String toCheck) { + return mostRecentSql.contains( toCheck ); + } + + } + + @Entity(name = "Person") + public static class Person { + @Id + @GeneratedValue + private Long id; + + private String name; + + public Person() { + } + + public Person(String name) { + this.name = name; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } +}