diff --git a/hibernate-core/src/test/java/org/hibernate/test/queryhint/QueryHintHANATest.java b/hibernate-core/src/test/java/org/hibernate/test/queryhint/QueryHintHANATest.java new file mode 100644 index 0000000000..fb8070fef1 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/queryhint/QueryHintHANATest.java @@ -0,0 +1,198 @@ +/* + * 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.queryhint; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +import java.util.List; +import java.util.Map; + +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToOne; + +import org.hibernate.Criteria; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.criterion.Restrictions; +import org.hibernate.dialect.AbstractHANADialect; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.query.Query; +import org.hibernate.test.util.jdbc.PreparedStatementSpyConnectionProvider; +import org.hibernate.testing.RequiresDialect; +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; +import org.junit.Test; + +/** + * @author Jonathan Bregler + */ +@RequiresDialect(AbstractHANADialect.class) +public class QueryHintHANATest extends BaseNonConfigCoreFunctionalTestCase { + + private PreparedStatementSpyConnectionProvider connectionProvider; + + @Override + protected void addSettings(Map settings) { + settings.put( AvailableSettings.USE_SQL_COMMENTS, "true" ); + settings.put( + org.hibernate.cfg.AvailableSettings.CONNECTION_PROVIDER, + connectionProvider ); + } + + @Override + protected void buildResources() { + connectionProvider = new PreparedStatementSpyConnectionProvider( false, false ); + super.buildResources(); + } + + @Override + public void releaseResources() { + super.releaseResources(); + connectionProvider.stop(); + } + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[]{ Employee.class, Department.class }; + } + + @Override + protected void afterSessionFactoryBuilt(SessionFactoryImplementor sessionFactory) { + Department department = new Department(); + department.name = "Sales"; + Employee employee1 = new Employee(); + employee1.department = department; + Employee employee2 = new Employee(); + employee2.department = department; + + doInHibernate( this::sessionFactory, s -> { + s.persist( department ); + s.persist( employee1 ); + s.persist( employee2 ); + } ); + } + + @Test + public void testQueryHint() { + + connectionProvider.clear(); + + doInHibernate( this::sessionFactory, s -> { + Query query = s.createQuery( "FROM QueryHintHANATest$Employee e WHERE e.department.name = :departmentName", Employee.class ) + .addQueryHint( "NO_CS_JOIN" ) + .setParameter( "departmentName", "Sales" ); + List results = query.list(); + + assertEquals( results.size(), 2 ); + } ); + + assertEquals( + 1, + connectionProvider.getPreparedStatements().size() ); + assertThat( connectionProvider.getPreparedSQLStatements().get( 0 ), containsString( " with hint (NO_CS_JOIN)" ) ); + connectionProvider.clear(); + + // test multiple hints + doInHibernate( this::sessionFactory, s -> { + Query query = s.createQuery( "FROM QueryHintHANATest$Employee e WHERE e.department.name = :departmentName", Employee.class ) + .addQueryHint( "NO_CS_JOIN" ) + .addQueryHint( "OPTIMIZE_METAMODEL" ) + .setParameter( "departmentName", "Sales" ); + List results = query.list(); + + assertEquals( results.size(), 2 ); + } ); + + assertEquals( + 1, + connectionProvider.getPreparedStatements().size() ); + assertThat( connectionProvider.getPreparedSQLStatements().get( 0 ), containsString( " with hint (NO_CS_JOIN,OPTIMIZE_METAMODEL)" ) ); + connectionProvider.clear(); + + // ensure the insertion logic can handle a comment appended to the front + doInHibernate( this::sessionFactory, s -> { + Query query = s.createQuery( "FROM QueryHintHANATest$Employee e WHERE e.department.name = :departmentName", Employee.class ) + .setComment( "this is a test" ) + .addQueryHint( "NO_CS_JOIN" ) + .setParameter( "departmentName", "Sales" ); + List results = query.list(); + + assertEquals( results.size(), 2 ); + } ); + + assertEquals( + 1, + connectionProvider.getPreparedStatements().size() ); + assertThat( connectionProvider.getPreparedSQLStatements().get( 0 ), containsString( " with hint (NO_CS_JOIN)" ) ); + connectionProvider.clear(); + + // test Criteria + doInHibernate( this::sessionFactory, s -> { + Criteria criteria = s.createCriteria( Employee.class ) + .addQueryHint( "NO_CS_JOIN" ) + .createCriteria( "department" ).add( Restrictions.eq( "name", "Sales" ) ); + List results = criteria.list(); + + assertEquals( results.size(), 2 ); + } ); + + assertEquals( + 1, + connectionProvider.getPreparedStatements().size() ); + assertThat( connectionProvider.getPreparedSQLStatements().get( 0 ), containsString( " with hint (NO_CS_JOIN)" ) ); + connectionProvider.clear(); + } + + @Test + @TestForIssue(jiraKey = "HHH-12362") + public void testQueryHintAndComment() { + connectionProvider.clear(); + + doInHibernate( this::sessionFactory, s -> { + Query query = s.createQuery( "FROM QueryHintHANATest$Employee e WHERE e.department.name = :departmentName", Employee.class ) + .addQueryHint( "NO_CS_JOIN" ) + .setComment( "My_Query" ) + .setParameter( "departmentName", "Sales" ); + List results = query.list(); + + assertEquals( results.size(), 2 ); + } ); + + assertEquals( + 1, + connectionProvider.getPreparedStatements().size() ); + assertThat( connectionProvider.getPreparedSQLStatements().get( 0 ), containsString( " with hint (NO_CS_JOIN)" ) ); + assertThat( connectionProvider.getPreparedSQLStatements().get( 0 ), containsString( "/* My_Query */ select" ) ); + connectionProvider.clear(); + } + + @Entity + public static class Employee { + + @Id + @GeneratedValue + public long id; + + @ManyToOne(fetch = FetchType.LAZY) + public Department department; + } + + @Entity + public static class Department { + + @Id + @GeneratedValue + public long id; + + public String name; + } +}