HHH-13780 Allow NamedQuery to set hint QueryHints.PASS_DISTINCT_THROUGH

This commit is contained in:
marekchodak 2020-01-19 22:04:47 +01:00 committed by Sanne Grinovero
parent 63a96e335e
commit bf473681e4
8 changed files with 122 additions and 17 deletions

View File

@ -67,6 +67,7 @@ public abstract class QueryBinder {
.setReadOnly( hints.getBoolean( queryName, QueryHints.READ_ONLY ) )
.setComment( hints.getString( queryName, QueryHints.COMMENT ) )
.setParameterTypes( null )
.setPassDistinctThrough( hints.getPassDistinctThrough( queryName ) )
.createNamedQueryDefinition();
if ( isDefault ) {
@ -108,8 +109,9 @@ public abstract class QueryBinder {
.setReadOnly( hints.getBoolean( queryName, QueryHints.READ_ONLY ) )
.setComment( hints.getString( queryName, QueryHints.COMMENT ) )
.setParameterTypes( null )
.setCallable( hints.getBoolean( queryName, QueryHints.CALLABLE ) );
.setCallable( hints.getBoolean( queryName, QueryHints.CALLABLE ) )
.setPassDistinctThrough( hints.getPassDistinctThrough( queryName ) );
if ( !BinderHelper.isEmptyAnnotationValue( resultSetMapping ) ) {
//sql result set usage
builder.setResultSetRef( resultSetMapping )

View File

@ -9,6 +9,7 @@ package org.hibernate.cfg.annotations;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import javax.persistence.LockModeType;
import javax.persistence.NamedQuery;
import javax.persistence.QueryHint;
@ -84,21 +85,28 @@ public class QueryHintDefinition {
}
}
public Boolean getPassDistinctThrough(String query) {
return doGetBoolean( query, QueryHints.PASS_DISTINCT_THROUGH ).orElse( null );
}
public boolean getBoolean(String query, String hintName) {
String value =(String) hintsMap.get( hintName );
return doGetBoolean( query, hintName ).orElse( false );
}
private Optional<Boolean> doGetBoolean(String query, String hintName) {
String value = (String) hintsMap.get( hintName );
if ( value == null ) {
return false;
return Optional.empty();
}
if ( value.equalsIgnoreCase( "true" ) ) {
return true;
return Optional.of( true );
}
else if ( value.equalsIgnoreCase( "false" ) ) {
return false;
return Optional.of( false );
}
else {
throw new AnnotationException( "Not a boolean in hint: " + query + ":" + hintName );
}
}
public String getString(String query, String hintName) {

View File

@ -33,6 +33,7 @@ public class NamedQueryDefinition implements Serializable {
private final CacheMode cacheMode;
private final boolean readOnly;
private final String comment;
private final Boolean passDistinctThrough;
// added for jpa 2.1
private final Integer firstResult;
@ -133,7 +134,8 @@ public class NamedQueryDefinition implements Serializable {
comment,
parameterTypes,
null, // firstResult
null // maxResults
null, // maxResults
null // passDistinctThrough
);
}
@ -151,7 +153,8 @@ public class NamedQueryDefinition implements Serializable {
String comment,
Map parameterTypes,
Integer firstResult,
Integer maxResults) {
Integer maxResults,
Boolean passDistinctThrough) {
this.name = name;
this.query = query;
this.cacheable = cacheable;
@ -167,6 +170,7 @@ public class NamedQueryDefinition implements Serializable {
this.firstResult = firstResult;
this.maxResults = maxResults;
this.passDistinctThrough = passDistinctThrough;
}
public String getName() {
@ -230,6 +234,10 @@ public class NamedQueryDefinition implements Serializable {
return maxResults;
}
public Boolean getPassDistinctThrough() {
return passDistinctThrough;
}
@Override
public String toString() {
return getClass().getName() + '(' + name + " [" + query + "])";
@ -250,7 +258,8 @@ public class NamedQueryDefinition implements Serializable {
getComment(),
getParameterTypes(),
getFirstResult(),
getMaxResults()
getMaxResults(),
getPassDistinctThrough()
);
}
}

View File

@ -28,6 +28,7 @@ public class NamedQueryDefinitionBuilder {
protected LockOptions lockOptions;
protected Integer firstResult;
protected Integer maxResults;
protected Boolean passDistinctThrough;
public NamedQueryDefinitionBuilder() {
}
@ -114,6 +115,11 @@ public class NamedQueryDefinitionBuilder {
return this;
}
public NamedQueryDefinitionBuilder setPassDistinctThrough(Boolean passDistinctThrough) {
this.passDistinctThrough = passDistinctThrough;
return this;
}
public NamedQueryDefinition createNamedQueryDefinition() {
return new NamedQueryDefinition(
name,
@ -129,7 +135,8 @@ public class NamedQueryDefinitionBuilder {
comment,
parameterTypes,
firstResult,
maxResults
maxResults,
passDistinctThrough
);
}
}

View File

@ -81,7 +81,8 @@ public class NamedSQLQueryDefinition extends NamedQueryDefinition {
null, // resultSetRef
querySpaces,
callable,
queryReturns
queryReturns,
null // passDistinctThrough
);
}
@ -140,7 +141,8 @@ public class NamedSQLQueryDefinition extends NamedQueryDefinition {
resultSetRef,
querySpaces,
callable,
null // queryReturns
null, // queryReturns
null // passDistinctThrough
);
}
@ -161,7 +163,8 @@ public class NamedSQLQueryDefinition extends NamedQueryDefinition {
String resultSetRef,
List<String> querySpaces,
boolean callable,
NativeSQLQueryReturn[] queryReturns) {
NativeSQLQueryReturn[] queryReturns,
Boolean passDistinctThrough) {
super(
name,
query.trim(), /* trim done to workaround stupid oracle bug that cant handle whitespaces before a { in a sp */
@ -176,7 +179,8 @@ public class NamedSQLQueryDefinition extends NamedQueryDefinition {
comment,
parameterTypes,
firstResult,
maxResults
maxResults,
passDistinctThrough
);
this.resultSetRef = resultSetRef;
this.querySpaces = querySpaces;
@ -219,7 +223,8 @@ public class NamedSQLQueryDefinition extends NamedQueryDefinition {
getResultSetRef(),
getQuerySpaces(),
isCallable(),
getQueryReturns()
getQueryReturns(),
getPassDistinctThrough()
);
}

View File

@ -149,6 +149,11 @@ public class NamedSQLQueryDefinitionBuilder extends NamedQueryDefinitionBuilder
return (NamedSQLQueryDefinitionBuilder) super.setMaxResults( maxResults );
}
@Override
public NamedSQLQueryDefinitionBuilder setPassDistinctThrough(Boolean passDistinctThrough) {
return (NamedSQLQueryDefinitionBuilder) super.setPassDistinctThrough( passDistinctThrough );
}
@Override
public NamedSQLQueryDefinition createNamedQueryDefinition() {
return new NamedSQLQueryDefinition(
@ -168,7 +173,8 @@ public class NamedSQLQueryDefinitionBuilder extends NamedQueryDefinitionBuilder
resultSetRef,
querySpacesCopy(),
callable,
queryReturns
queryReturns,
passDistinctThrough
);
}

View File

@ -62,6 +62,7 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.transaction.internal.TransactionImpl;
import org.hibernate.engine.transaction.spi.TransactionImplementor;
import org.hibernate.id.uuid.StandardRandomStrategy;
import org.hibernate.jpa.QueryHints;
import org.hibernate.jpa.internal.util.FlushModeTypeHelper;
import org.hibernate.jpa.spi.CriteriaQueryTupleTransformer;
import org.hibernate.jpa.spi.HibernateEntityManagerImplementor;
@ -695,6 +696,9 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
if ( nqd.getFlushMode() != null ) {
query.setHibernateFlushMode( nqd.getFlushMode() );
}
if ( nqd.getPassDistinctThrough() != null ) {
query.setHint( QueryHints.HINT_PASS_DISTINCT_THROUGH, nqd.getPassDistinctThrough() );
}
}
@Override

View File

@ -14,6 +14,9 @@ import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.QueryHint;
import org.hibernate.boot.SessionFactoryBuilder;
import org.hibernate.jpa.QueryHints;
@ -34,6 +37,9 @@ import static org.junit.Assert.assertTrue;
@TestForIssue( jiraKey = "HHH-10965" )
public class SelectDistinctHqlTest extends BaseNonConfigCoreFunctionalTestCase {
private static final String DISTINCT_PASSES_THROUGH_TRUE_NAMED_QUERY = "distinctPassesThroughTrue";
private static final String DISTINCT_PASSES_THROUGH_FALSE_NAMED_QUERY = "distinctPassesThroughFalse";
private static final String DISTINCT_PASSES_THROUGH_NOT_SPECIFIED_NAMED_QUERY = "distinctPassesThroughNotSpecified";
private SQLStatementInterceptor sqlStatementInterceptor;
@Override
@ -137,7 +143,65 @@ public class SelectDistinctHqlTest extends BaseNonConfigCoreFunctionalTestCase {
});
}
@Test
@TestForIssue(jiraKey = "HHH-13780")
public void testNamedQueryDistinctPassThroughTrue() {
doInHibernate( this::sessionFactory, session -> {
sqlStatementInterceptor.getSqlQueries().clear();
List<Person> persons = session.createNamedQuery( DISTINCT_PASSES_THROUGH_TRUE_NAMED_QUERY, Person.class )
.setMaxResults( 5 )
.getResultList();
assertEquals( 1, persons.size() );
String sqlQuery = sqlStatementInterceptor.getSqlQueries().getLast();
assertTrue( sqlQuery.contains( " distinct " ) );
} );
}
@Test
@TestForIssue(jiraKey = "HHH-13780")
public void testNamedQueryDistinctPassThroughTrueWhenNotSpecified() {
doInHibernate( this::sessionFactory, session -> {
sqlStatementInterceptor.getSqlQueries().clear();
List<Person> persons =
session.createNamedQuery( DISTINCT_PASSES_THROUGH_NOT_SPECIFIED_NAMED_QUERY, Person.class )
.setMaxResults( 5 )
.getResultList();
assertEquals( 1, persons.size() );
String sqlQuery = sqlStatementInterceptor.getSqlQueries().getLast();
assertTrue( sqlQuery.contains( " distinct " ) );
} );
}
@Test
@TestForIssue(jiraKey = "HHH-13780")
public void testNamedQueryDistinctPassThroughFalse() {
doInHibernate( this::sessionFactory, session -> {
sqlStatementInterceptor.getSqlQueries().clear();
List<Person> persons =
session.createNamedQuery( DISTINCT_PASSES_THROUGH_FALSE_NAMED_QUERY, Person.class )
.setMaxResults( 5 )
.getResultList();
assertEquals( 1, persons.size() );
String sqlQuery = sqlStatementInterceptor.getSqlQueries().getLast();
assertFalse( sqlQuery.contains( " distinct " ) );
} );
}
@Entity(name = "Person")
@NamedQueries({
@NamedQuery(name = DISTINCT_PASSES_THROUGH_TRUE_NAMED_QUERY,
query = "select distinct p from Person p left join fetch p.phones",
hints = {
@QueryHint(name = QueryHints.HINT_PASS_DISTINCT_THROUGH, value = "true")
}),
@NamedQuery(name = DISTINCT_PASSES_THROUGH_FALSE_NAMED_QUERY,
query = "select distinct p from Person p left join fetch p.phones",
hints = {
@QueryHint(name = QueryHints.HINT_PASS_DISTINCT_THROUGH, value = "false")
}),
@NamedQuery(name = DISTINCT_PASSES_THROUGH_NOT_SPECIFIED_NAMED_QUERY,
query = "select distinct p from Person p left join fetch p.phones")
})
public static class Person {
@Id