/* * 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.queryhint; import java.util.List; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.ManyToOne; import org.hibernate.Criteria; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Configuration; import org.hibernate.criterion.Restrictions; import org.hibernate.dialect.SQLServer2012Dialect; import org.hibernate.testing.RequiresDialect; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; /** * @author Brett Meyer */ @RequiresDialect(SQLServer2012Dialect.class) public class QueryHintSQLServer2012Test extends BaseCoreFunctionalTestCase { @Override protected Class<?>[] getAnnotatedClasses() { return new Class<?>[] { Employee.class, Department.class }; } @Override protected void configure(Configuration configuration) { configuration.setProperty( AvailableSettings.DIALECT, QueryHintTestSQLServer2012Dialect.class.getName() ); configuration.setProperty( AvailableSettings.USE_SQL_COMMENTS, "true" ); } @Test public void testQueryHint() { Department department = new Department(); department.name = "Sales"; Employee employee1 = new Employee(); employee1.department = department; Employee employee2 = new Employee(); employee2.department = department; Session s = openSession(); s.getTransaction().begin(); s.persist( department ); s.persist( employee1 ); s.persist( employee2 ); s.getTransaction().commit(); s.clear(); // test Query w/ a simple SQLServer2012 optimizer hint s.getTransaction().begin(); Query query = s.createQuery( "FROM QueryHintSQLServer2012Test$Employee e WHERE e.department.name = :departmentName" ).addQueryHint( "MAXDOP 2" ) .setParameter( "departmentName", "Sales" ); List results = query.list(); s.getTransaction().commit(); s.clear(); assertEquals( results.size(), 2 ); assertTrue( QueryHintTestSQLServer2012Dialect.getProcessedSql().contains( "OPTION (MAXDOP 2)" ) ); QueryHintTestSQLServer2012Dialect.resetProcessedSql(); // test multiple hints s.getTransaction().begin(); query = s.createQuery( "FROM QueryHintSQLServer2012Test$Employee e WHERE e.department.name = :departmentName" ) .addQueryHint("MAXDOP 2") .addQueryHint("CONCAT UNION") .setParameter("departmentName", "Sales"); results = query.list(); s.getTransaction().commit(); s.clear(); assertEquals( results.size(), 2 ); assertTrue( QueryHintTestSQLServer2012Dialect.getProcessedSql().contains( "MAXDOP 2" ) ); assertTrue( QueryHintTestSQLServer2012Dialect.getProcessedSql().contains( "CONCAT UNION" ) ); QueryHintTestSQLServer2012Dialect.resetProcessedSql(); // ensure the insertion logic can handle a comment appended to the front s.getTransaction().begin(); query = s.createQuery( "FROM QueryHintSQLServer2012Test$Employee e WHERE e.department.name = :departmentName" ).setComment( "this is a test" ) .addQueryHint( "MAXDOP 2" ) .setParameter( "departmentName", "Sales" ); results = query.list(); s.getTransaction().commit(); s.clear(); assertEquals( results.size(), 2 ); assertTrue( QueryHintTestSQLServer2012Dialect.getProcessedSql().contains( "OPTION (MAXDOP 2)" ) ); QueryHintTestSQLServer2012Dialect.resetProcessedSql(); // test Criteria s.getTransaction().begin(); Criteria criteria = s.createCriteria( Employee.class ).addQueryHint( "MAXDOP 2" ).createCriteria( "department" ) .add( Restrictions.eq( "name", "Sales" ) ); results = criteria.list(); s.getTransaction().commit(); s.close(); assertEquals( results.size(), 2 ); assertTrue( QueryHintTestSQLServer2012Dialect.getProcessedSql().contains( "OPTION (MAXDOP 2)" ) ); } /** * Since the query hint is added to the SQL during Loader's executeQueryStatement -> preprocessSQL, rather than * early on during the QueryTranslator or QueryLoader initialization, there's not an easy way to check the full SQL * after completely processing it. Instead, use this ridiculous hack to ensure Loader actually calls Dialect. TODO: * This is terrible. Better ideas? */ public static class QueryHintTestSQLServer2012Dialect extends SQLServer2012Dialect { private static String processedSql; @Override public String getQueryHintString(String sql, List<String> hints) { processedSql = super.getQueryHintString( sql, hints ); return processedSql; } public static String getProcessedSql() { return processedSql; } public static void resetProcessedSql() { processedSql = ""; } } @Entity public static class Employee { @Id @GeneratedValue public long id; @ManyToOne public Department department; } @Entity public static class Department { @Id @GeneratedValue public long id; public String name; } }