enable query hints for SQL Server 2012
This commit is contained in:
parent
1b9cd19ca7
commit
bbaeaa6c58
|
@ -23,7 +23,10 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.dialect;
|
package org.hibernate.dialect;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hibernate.internal.util.StringHelper;
|
||||||
/**
|
/**
|
||||||
* Microsoft SQL Server 2012 Dialect
|
* Microsoft SQL Server 2012 Dialect
|
||||||
*
|
*
|
||||||
|
@ -65,4 +68,29 @@ public class SQLServer2012Dialect extends SQLServer2008Dialect {
|
||||||
public String getQuerySequencesString() {
|
public String getQuerySequencesString() {
|
||||||
return "select name from sys.sequences";
|
return "select name from sys.sequences";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getQueryHintString(String sql, List<String> hints) {
|
||||||
|
final String hint = StringHelper.join(", ", hints.iterator());
|
||||||
|
|
||||||
|
if (StringHelper.isEmpty(hint)) {
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
final StringBuilder buffer = new StringBuilder(sql.length()
|
||||||
|
+ hint.length() + 12);
|
||||||
|
final int pos = sql.indexOf(";");
|
||||||
|
if (pos > -1) {
|
||||||
|
buffer.append(sql.substring(0, pos));
|
||||||
|
} else {
|
||||||
|
buffer.append(sql);
|
||||||
|
}
|
||||||
|
buffer.append(" OPTION (").append(hint).append(")");
|
||||||
|
if (pos > -1) {
|
||||||
|
buffer.append(";");
|
||||||
|
}
|
||||||
|
sql = buffer.toString();
|
||||||
|
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,177 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* JBoss, Home of Professional Open Source
|
||||||
|
* Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
|
||||||
|
* as indicated by the @authors tag. All rights reserved.
|
||||||
|
* See the copyright.txt in the distribution for a
|
||||||
|
* full listing of individual contributors.
|
||||||
|
*
|
||||||
|
* This copyrighted material is made available to anyone wishing to use,
|
||||||
|
* modify, copy, or redistribute it subject to the terms and conditions
|
||||||
|
* of the GNU Lesser General Public License, v. 2.1.
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT A
|
||||||
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License,
|
||||||
|
* v.2.1 along with this distribution; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
package org.hibernate.test.queryhint;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 QueryHintTest$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 QueryHintTest$Employee e WHERE e.department.name = :departmentName" ).addQueryHint( "MAXDOP 2" )
|
||||||
|
.addQueryHint( "USE_CONCAT" ).setParameter( "departmentName", "Sales" );
|
||||||
|
results = query.list();
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.clear();
|
||||||
|
|
||||||
|
assertEquals( results.size(), 2 );
|
||||||
|
assertTrue( QueryHintTestSQLServer2012Dialect.getProcessedSql().contains( "OPTION (MAXDOP 2)" ) );
|
||||||
|
|
||||||
|
QueryHintTestSQLServer2012Dialect.resetProcessedSql();
|
||||||
|
|
||||||
|
// ensure the insertion logic can handle a comment appended to the front
|
||||||
|
s.getTransaction().begin();
|
||||||
|
query = s.createQuery( "FROM QueryHintTest$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)" ) );
|
||||||
|
assertEquals( false, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue