HHH-12362 - Allow both SQL query hints and comments
This commit is contained in:
parent
6e912cec0f
commit
3ebb949dcc
|
@ -2985,7 +2985,7 @@ public abstract class Dialect implements ConversionContext {
|
|||
if ( parameters.getQueryHints() != null && parameters.getQueryHints().size() > 0 ) {
|
||||
sql = getQueryHintString( sql, parameters.getQueryHints() );
|
||||
}
|
||||
else if ( commentsEnabled && parameters.getComment() != null ){
|
||||
if ( commentsEnabled && parameters.getComment() != null ){
|
||||
sql = prependComment( sql, parameters.getComment() );
|
||||
}
|
||||
|
||||
|
|
|
@ -7,31 +7,58 @@
|
|||
package org.hibernate.test.queryhint;
|
||||
|
||||
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.Query;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.criterion.Restrictions;
|
||||
import org.hibernate.dialect.Oracle8iDialect;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.query.Query;
|
||||
|
||||
import org.hibernate.testing.RequiresDialect;
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.hibernate.test.util.jdbc.PreparedStatementSpyConnectionProvider;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
/**
|
||||
* @author Brett Meyer
|
||||
*/
|
||||
@RequiresDialect( Oracle8iDialect.class )
|
||||
public class QueryHintTest extends BaseCoreFunctionalTestCase {
|
||||
public class QueryHintTest 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();
|
||||
super.buildResources();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releaseResources() {
|
||||
super.releaseResources();
|
||||
connectionProvider.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
|
@ -39,13 +66,7 @@ public class QueryHintTest extends BaseCoreFunctionalTestCase {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void configure(Configuration configuration) {
|
||||
configuration.setProperty( AvailableSettings.DIALECT, QueryHintTestDialect.class.getName() );
|
||||
configuration.setProperty( AvailableSettings.USE_SQL_COMMENTS, "true" );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryHint() {
|
||||
protected void afterSessionFactoryBuilt(SessionFactoryImplementor sessionFactory) {
|
||||
Department department = new Department();
|
||||
department.name = "Sales";
|
||||
Employee employee1 = new Employee();
|
||||
|
@ -53,93 +74,110 @@ public class QueryHintTest extends BaseCoreFunctionalTestCase {
|
|||
Employee employee2 = new Employee();
|
||||
employee2.department = department;
|
||||
|
||||
Session s = openSession();
|
||||
s.getTransaction().begin();
|
||||
s.persist( department ); s.persist( employee1 );
|
||||
doInHibernate( this::sessionFactory, s -> {
|
||||
s.persist( department );
|
||||
s.persist( employee1 );
|
||||
s.persist( employee2 );
|
||||
s.getTransaction().commit();
|
||||
s.clear();
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryHint() {
|
||||
|
||||
connectionProvider.clear();
|
||||
|
||||
// test Query w/ a simple Oracle optimizer hint
|
||||
s.getTransaction().begin();
|
||||
doInHibernate( this::sessionFactory, s -> {
|
||||
Query query = s.createQuery( "FROM QueryHintTest$Employee e WHERE e.department.name = :departmentName" )
|
||||
.addQueryHint( "ALL_ROWS" )
|
||||
.setParameter( "departmentName", "Sales" );
|
||||
List results = query.list();
|
||||
s.getTransaction().commit();
|
||||
s.clear();
|
||||
|
||||
assertEquals(results.size(), 2);
|
||||
assertTrue(QueryHintTestDialect.getProcessedSql().contains( "select /*+ ALL_ROWS */"));
|
||||
} );
|
||||
|
||||
QueryHintTestDialect.resetProcessedSql();
|
||||
assertEquals(
|
||||
1,
|
||||
connectionProvider.getPreparedStatements().size()
|
||||
);
|
||||
assertNotNull( connectionProvider.getPreparedSQLStatements().get( 0 ).contains( "select /*+ ALL_ROWS */" ) );
|
||||
connectionProvider.clear();
|
||||
|
||||
// test multiple hints
|
||||
s.getTransaction().begin();
|
||||
query = s.createQuery( "FROM QueryHintTest$Employee e WHERE e.department.name = :departmentName" )
|
||||
doInHibernate( this::sessionFactory, s -> {
|
||||
Query query = s.createQuery( "FROM QueryHintTest$Employee e WHERE e.department.name = :departmentName" )
|
||||
.addQueryHint( "ALL_ROWS" )
|
||||
.addQueryHint( "USE_CONCAT" )
|
||||
.setParameter( "departmentName", "Sales" );
|
||||
results = query.list();
|
||||
s.getTransaction().commit();
|
||||
s.clear();
|
||||
List results = query.list();
|
||||
|
||||
assertEquals(results.size(), 2);
|
||||
assertTrue(QueryHintTestDialect.getProcessedSql().contains( "select /*+ ALL_ROWS, USE_CONCAT */"));
|
||||
} );
|
||||
|
||||
QueryHintTestDialect.resetProcessedSql();
|
||||
assertEquals(
|
||||
1,
|
||||
connectionProvider.getPreparedStatements().size()
|
||||
);
|
||||
assertNotNull( connectionProvider.getPreparedSQLStatements().get( 0 ).contains( "select /*+ ALL_ROWS, USE_CONCAT */" ) );
|
||||
connectionProvider.clear();
|
||||
|
||||
// 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" )
|
||||
doInHibernate( this::sessionFactory, s -> {
|
||||
Query query = s.createQuery( "FROM QueryHintTest$Employee e WHERE e.department.name = :departmentName" )
|
||||
.setComment( "this is a test" )
|
||||
.addQueryHint( "ALL_ROWS" )
|
||||
.setParameter( "departmentName", "Sales" );
|
||||
results = query.list();
|
||||
s.getTransaction().commit();
|
||||
s.clear();
|
||||
List results = query.list();
|
||||
|
||||
assertEquals(results.size(), 2);
|
||||
assertTrue(QueryHintTestDialect.getProcessedSql().contains( "select /*+ ALL_ROWS */"));
|
||||
} );
|
||||
|
||||
QueryHintTestDialect.resetProcessedSql();
|
||||
assertEquals(
|
||||
1,
|
||||
connectionProvider.getPreparedStatements().size()
|
||||
);
|
||||
assertNotNull( connectionProvider.getPreparedSQLStatements().get( 0 ).contains( "select /*+ ALL_ROWS */" ) );
|
||||
connectionProvider.clear();
|
||||
|
||||
// test Criteria
|
||||
s.getTransaction().begin();
|
||||
doInHibernate( this::sessionFactory, s -> {
|
||||
Criteria criteria = s.createCriteria( Employee.class )
|
||||
.addQueryHint( "ALL_ROWS" )
|
||||
.createCriteria( "department" ).add( Restrictions.eq( "name", "Sales" ) );
|
||||
results = criteria.list();
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
List results = criteria.list();
|
||||
|
||||
assertEquals(results.size(), 2);
|
||||
assertTrue(QueryHintTestDialect.getProcessedSql().contains( "select /*+ ALL_ROWS */"));
|
||||
} );
|
||||
|
||||
assertEquals(
|
||||
1,
|
||||
connectionProvider.getPreparedStatements().size()
|
||||
);
|
||||
assertNotNull( connectionProvider.getPreparedSQLStatements().get( 0 ).contains( "select /*+ ALL_ROWS */" ) );
|
||||
connectionProvider.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 QueryHintTestDialect extends Oracle8iDialect {
|
||||
private static String processedSql;
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-12362" )
|
||||
public void testQueryHintAndComment() {
|
||||
connectionProvider.clear();
|
||||
|
||||
@Override
|
||||
public String getQueryHintString(String sql, List<String> hints) {
|
||||
processedSql = super.getQueryHintString( sql, hints );
|
||||
return processedSql;
|
||||
}
|
||||
doInHibernate( this::sessionFactory, s -> {
|
||||
Query query = s.createQuery( "FROM QueryHintTest$Employee e WHERE e.department.name = :departmentName" )
|
||||
.addQueryHint( "ALL_ROWS" )
|
||||
.setComment( "My_Query" )
|
||||
.setParameter( "departmentName", "Sales" );
|
||||
List results = query.list();
|
||||
|
||||
public static String getProcessedSql() {
|
||||
return processedSql;
|
||||
}
|
||||
assertEquals(results.size(), 2);
|
||||
} );
|
||||
|
||||
public static void resetProcessedSql() {
|
||||
processedSql = "";
|
||||
}
|
||||
assertEquals(
|
||||
1,
|
||||
connectionProvider.getPreparedStatements().size()
|
||||
);
|
||||
assertNotNull( connectionProvider.getPreparedSQLStatements().get( 0 ).contains( "/* My_Query */ select /*+ ALL_ROWS */" ) );
|
||||
connectionProvider.clear();
|
||||
}
|
||||
|
||||
@Entity
|
||||
|
@ -148,7 +186,7 @@ public class QueryHintTest extends BaseCoreFunctionalTestCase {
|
|||
@GeneratedValue
|
||||
public long id;
|
||||
|
||||
@ManyToOne
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
public Department department;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue