Merge remote-tracking branch 'upstream5/master' into wip/6.0_merge_15
This commit is contained in:
commit
d2865a54df
|
@ -3133,4 +3133,9 @@ public abstract class Dialect implements ConversionContext {
|
||||||
public SqlAstTranslatorFactory getSqlAstTranslatorFactory() {
|
public SqlAstTranslatorFactory getSqlAstTranslatorFactory() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean supportsSelectAliasInGroupByClause() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -444,4 +444,10 @@ public class H2Dialect extends Dialect {
|
||||||
public String getQueryHintString(String query, String hints) {
|
public String getQueryHintString(String query, String hints) {
|
||||||
return IndexQueryHintHandler.INSTANCE.addQueryHints( query, hints );
|
return IndexQueryHintHandler.INSTANCE.addQueryHints( query, hints );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsSelectAliasInGroupByClause() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -612,4 +612,10 @@ public class MySQLDialect extends Dialect {
|
||||||
protected String escapeLiteral(String literal) {
|
protected String escapeLiteral(String literal) {
|
||||||
return ESCAPE_PATTERN.matcher( super.escapeLiteral( literal ) ).replaceAll( ESCAPE_PATTERN_REPLACEMENT );
|
return ESCAPE_PATTERN.matcher( super.escapeLiteral( literal ) ).replaceAll( ESCAPE_PATTERN_REPLACEMENT );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsSelectAliasInGroupByClause() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,7 @@ public class Oracle8iDialect extends Dialect {
|
||||||
|
|
||||||
private static final Pattern UNION_KEYWORD_PATTERN = Pattern.compile( "\\bunion\\b" );
|
private static final Pattern UNION_KEYWORD_PATTERN = Pattern.compile( "\\bunion\\b" );
|
||||||
|
|
||||||
private static final Pattern SQL_STATEMENT_TYPE_PATTERN = Pattern.compile("^(?:\\/\\*.*?\\*\\/)?\\s*(select|insert|update|delete)\\s+.*?");
|
private static final Pattern SQL_STATEMENT_TYPE_PATTERN = Pattern.compile("^(?:\\/\\*.*?\\*\\/)?\\s*(select|insert|update|delete)\\s+.*?", Pattern.CASE_INSENSITIVE);
|
||||||
|
|
||||||
private static final AbstractLimitHandler LIMIT_HANDLER = new AbstractLimitHandler() {
|
private static final AbstractLimitHandler LIMIT_HANDLER = new AbstractLimitHandler() {
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -647,4 +647,10 @@ public class PostgreSQL81Dialect extends Dialect {
|
||||||
public boolean supportsJdbcConnectionLobCreation(DatabaseMetaData databaseMetaData) {
|
public boolean supportsJdbcConnectionLobCreation(DatabaseMetaData databaseMetaData) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsSelectAliasInGroupByClause() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -398,6 +398,24 @@ public abstract class AbstractEntityPersister
|
||||||
|
|
||||||
protected abstract boolean isClassOrSuperclassTable(int j);
|
protected abstract boolean isClassOrSuperclassTable(int j);
|
||||||
|
|
||||||
|
protected boolean isClassOrSuperclassJoin(int j) {
|
||||||
|
/*
|
||||||
|
* TODO:
|
||||||
|
* SingleTableEntityPersister incorrectly used isClassOrSuperclassJoin == isClassOrSuperclassTable,
|
||||||
|
* this caused HHH-12895, as this resulted in the subclass tables always being joined, even if no
|
||||||
|
* property on these tables was accessed.
|
||||||
|
*
|
||||||
|
* JoinedTableEntityPersister does not use isClassOrSuperclassJoin at all, probably incorrectly so.
|
||||||
|
* I however haven't been able to reproduce any quirks regarding <join>s, secondary tables or
|
||||||
|
* @JoinTable's.
|
||||||
|
*
|
||||||
|
* Probably this method needs to be properly implemented for the various entity persisters,
|
||||||
|
* but this at least fixes the SingleTableEntityPersister, while maintaining the the
|
||||||
|
* previous behaviour for other persisters.
|
||||||
|
*/
|
||||||
|
return isClassOrSuperclassTable( j );
|
||||||
|
}
|
||||||
|
|
||||||
public abstract int getSubclassTableSpan();
|
public abstract int getSubclassTableSpan();
|
||||||
|
|
||||||
protected abstract int getTableSpan();
|
protected abstract int getTableSpan();
|
||||||
|
@ -4281,7 +4299,7 @@ public abstract class AbstractEntityPersister
|
||||||
Set<String> treatAsDeclarations,
|
Set<String> treatAsDeclarations,
|
||||||
Set<String> referencedTables) {
|
Set<String> referencedTables) {
|
||||||
|
|
||||||
if ( isClassOrSuperclassTable( subclassTableNumber ) ) {
|
if ( isClassOrSuperclassJoin( subclassTableNumber ) ) {
|
||||||
String superclassTableName = getSubclassTableName( subclassTableNumber );
|
String superclassTableName = getSubclassTableName( subclassTableNumber );
|
||||||
if ( referencedTables != null && canOmitSuperclassTableJoin() && !referencedTables.contains(
|
if ( referencedTables != null && canOmitSuperclassTableJoin() && !referencedTables.contains(
|
||||||
superclassTableName ) ) {
|
superclassTableName ) ) {
|
||||||
|
|
|
@ -91,6 +91,7 @@ public class SingleTableEntityPersister extends AbstractEntityPersister {
|
||||||
private final boolean[] subclassTableSequentialSelect;
|
private final boolean[] subclassTableSequentialSelect;
|
||||||
private final String[][] subclassTableKeyColumnClosure;
|
private final String[][] subclassTableKeyColumnClosure;
|
||||||
private final boolean[] isClassOrSuperclassTable;
|
private final boolean[] isClassOrSuperclassTable;
|
||||||
|
private final boolean[] isClassOrSuperclassJoin;
|
||||||
|
|
||||||
// properties of this class, including inherited properties
|
// properties of this class, including inherited properties
|
||||||
private final int[] propertyTableNumbers;
|
private final int[] propertyTableNumbers;
|
||||||
|
@ -243,6 +244,7 @@ public class SingleTableEntityPersister extends AbstractEntityPersister {
|
||||||
ArrayList<String> subclassTables = new ArrayList<String>();
|
ArrayList<String> subclassTables = new ArrayList<String>();
|
||||||
ArrayList<String[]> joinKeyColumns = new ArrayList<String[]>();
|
ArrayList<String[]> joinKeyColumns = new ArrayList<String[]>();
|
||||||
ArrayList<Boolean> isConcretes = new ArrayList<Boolean>();
|
ArrayList<Boolean> isConcretes = new ArrayList<Boolean>();
|
||||||
|
ArrayList<Boolean> isClassOrSuperclassJoins = new ArrayList<Boolean>();
|
||||||
ArrayList<Boolean> isDeferreds = new ArrayList<Boolean>();
|
ArrayList<Boolean> isDeferreds = new ArrayList<Boolean>();
|
||||||
ArrayList<Boolean> isInverses = new ArrayList<Boolean>();
|
ArrayList<Boolean> isInverses = new ArrayList<Boolean>();
|
||||||
ArrayList<Boolean> isNullables = new ArrayList<Boolean>();
|
ArrayList<Boolean> isNullables = new ArrayList<Boolean>();
|
||||||
|
@ -250,6 +252,7 @@ public class SingleTableEntityPersister extends AbstractEntityPersister {
|
||||||
subclassTables.add( qualifiedTableNames[0] );
|
subclassTables.add( qualifiedTableNames[0] );
|
||||||
joinKeyColumns.add( getIdentifierColumnNames() );
|
joinKeyColumns.add( getIdentifierColumnNames() );
|
||||||
isConcretes.add( Boolean.TRUE );
|
isConcretes.add( Boolean.TRUE );
|
||||||
|
isClassOrSuperclassJoins.add( Boolean.TRUE );
|
||||||
isDeferreds.add( Boolean.FALSE );
|
isDeferreds.add( Boolean.FALSE );
|
||||||
isInverses.add( Boolean.FALSE );
|
isInverses.add( Boolean.FALSE );
|
||||||
isNullables.add( Boolean.FALSE );
|
isNullables.add( Boolean.FALSE );
|
||||||
|
@ -257,14 +260,15 @@ public class SingleTableEntityPersister extends AbstractEntityPersister {
|
||||||
joinIter = persistentClass.getSubclassJoinClosureIterator();
|
joinIter = persistentClass.getSubclassJoinClosureIterator();
|
||||||
while ( joinIter.hasNext() ) {
|
while ( joinIter.hasNext() ) {
|
||||||
Join join = (Join) joinIter.next();
|
Join join = (Join) joinIter.next();
|
||||||
isConcretes.add( persistentClass.isClassOrSuperclassJoin( join ) );
|
isConcretes.add( persistentClass.isClassOrSuperclassTable( join.getTable() ) );
|
||||||
isDeferreds.add( join.isSequentialSelect() );
|
isClassOrSuperclassJoins.add( persistentClass.isClassOrSuperclassJoin( join ) );
|
||||||
isInverses.add( join.isInverse() );
|
isInverses.add( join.isInverse() );
|
||||||
isNullables.add( join.isOptional() );
|
isNullables.add( join.isOptional() );
|
||||||
isLazies.add( lazyAvailable && join.isLazy() );
|
isLazies.add( lazyAvailable && join.isLazy() );
|
||||||
if ( join.isSequentialSelect() && !persistentClass.isClassOrSuperclassJoin( join ) ) {
|
|
||||||
hasDeferred = true;
|
boolean isDeferred = join.isSequentialSelect() && ! persistentClass.isClassOrSuperclassJoin( join ) ;
|
||||||
}
|
isDeferreds.add( isDeferred );
|
||||||
|
hasDeferred |= isDeferred;
|
||||||
|
|
||||||
String joinTableName = determineTableName( join.getTable(), jdbcEnvironment );
|
String joinTableName = determineTableName( join.getTable(), jdbcEnvironment );
|
||||||
subclassTables.add( joinTableName );
|
subclassTables.add( joinTableName );
|
||||||
|
@ -284,6 +288,7 @@ public class SingleTableEntityPersister extends AbstractEntityPersister {
|
||||||
subclassTableIsLazyClosure = ArrayHelper.toBooleanArray( isLazies );
|
subclassTableIsLazyClosure = ArrayHelper.toBooleanArray( isLazies );
|
||||||
subclassTableKeyColumnClosure = ArrayHelper.to2DStringArray( joinKeyColumns );
|
subclassTableKeyColumnClosure = ArrayHelper.to2DStringArray( joinKeyColumns );
|
||||||
isClassOrSuperclassTable = ArrayHelper.toBooleanArray( isConcretes );
|
isClassOrSuperclassTable = ArrayHelper.toBooleanArray( isConcretes );
|
||||||
|
isClassOrSuperclassJoin = ArrayHelper.toBooleanArray( isClassOrSuperclassJoins );
|
||||||
isInverseSubclassTable = ArrayHelper.toBooleanArray( isInverses );
|
isInverseSubclassTable = ArrayHelper.toBooleanArray( isInverses );
|
||||||
isNullableSubclassTable = ArrayHelper.toBooleanArray( isNullables );
|
isNullableSubclassTable = ArrayHelper.toBooleanArray( isNullables );
|
||||||
hasSequentialSelects = hasDeferred;
|
hasSequentialSelects = hasDeferred;
|
||||||
|
@ -813,6 +818,10 @@ public class SingleTableEntityPersister extends AbstractEntityPersister {
|
||||||
return isClassOrSuperclassTable[j];
|
return isClassOrSuperclassTable[j];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected boolean isClassOrSuperclassJoin(int j) {
|
||||||
|
return isClassOrSuperclassJoin[j];
|
||||||
|
}
|
||||||
|
|
||||||
protected boolean isSubclassTableLazy(int j) {
|
protected boolean isSubclassTableLazy(int j) {
|
||||||
return subclassTableIsLazyClosure[j];
|
return subclassTableIsLazyClosure[j];
|
||||||
}
|
}
|
||||||
|
@ -847,6 +856,11 @@ public class SingleTableEntityPersister extends AbstractEntityPersister {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canOmitSuperclassTableJoin() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isMultiTable() {
|
public boolean isMultiTable() {
|
||||||
return getTableSpan() > 1;
|
return getTableSpan() > 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* 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.jpa.test.jointable;
|
||||||
|
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Christian Beikov
|
||||||
|
*/
|
||||||
|
@Entity(name="House")
|
||||||
|
public class Address {
|
||||||
|
private Long id;
|
||||||
|
private String street;
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStreet() {
|
||||||
|
return street;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStreet(String street) {
|
||||||
|
this.street = street;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* 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.jpa.test.jointable;
|
||||||
|
|
||||||
|
import org.hibernate.engine.query.spi.HQLQueryPlan;
|
||||||
|
import org.hibernate.hql.spi.QueryTranslator;
|
||||||
|
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Christian Beikov
|
||||||
|
*/
|
||||||
|
public class ManyToOneJoinTableTest extends BaseCoreFunctionalTestCase {
|
||||||
|
@Override
|
||||||
|
protected Class<?>[] getAnnotatedClasses() {
|
||||||
|
return new Class[] {
|
||||||
|
Person.class,
|
||||||
|
Address.class
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAvoidJoin() {
|
||||||
|
final HQLQueryPlan plan = sessionFactory().getQueryPlanCache().getHQLQueryPlan(
|
||||||
|
"SELECT e.id FROM Person e",
|
||||||
|
false,
|
||||||
|
Collections.EMPTY_MAP
|
||||||
|
);
|
||||||
|
assertEquals( 1, plan.getTranslators().length );
|
||||||
|
final QueryTranslator translator = plan.getTranslators()[0];
|
||||||
|
final String generatedSql = translator.getSQLString();
|
||||||
|
// Ideally, we could detect that *ToOne join tables aren't used, but that requires tracking the uses of properties
|
||||||
|
// Since *ToOne join tables are treated like secondary or subclass/superclass tables, the proper fix will allow many more optimizations
|
||||||
|
assertFalse( generatedSql.contains( "join" ) );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* 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.jpa.test.jointable;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Christian Beikov
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
public class Person {
|
||||||
|
private Long id;
|
||||||
|
private Address address;
|
||||||
|
private Set<Address> addresses;
|
||||||
|
|
||||||
|
@Id
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinTable( name = "SOME_OTHER_TABLE" )
|
||||||
|
public Address getAddress() {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAddress(Address address) {
|
||||||
|
this.address = address;
|
||||||
|
}
|
||||||
|
|
||||||
|
@OneToMany
|
||||||
|
@JoinTable( name = "SOME_OTHER_TABLE2" )
|
||||||
|
public Set<Address> getAddresses() {
|
||||||
|
return addresses;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAddresses(Set<Address> addresses) {
|
||||||
|
this.addresses = addresses;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,228 @@
|
||||||
|
/*
|
||||||
|
* 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.query;
|
||||||
|
|
||||||
|
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||||
|
import org.hibernate.testing.DialectChecks;
|
||||||
|
import org.hibernate.testing.RequiresDialectFeature;
|
||||||
|
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import javax.persistence.CascadeType;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.IdClass;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.Tuple;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jan-Willem Gmelig Meyling
|
||||||
|
* @author Sayra Ranjha
|
||||||
|
*/
|
||||||
|
@RequiresDialectFeature(value = DialectChecks.SupportsSelectAliasInGroupByClause.class, jiraKey = "HHH-9301")
|
||||||
|
public class GroupByAliasTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
|
|
||||||
|
public static final int MAX_COUNT = 15;
|
||||||
|
|
||||||
|
private SQLStatementInterceptor sqlStatementInterceptor;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void addConfigOptions(Map options) {
|
||||||
|
sqlStatementInterceptor = new SQLStatementInterceptor( options );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class[] getAnnotatedClasses() {
|
||||||
|
return new Class[] {
|
||||||
|
Person.class,
|
||||||
|
Association.class
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void afterEntityManagerFactoryBuilt() {
|
||||||
|
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||||
|
for ( int i = 0; i < MAX_COUNT; i++ ) {
|
||||||
|
Association association = new Association();
|
||||||
|
association.setId( i );
|
||||||
|
association.setName(String.format( "Association nr %d", i ) );
|
||||||
|
|
||||||
|
Person person = new Person();
|
||||||
|
person.setId( i );
|
||||||
|
person.setName( String.format( "Person nr %d", i ) );
|
||||||
|
person.setAssociation(association);
|
||||||
|
person.setAge(5);
|
||||||
|
entityManager.persist( person );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSingleIdAlias() {
|
||||||
|
sqlStatementInterceptor.clear();
|
||||||
|
|
||||||
|
List<Tuple> list = doInJPA(this::entityManagerFactory, entityManager -> {
|
||||||
|
return entityManager.createQuery(
|
||||||
|
"select p.id as id_alias, sum(p.age) " +
|
||||||
|
"from Person p group by id_alias order by id_alias", Tuple.class)
|
||||||
|
.getResultList();
|
||||||
|
});
|
||||||
|
|
||||||
|
String s = sqlStatementInterceptor.getSqlQueries().get(0);
|
||||||
|
assertNotNull(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCompoundIdAlias() {
|
||||||
|
sqlStatementInterceptor.clear();
|
||||||
|
|
||||||
|
List<Tuple> list = doInJPA(this::entityManagerFactory, entityManager -> {
|
||||||
|
return entityManager.createQuery(
|
||||||
|
"select p.association as id_alias, sum(p.age) " +
|
||||||
|
"from Person p group by id_alias order by id_alias", Tuple.class)
|
||||||
|
.getResultList();
|
||||||
|
});
|
||||||
|
|
||||||
|
String s = sqlStatementInterceptor.getSqlQueries().get(0);
|
||||||
|
assertNotNull(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultiIdAlias() {
|
||||||
|
sqlStatementInterceptor.clear();
|
||||||
|
|
||||||
|
List<Tuple> list = doInJPA(this::entityManagerFactory, entityManager -> {
|
||||||
|
return entityManager.createQuery(
|
||||||
|
"select p.id as id_alias_1, p.association as id_alias_2, sum(p.age) " +
|
||||||
|
"from Person p group by id_alias_1, id_alias_2 order by id_alias_1, id_alias_2 ", Tuple.class)
|
||||||
|
.getResultList();
|
||||||
|
});
|
||||||
|
|
||||||
|
String s = sqlStatementInterceptor.getSqlQueries().get(0);
|
||||||
|
assertNotNull(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Person")
|
||||||
|
public static class Person {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private Integer age;
|
||||||
|
|
||||||
|
@ManyToOne(cascade = CascadeType.PERSIST)
|
||||||
|
private Association association;
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getAge() {
|
||||||
|
return age;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAge(Integer age) {
|
||||||
|
this.age = age;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Association getAssociation() {
|
||||||
|
return association;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAssociation(Association association) {
|
||||||
|
this.association = association;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@IdClass(Association.IdClass.class)
|
||||||
|
@Entity(name = "Association")
|
||||||
|
public static class Association {
|
||||||
|
|
||||||
|
public static class IdClass implements Serializable {
|
||||||
|
private Integer id;
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
IdClass id1 = (IdClass) o;
|
||||||
|
return Objects.equals(id, id1.id) &&
|
||||||
|
Objects.equals(name, id1.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(id, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -6,7 +6,6 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.query.hhh13670;
|
package org.hibernate.query.hhh13670;
|
||||||
|
|
||||||
import org.hibernate.cfg.Configuration;
|
|
||||||
import org.hibernate.testing.TestForIssue;
|
import org.hibernate.testing.TestForIssue;
|
||||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
@ -101,6 +100,74 @@ public class HHH13670Test extends BaseCoreFunctionalTestCase {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSubTypePropertyReferencedFromEntityJoinInSyntheticSubquery() {
|
||||||
|
doInJPA(this::sessionFactory, em -> {
|
||||||
|
List<Tuple> resultList = em.createQuery(
|
||||||
|
"SELECT subB_0.id, subA_0.id, subB_0.id, subA_0.id FROM SubB subB_0 INNER JOIN SubA subA_0 ON 1=1 WHERE (EXISTS (SELECT 1 FROM subB_0.parent _synth_subquery_0 WHERE subA_0.id = _synth_subquery_0.id)) ORDER BY subB_0.id ASC, subA_0.id ASC", Tuple.class)
|
||||||
|
.getResultList();
|
||||||
|
|
||||||
|
assertEquals(1, resultList.size());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSubTypePropertyReferencedFromEntityJoinInSyntheticSubquery2() {
|
||||||
|
doInJPA(this::sessionFactory, em -> {
|
||||||
|
List<Tuple> resultList = em.createQuery(
|
||||||
|
"SELECT subB_0.id, subA_0.id, subB_0.id, subA_0.id FROM SubB subB_0 INNER JOIN SubA subA_0 ON 1=1 WHERE (EXISTS (SELECT 1 FROM Super s WHERE subA_0.id = s.parent.id)) ORDER BY subB_0.id ASC, subA_0.id ASC", Tuple.class)
|
||||||
|
.getResultList();
|
||||||
|
|
||||||
|
assertEquals(4, resultList.size());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSubTypePropertyReferencedFromEntityJoinInSyntheticSubquery3() {
|
||||||
|
doInJPA(this::sessionFactory, em -> {
|
||||||
|
List<Tuple> resultList = em.createQuery(
|
||||||
|
"SELECT subB_0.id, subA_0.id, subB_0.id, subA_0.id FROM SubB subB_0 INNER JOIN SubA subA_0 ON 1=1 WHERE (EXISTS (SELECT 1 FROM Super s WHERE s.id = subB_0.parent.id)) ORDER BY subB_0.id ASC, subA_0.id ASC", Tuple.class)
|
||||||
|
.getResultList();
|
||||||
|
|
||||||
|
assertEquals(6, resultList.size());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSubTypePropertyReferencedFromEntityJoinInSyntheticSubquery4() {
|
||||||
|
doInJPA(this::sessionFactory, em -> {
|
||||||
|
List<Tuple> resultList = em.createQuery(
|
||||||
|
"SELECT subB_0.id, subA_0.id, subB_0.id, subA_0.id FROM SubB subB_0 INNER JOIN SubA subA_0 ON 1=1 WHERE (EXISTS (SELECT 1 FROM Super s WHERE s.id = subA_0.parent.id)) ORDER BY subB_0.id ASC, subA_0.id ASC", Tuple.class)
|
||||||
|
.getResultList();
|
||||||
|
|
||||||
|
assertEquals(0, resultList.size());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSubTypePropertyReferencedFromWhereClause() {
|
||||||
|
doInJPA(this::sessionFactory, em -> {
|
||||||
|
List<Tuple> resultList = em.createQuery("SELECT subB_0.id FROM SubB subB_0 WHERE subB_0.parent.id IS NOT NULL", Tuple.class)
|
||||||
|
.getResultList();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSubTypePropertyReferencedFromGroupByClause() {
|
||||||
|
doInJPA(this::sessionFactory, em -> {
|
||||||
|
List<Tuple> resultList = em.createQuery("SELECT subB_0.id FROM SubB subB_0 GROUP BY subB_0.id , subB_0.parent.id", Tuple.class)
|
||||||
|
.getResultList();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSubTypePropertyReferencedFromOrderByClause() {
|
||||||
|
doInJPA(this::sessionFactory, em -> {
|
||||||
|
List<Tuple> resultList = em.createQuery("SELECT subB_0.id FROM SubB subB_0 ORDER BY subB_0.id , subB_0.parent.id", Tuple.class)
|
||||||
|
.getResultList();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Class<?>[] getAnnotatedClasses() {
|
protected Class<?>[] getAnnotatedClasses() {
|
||||||
return new Class<?>[] { Super.class, SubA.class, SubB.class };
|
return new Class<?>[] { Super.class, SubA.class, SubB.class };
|
||||||
|
|
|
@ -162,7 +162,32 @@ public class QueryHintTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||||
sqlStatementInterceptor.clear();
|
sqlStatementInterceptor.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Entity
|
@Test
|
||||||
|
@TestForIssue( jiraKey = "HHH-13608")
|
||||||
|
public void testQueryHintCaseInsensitive() {
|
||||||
|
sqlStatementInterceptor.clear();
|
||||||
|
|
||||||
|
doInHibernate( this::sessionFactory, s -> {
|
||||||
|
List results = s.createNativeQuery(
|
||||||
|
"SELECT e.id as id " +
|
||||||
|
"FROM Employee e " +
|
||||||
|
"JOIN Department d ON e.department_id = d.id " +
|
||||||
|
"WHERE d.name = :departmentName" )
|
||||||
|
.addQueryHint( "ALL_ROWS" )
|
||||||
|
.setComment( "My_Query" )
|
||||||
|
.setParameter( "departmentName", "Sales" )
|
||||||
|
.getResultList();
|
||||||
|
|
||||||
|
assertEquals(results.size(), 2);
|
||||||
|
} );
|
||||||
|
|
||||||
|
sqlStatementInterceptor.assertExecutedCount( 1 );
|
||||||
|
|
||||||
|
assertTrue( sqlStatementInterceptor.getSqlQueries().get( 0 ).contains( "/* My_Query */ SELECT /*+ ALL_ROWS */" ) );
|
||||||
|
sqlStatementInterceptor.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Employee")
|
||||||
public static class Employee {
|
public static class Employee {
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue
|
@GeneratedValue
|
||||||
|
@ -172,7 +197,7 @@ public class QueryHintTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||||
public Department department;
|
public Department department;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Entity
|
@Entity(name = "Department")
|
||||||
public static class Department {
|
public static class Department {
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue
|
@GeneratedValue
|
||||||
|
|
|
@ -266,6 +266,12 @@ abstract public class DialectChecks {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class SupportsSelectAliasInGroupByClause implements DialectCheck {
|
||||||
|
public boolean isMatch(Dialect dialect) {
|
||||||
|
return dialect.supportsSelectAliasInGroupByClause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static class SupportsNClob implements DialectCheck {
|
public static class SupportsNClob implements DialectCheck {
|
||||||
@Override
|
@Override
|
||||||
public boolean isMatch(Dialect dialect) {
|
public boolean isMatch(Dialect dialect) {
|
||||||
|
|
Loading…
Reference in New Issue