mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-02-28 14:59:12 +00:00
HHH-5645 - Criteria.createAlias with specified criterion results in wrong parameters passed into SQL statement
This commit is contained in:
parent
eaeda2f41d
commit
dbd5f81f79
@ -6,6 +6,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.hibernate.loader;
|
package org.hibernate.loader;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.LockOptions;
|
import org.hibernate.LockOptions;
|
||||||
import org.hibernate.dialect.Dialect;
|
import org.hibernate.dialect.Dialect;
|
||||||
@ -24,6 +26,7 @@
|
|||||||
* @author Gavin King
|
* @author Gavin King
|
||||||
*/
|
*/
|
||||||
public abstract class OuterJoinLoader extends BasicLoader {
|
public abstract class OuterJoinLoader extends BasicLoader {
|
||||||
|
|
||||||
protected Loadable[] persisters;
|
protected Loadable[] persisters;
|
||||||
protected CollectionPersister[] collectionPersisters;
|
protected CollectionPersister[] collectionPersisters;
|
||||||
protected int[] collectionOwners;
|
protected int[] collectionOwners;
|
||||||
@ -35,6 +38,7 @@ public abstract class OuterJoinLoader extends BasicLoader {
|
|||||||
protected String sql;
|
protected String sql;
|
||||||
protected String[] suffixes;
|
protected String[] suffixes;
|
||||||
protected String[] collectionSuffixes;
|
protected String[] collectionSuffixes;
|
||||||
|
protected List associations;
|
||||||
|
|
||||||
private LoadQueryInfluencers loadQueryInfluencers;
|
private LoadQueryInfluencers loadQueryInfluencers;
|
||||||
|
|
||||||
@ -110,6 +114,7 @@ protected void initFromWalker(JoinWalker walker) {
|
|||||||
collectionOwners = walker.getCollectionOwners();
|
collectionOwners = walker.getCollectionOwners();
|
||||||
sql = walker.getSQLString();
|
sql = walker.getSQLString();
|
||||||
aliases = walker.getAliases();
|
aliases = walker.getAliases();
|
||||||
|
associations = walker.getAssociations();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -28,6 +29,7 @@
|
|||||||
import org.hibernate.internal.CriteriaImpl;
|
import org.hibernate.internal.CriteriaImpl;
|
||||||
import org.hibernate.internal.util.collections.ArrayHelper;
|
import org.hibernate.internal.util.collections.ArrayHelper;
|
||||||
import org.hibernate.loader.OuterJoinLoader;
|
import org.hibernate.loader.OuterJoinLoader;
|
||||||
|
import org.hibernate.loader.OuterJoinableAssociation;
|
||||||
import org.hibernate.loader.spi.AfterLoadAction;
|
import org.hibernate.loader.spi.AfterLoadAction;
|
||||||
import org.hibernate.persister.entity.Loadable;
|
import org.hibernate.persister.entity.Loadable;
|
||||||
import org.hibernate.persister.entity.Lockable;
|
import org.hibernate.persister.entity.Lockable;
|
||||||
@ -88,6 +90,7 @@ public CriteriaLoader(
|
|||||||
|
|
||||||
initFromWalker(walker);
|
initFromWalker(walker);
|
||||||
|
|
||||||
|
translator.setAssociations(getAssociationPaths());
|
||||||
userAliases = walker.getUserAliases();
|
userAliases = walker.getUserAliases();
|
||||||
resultTypes = walker.getResultTypes();
|
resultTypes = walker.getResultTypes();
|
||||||
includeInResultRow = walker.includeInResultRow();
|
includeInResultRow = walker.includeInResultRow();
|
||||||
@ -97,6 +100,19 @@ public CriteriaLoader(
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Set<String> getAssociationPaths() {
|
||||||
|
Set<String> associationPaths = new HashSet<>();
|
||||||
|
for ( Object association : this.associations ) {
|
||||||
|
if ( association instanceof OuterJoinableAssociation ) {
|
||||||
|
OuterJoinableAssociation outerJoinableAssociation = (OuterJoinableAssociation) association;
|
||||||
|
if ( outerJoinableAssociation.getPropertyPath() != null ) {
|
||||||
|
associationPaths.add( outerJoinableAssociation.getPropertyPath().getFullPath() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return associationPaths;
|
||||||
|
}
|
||||||
|
|
||||||
public ScrollableResultsImplementor scroll(SharedSessionContractImplementor session, ScrollMode scrollMode)
|
public ScrollableResultsImplementor scroll(SharedSessionContractImplementor session, ScrollMode scrollMode)
|
||||||
throws HibernateException {
|
throws HibernateException {
|
||||||
QueryParameters qp = translator.getQueryParameters();
|
QueryParameters qp = translator.getQueryParameters();
|
||||||
|
@ -9,10 +9,12 @@
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -65,6 +67,7 @@ public class CriteriaQueryTranslator implements CriteriaQuery {
|
|||||||
private final Map<String, Criteria> associationPathCriteriaMap = new LinkedHashMap<String, Criteria>();
|
private final Map<String, Criteria> associationPathCriteriaMap = new LinkedHashMap<String, Criteria>();
|
||||||
private final Map<String, JoinType> associationPathJoinTypesMap = new LinkedHashMap<String,JoinType>();
|
private final Map<String, JoinType> associationPathJoinTypesMap = new LinkedHashMap<String,JoinType>();
|
||||||
private final Map<String, Criterion> withClauseMap = new HashMap<String, Criterion>();
|
private final Map<String, Criterion> withClauseMap = new HashMap<String, Criterion>();
|
||||||
|
private Set<String> associations;
|
||||||
|
|
||||||
private final SessionFactoryImplementor sessionFactory;
|
private final SessionFactoryImplementor sessionFactory;
|
||||||
private final SessionFactoryHelper helper;
|
private final SessionFactoryHelper helper;
|
||||||
@ -95,6 +98,10 @@ public CriteriaQueryTranslator(
|
|||||||
createCriteriaSQLAliasMap();
|
createCriteriaSQLAliasMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setAssociations(Set<String> associations) {
|
||||||
|
this.associations = associations;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String generateSQLAlias() {
|
public String generateSQLAlias() {
|
||||||
int aliasCount = 0;
|
int aliasCount = 0;
|
||||||
@ -304,6 +311,9 @@ public QueryParameters getQueryParameters() {
|
|||||||
|
|
||||||
final List<Object> values = new ArrayList<Object>();
|
final List<Object> values = new ArrayList<Object>();
|
||||||
final List<Type> types = new ArrayList<Type>();
|
final List<Type> types = new ArrayList<Type>();
|
||||||
|
final Map<String, String> aliasMap = new HashMap<String, String>();
|
||||||
|
final Map<String, Collection<Object>> valueMap = new HashMap<String, Collection<Object>>();
|
||||||
|
final Map<String, Collection<Type>> typeMap = new HashMap<String, Collection<Type>>();
|
||||||
final Iterator<CriteriaImpl.Subcriteria> subcriteriaIterator = rootCriteria.iterateSubcriteria();
|
final Iterator<CriteriaImpl.Subcriteria> subcriteriaIterator = rootCriteria.iterateSubcriteria();
|
||||||
while ( subcriteriaIterator.hasNext() ) {
|
while ( subcriteriaIterator.hasNext() ) {
|
||||||
final CriteriaImpl.Subcriteria subcriteria = subcriteriaIterator.next();
|
final CriteriaImpl.Subcriteria subcriteria = subcriteriaIterator.next();
|
||||||
@ -311,11 +321,65 @@ public QueryParameters getQueryParameters() {
|
|||||||
if ( lm != null ) {
|
if ( lm != null ) {
|
||||||
lockOptions.setAliasSpecificLockMode( getSQLAlias( subcriteria ), lm );
|
lockOptions.setAliasSpecificLockMode( getSQLAlias( subcriteria ), lm );
|
||||||
}
|
}
|
||||||
|
aliasMap.put(subcriteria.getPath(), subcriteria.getAlias());
|
||||||
if ( subcriteria.getWithClause() != null ) {
|
if ( subcriteria.getWithClause() != null ) {
|
||||||
final TypedValue[] tv = subcriteria.getWithClause().getTypedValues( subcriteria, this );
|
final TypedValue[] tv = subcriteria.getWithClause().getTypedValues( subcriteria, this );
|
||||||
for ( TypedValue aTv : tv ) {
|
for ( TypedValue aTv : tv ) {
|
||||||
values.add( aTv.getValue() );
|
Collection<Object> valueCollection = valueMap.get( subcriteria.getPath() );
|
||||||
types.add( aTv.getType() );
|
Collection<Type> typeCollection = typeMap.get( subcriteria.getPath() );
|
||||||
|
if ( valueCollection == null ) {
|
||||||
|
valueCollection = new LinkedList<>();
|
||||||
|
valueMap.put( subcriteria.getPath(), valueCollection );
|
||||||
|
}
|
||||||
|
if ( typeCollection == null ) {
|
||||||
|
typeCollection = new LinkedList<>();
|
||||||
|
typeMap.put( subcriteria.getPath(), typeCollection );
|
||||||
|
}
|
||||||
|
valueCollection.add( aTv.getValue() );
|
||||||
|
typeCollection.add( aTv.getType() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( associations != null ) {
|
||||||
|
for ( String association : this.associations ) {
|
||||||
|
if ( association.contains( "." ) ) {
|
||||||
|
String alias = null;
|
||||||
|
String[] pathArray = association.split( "\\." );
|
||||||
|
for ( int i = 0; i < pathArray.length; i++ ) {
|
||||||
|
String element = pathArray[i];
|
||||||
|
if ( i == ( pathArray.length - 1 ) ) {
|
||||||
|
Collection<Object> valueList = valueMap.get( alias + "." + element );
|
||||||
|
Collection<Type> typeList = typeMap.get( alias + "." + element );
|
||||||
|
if ( typeList != null ) {
|
||||||
|
values.addAll( valueList );
|
||||||
|
types.addAll( typeList );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( alias == null ) {
|
||||||
|
alias = aliasMap.get( element );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
alias = aliasMap.get( alias + "." + element );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
String matchingAssociation = null;
|
||||||
|
for ( Map.Entry<String, Collection<Type>> typeEntry : typeMap.entrySet() ) {
|
||||||
|
String path = typeEntry.getKey();
|
||||||
|
if ( path.equals( association ) || path.endsWith( "." + association ) ) {
|
||||||
|
matchingAssociation = path;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( matchingAssociation != null ) {
|
||||||
|
Collection<Object> valueList = valueMap.get( matchingAssociation );
|
||||||
|
Collection<Type> typeList = typeMap.get( matchingAssociation );
|
||||||
|
if ( typeList != null ) {
|
||||||
|
values.addAll( valueList );
|
||||||
|
types.addAll( typeList );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -666,5 +730,4 @@ public boolean hasRestriction(String path) {
|
|||||||
final CriteriaImpl.Subcriteria subcriteria = (CriteriaImpl.Subcriteria) getCriteria( path );
|
final CriteriaImpl.Subcriteria subcriteria = (CriteriaImpl.Subcriteria) getCriteria( path );
|
||||||
return subcriteria != null && subcriteria.hasRestriction();
|
return subcriteria != null && subcriteria.hasRestriction();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,241 @@
|
|||||||
|
/*
|
||||||
|
* 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.criteria.internal;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.OneToMany;
|
||||||
|
import javax.persistence.Temporal;
|
||||||
|
import javax.persistence.TemporalType;
|
||||||
|
|
||||||
|
import org.hibernate.Criteria;
|
||||||
|
import org.hibernate.criterion.CriteriaSpecification;
|
||||||
|
import org.hibernate.criterion.Criterion;
|
||||||
|
import org.hibernate.criterion.ProjectionList;
|
||||||
|
import org.hibernate.criterion.Projections;
|
||||||
|
import org.hibernate.criterion.Restrictions;
|
||||||
|
import org.hibernate.dialect.H2Dialect;
|
||||||
|
import org.hibernate.sql.JoinType;
|
||||||
|
|
||||||
|
import org.hibernate.testing.RequiresDialect;
|
||||||
|
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Vlad Mihalcea
|
||||||
|
*/
|
||||||
|
public class AliasWithCriterionTest extends BaseCoreFunctionalTestCase {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCaseClause() {
|
||||||
|
doInHibernate( this::sessionFactory, session -> {
|
||||||
|
Criteria criteria = session.createCriteria( TableA.class );
|
||||||
|
|
||||||
|
final String TABLE_B_ALIAS = "tableBAlias";
|
||||||
|
final String TABLE_C_ALIAS = "tableCAlias";
|
||||||
|
|
||||||
|
Criterion tableCRestriction = Restrictions.eq( TABLE_C_ALIAS + ".tableCBoolean", false );
|
||||||
|
criteria.createAlias(
|
||||||
|
TABLE_B_ALIAS + ".tableCs",
|
||||||
|
TABLE_C_ALIAS,
|
||||||
|
JoinType.LEFT_OUTER_JOIN,
|
||||||
|
tableCRestriction
|
||||||
|
);
|
||||||
|
|
||||||
|
Criterion tableBRestriction = Restrictions.eq( TABLE_B_ALIAS + ".tableBDate", new Date() );
|
||||||
|
criteria.createAlias( "tableBs", TABLE_B_ALIAS, JoinType.LEFT_OUTER_JOIN, tableBRestriction );
|
||||||
|
|
||||||
|
criteria.add( Restrictions.eq( "tableACharacter", "c" ) );
|
||||||
|
|
||||||
|
ProjectionList projectionList = Projections.projectionList();
|
||||||
|
projectionList.add( Projections.property( "tableACharacter" ) );
|
||||||
|
criteria.setProjection( projectionList );
|
||||||
|
|
||||||
|
criteria.list();
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class[] getAnnotatedClasses() {
|
||||||
|
return new Class[] {
|
||||||
|
TableA.class,
|
||||||
|
TableB.class,
|
||||||
|
TableC.class,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "TableA")
|
||||||
|
public static class TableA {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Column(name = "table_a_id")
|
||||||
|
private Long tableAId;
|
||||||
|
|
||||||
|
@Column(name = "table_a_character")
|
||||||
|
private String tableACharacter;
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = "tableA")
|
||||||
|
private Set<TableB> tableBs;
|
||||||
|
|
||||||
|
public TableA() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getTableAId() {
|
||||||
|
return this.tableAId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTableAId_(Long _tableAId_) {
|
||||||
|
this.tableAId = _tableAId_;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTableACharacter() {
|
||||||
|
return this.tableACharacter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTableACharacter(String _tableACharacter_) {
|
||||||
|
this.tableACharacter = _tableACharacter_;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<TableB> getTableBs() {
|
||||||
|
return this.tableBs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTableBs(Set<TableB> tableBs) {
|
||||||
|
this.tableBs = tableBs;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "TableB")
|
||||||
|
public static class TableB {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Column(name = "table_b_id")
|
||||||
|
private Long tableBId;
|
||||||
|
|
||||||
|
@Column(name = "table_a_id", insertable = false, updatable = false)
|
||||||
|
private Long tableAId;
|
||||||
|
|
||||||
|
@Temporal(TemporalType.DATE)
|
||||||
|
@Column(name = "table_b_date")
|
||||||
|
private Date tableBDate;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "table_a_id")
|
||||||
|
private TableA tableA;
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = "tableB")
|
||||||
|
private Set<TableC> tableCs;
|
||||||
|
|
||||||
|
|
||||||
|
public TableB() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getTableBId() {
|
||||||
|
return this.tableBId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTableBId(Long _tableBId_) {
|
||||||
|
this.tableBId = _tableBId_;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getTableAId() {
|
||||||
|
return this.tableAId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTableAId(Long _tableAId_) {
|
||||||
|
this.tableAId = _tableAId_;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getTableBDate() {
|
||||||
|
return this.tableBDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTableBDate(Date _tableBDate_) {
|
||||||
|
this.tableBDate = _tableBDate_;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TableA getTableA() {
|
||||||
|
return tableA;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTableA(TableA tableA) {
|
||||||
|
this.tableA = tableA;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<TableC> getTableCs() {
|
||||||
|
return tableCs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTableCs(Set<TableC> tableCs) {
|
||||||
|
this.tableCs = tableCs;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "TableC")
|
||||||
|
public static class TableC {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Column(name = "table_c_id")
|
||||||
|
private Long tableCId;
|
||||||
|
|
||||||
|
@Column(name = "table_b_id", insertable = false, updatable = false)
|
||||||
|
private Long tableBId;
|
||||||
|
|
||||||
|
@Column(name = "table_c_boolean")
|
||||||
|
private Boolean tableCBoolean;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "table_b_id")
|
||||||
|
private TableB tableB;
|
||||||
|
|
||||||
|
public TableC() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getTableCId() {
|
||||||
|
return this.tableCId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTableCId(Long tableCId) {
|
||||||
|
this.tableCId = tableCId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getTableBId() {
|
||||||
|
return this.tableBId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTableBId(Long tableBId) {
|
||||||
|
this.tableBId = tableBId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getTableCBoolean() {
|
||||||
|
return this.tableCBoolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTableCBoolean(Boolean tableCBoolean) {
|
||||||
|
this.tableCBoolean = tableCBoolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TableB getTableB() {
|
||||||
|
return tableB;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTableB(TableB tableB) {
|
||||||
|
this.tableB = tableB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user