HHH-7841 : Redesign Loader

This commit is contained in:
Gail Badner 2013-04-03 00:55:20 -07:00 committed by Steve Ebersole
parent 3f8699c913
commit b3791bc3c3
34 changed files with 1233 additions and 141 deletions

View File

@ -188,6 +188,11 @@ public class DefaultEntityAliases implements EntityAliases {
return rowIdAlias;
}
@Override
public String getSuffix() {
return suffix;
}
private static void intern(String[] strings) {
for (int i=0; i<strings.length; i++ ) {
strings[i] = strings[i].intern();

View File

@ -57,4 +57,9 @@ public interface EntityAliases {
*/
public String getRowIdAlias();
/**
* Returns the suffix used to generate the aliases.
* @return the suffix used to generate the aliases.
*/
public String getSuffix();
}

View File

@ -0,0 +1,114 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
*
* 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, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY 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
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
package org.hibernate.loader.internal;
import java.util.List;
import org.hibernate.LockOptions;
import org.hibernate.MappingException;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.plan.spi.EntityReturn;
import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.sql.JoinFragment;
import org.hibernate.sql.Select;
/**
* Abstract walker for walkers which begin at an entity (criteria
* queries and entity loaders).
*
* @author Gavin King
*/
public abstract class AbstractEntityLoadQueryImpl extends AbstractLoadQueryImpl {
private final EntityReturn entityReturn;
public AbstractEntityLoadQueryImpl(
SessionFactoryImplementor factory,
EntityReturn entityReturn,
List<JoinableAssociationImpl> associations,
List<String> suffixes) {
super( factory, associations, suffixes );
this.entityReturn = entityReturn;
}
protected final String generateSql(
final String whereString,
final String orderByString,
final LockOptions lockOptions) throws MappingException {
return generateSql( null, whereString, orderByString, "", lockOptions );
}
private String generateSql(
final String projection,
final String condition,
final String orderBy,
final String groupBy,
final LockOptions lockOptions) throws MappingException {
JoinFragment ojf = mergeOuterJoins();
Select select = new Select( getDialect() )
.setLockOptions( lockOptions )
.setSelectClause(
projection == null ?
getPersister().selectFragment( getAlias(), entityReturn.getEntityAliases().getSuffix() ) + associationSelectString() :
projection
)
.setFromClause(
getDialect().appendLockHint( lockOptions, getPersister().fromTableFragment( getAlias() ) ) +
getPersister().fromJoinFragment( getAlias(), true, true )
)
.setWhereClause( condition )
.setOuterJoins(
ojf.toFromFragmentString(),
ojf.toWhereFragmentString() + getWhereFragment()
)
.setOrderByClause( orderBy( orderBy ) )
.setGroupByClause( groupBy );
if ( getFactory().getSettings().isCommentsEnabled() ) {
select.setComment( getComment() );
}
return select.toStatementString();
}
protected String getWhereFragment() throws MappingException {
// here we do not bother with the discriminator.
return getPersister().whereJoinFragment( getAlias(), true, true );
}
public abstract String getComment();
public final OuterJoinLoadable getPersister() {
return (OuterJoinLoadable) entityReturn.getEntityPersister();
}
public final String getAlias() {
return entityReturn.getSqlTableAlias();
}
public String toString() {
return getClass().getName() + '(' + getPersister().getEntityName() + ')';
}
}

View File

@ -0,0 +1,241 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* 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, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY 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
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.internal;
import java.util.Iterator;
import java.util.List;
import org.hibernate.MappingException;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.Joinable;
import org.hibernate.sql.ConditionFragment;
import org.hibernate.sql.DisjunctionFragment;
import org.hibernate.sql.InFragment;
import org.hibernate.sql.JoinFragment;
import org.hibernate.sql.JoinType;
/**
* Walks the metamodel, searching for joins, and collecting
* together information needed by <tt>OuterJoinLoader</tt>.
*
* @see org.hibernate.loader.OuterJoinLoader
* @author Gavin King, Jon Lipsky
*/
public abstract class AbstractLoadQueryImpl {
private final SessionFactoryImplementor factory;
private final List<JoinableAssociationImpl> associations;
private final List<String> suffixes;
private String[] collectionSuffixes;
protected AbstractLoadQueryImpl(
SessionFactoryImplementor factory,
List<JoinableAssociationImpl> associations,
List<String> suffixes) {
this.factory = factory;
this.associations = associations;
// TODO: we should be able to get the suffixes out of associations.
this.suffixes = suffixes;
}
protected SessionFactoryImplementor getFactory() {
return factory;
}
protected Dialect getDialect() {
return factory.getDialect();
}
protected String orderBy(final String orderBy) {
return mergeOrderings( orderBy( associations ), orderBy );
}
protected static String mergeOrderings(String ordering1, String ordering2) {
if ( ordering1.length() == 0 ) {
return ordering2;
}
else if ( ordering2.length() == 0 ) {
return ordering1;
}
else {
return ordering1 + ", " + ordering2;
}
}
/**
* Generate a sequence of <tt>LEFT OUTER JOIN</tt> clauses for the given associations.
*/
protected final JoinFragment mergeOuterJoins()
throws MappingException {
JoinFragment outerjoin = getDialect().createOuterJoinFragment();
JoinableAssociationImpl last = null;
for ( JoinableAssociationImpl oj : associations ) {
if ( last != null && last.isManyToManyWith( oj ) ) {
oj.addManyToManyJoin( outerjoin, ( QueryableCollection ) last.getJoinable() );
}
else {
oj.addJoins(outerjoin);
}
last = oj;
}
return outerjoin;
}
/**
* Count the number of instances of Joinable which are actually
* also instances of PersistentCollection which are being fetched
* by outer join
*/
protected static final int countCollectionPersisters(List associations)
throws MappingException {
int result = 0;
Iterator iter = associations.iterator();
while ( iter.hasNext() ) {
JoinableAssociationImpl oj = (JoinableAssociationImpl) iter.next();
if ( oj.getJoinType()==JoinType.LEFT_OUTER_JOIN &&
oj.getJoinable().isCollection() &&
! oj.hasRestriction() ) {
result++;
}
}
return result;
}
/**
* Get the order by string required for collection fetching
*/
protected static final String orderBy(List<JoinableAssociationImpl> associations)
throws MappingException {
StringBuilder buf = new StringBuilder();
JoinableAssociationImpl last = null;
for ( JoinableAssociationImpl oj : associations ) {
if ( oj.getJoinType() == JoinType.LEFT_OUTER_JOIN ) { // why does this matter?
if ( oj.getJoinable().isCollection() ) {
final QueryableCollection queryableCollection = (QueryableCollection) oj.getJoinable();
if ( queryableCollection.hasOrdering() ) {
final String orderByString = queryableCollection.getSQLOrderByString( oj.getRHSAlias() );
buf.append( orderByString ).append(", ");
}
}
else {
// it might still need to apply a collection ordering based on a
// many-to-many defined order-by...
if ( last != null && last.getJoinable().isCollection() ) {
final QueryableCollection queryableCollection = (QueryableCollection) last.getJoinable();
if ( queryableCollection.isManyToMany() && last.isManyToManyWith( oj ) ) {
if ( queryableCollection.hasManyToManyOrdering() ) {
final String orderByString = queryableCollection.getManyToManyOrderByString( oj.getRHSAlias() );
buf.append( orderByString ).append(", ");
}
}
}
}
}
last = oj;
}
if ( buf.length()>0 ) buf.setLength( buf.length()-2 );
return buf.toString();
}
/**
* Render the where condition for a (batch) load by identifier / collection key
*/
protected StringBuilder whereString(String alias, String[] columnNames, int batchSize) {
if ( columnNames.length==1 ) {
// if not a composite key, use "foo in (?, ?, ?)" for batching
// if no batch, and not a composite key, use "foo = ?"
InFragment in = new InFragment().setColumn( alias, columnNames[0] );
for ( int i=0; i<batchSize; i++ ) in.addValue("?");
return new StringBuilder( in.toFragmentString() );
}
else {
//a composite key
ConditionFragment byId = new ConditionFragment()
.setTableAlias(alias)
.setCondition( columnNames, "?" );
StringBuilder whereString = new StringBuilder();
if ( batchSize==1 ) {
// if no batch, use "foo = ? and bar = ?"
whereString.append( byId.toFragmentString() );
}
else {
// if a composite key, use "( (foo = ? and bar = ?) or (foo = ? and bar = ?) )" for batching
whereString.append('('); //TODO: unnecessary for databases with ANSI-style joins
DisjunctionFragment df = new DisjunctionFragment();
for ( int i=0; i<batchSize; i++ ) {
df.addCondition(byId);
}
whereString.append( df.toFragmentString() );
whereString.append(')'); //TODO: unnecessary for databases with ANSI-style joins
}
return whereString;
}
}
/**
* Generate a select list of columns containing all properties of the entity classes
*/
protected final String associationSelectString()
throws MappingException {
if ( associations.size() == 0 ) {
return "";
}
else {
StringBuilder buf = new StringBuilder( associations.size() * 100 );
int entityAliasCount=0;
int collectionAliasCount=0;
for ( int i=0; i<associations.size(); i++ ) {
JoinableAssociationImpl join = associations.get(i);
JoinableAssociationImpl next = (i == associations.size() - 1)
? null
: associations.get( i + 1 );
final Joinable joinable = join.getJoinable();
final String entitySuffix = ( suffixes == null || entityAliasCount >= suffixes.size() )
? null
: suffixes.get( entityAliasCount );
final String collectionSuffix = ( collectionSuffixes == null || collectionAliasCount >= collectionSuffixes.length )
? null
: collectionSuffixes[collectionAliasCount];
final String selectFragment = joinable.selectFragment(
next == null ? null : next.getJoinable(),
next == null ? null : next.getRHSAlias(),
join.getRHSAlias(),
entitySuffix,
collectionSuffix,
join.getJoinType()==JoinType.LEFT_OUTER_JOIN
);
if (selectFragment.trim().length() > 0) {
buf.append(", ").append(selectFragment);
}
if ( joinable.consumesEntityAlias() ) entityAliasCount++;
if ( joinable.consumesCollectionAlias() && join.getJoinType()==JoinType.LEFT_OUTER_JOIN ) collectionAliasCount++;
}
return buf.toString();
}
}
}

View File

@ -0,0 +1,140 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* 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, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY 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
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.internal;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.plan.spi.CollectionFetch;
import org.hibernate.loader.plan.spi.CompositeFetch;
import org.hibernate.loader.plan.spi.EntityFetch;
import org.hibernate.loader.plan.spi.EntityReturn;
import org.hibernate.loader.plan.spi.LoadPlan;
import org.hibernate.loader.plan.spi.LoadPlanVisitationStrategyAdapter;
import org.hibernate.loader.plan.spi.LoadPlanVisitor;
import org.hibernate.loader.spi.LoadQueryBuilder;
import org.hibernate.persister.entity.OuterJoinLoadable;
/**
* @author Gail Badner
*/
public class EntityLoadQueryBuilderImpl implements LoadQueryBuilder {
private final SessionFactoryImplementor sessionFactory;
private final LoadQueryInfluencers loadQueryInfluencers;
private final LoadPlan loadPlan;
private final List<JoinableAssociationImpl> associations;
private final List<String> suffixes;
public EntityLoadQueryBuilderImpl(
SessionFactoryImplementor sessionFactory,
LoadQueryInfluencers loadQueryInfluencers,
LoadPlan loadPlan) {
this.sessionFactory = sessionFactory;
this.loadQueryInfluencers = loadQueryInfluencers;
this.loadPlan = loadPlan;
LocalVisitationStrategy strategy = new LocalVisitationStrategy();
LoadPlanVisitor.visit( loadPlan, strategy );
this.associations = strategy.associations;
this.suffixes = strategy.suffixes;
}
@Override
public String generateSql(int batchSize) {
return generateSql( batchSize, getOuterJoinLoadable().getKeyColumnNames() );
}
public String generateSql(int batchSize, String[] uniqueKey) {
final EntityLoadQueryImpl loadQuery = new EntityLoadQueryImpl(
sessionFactory,
getRootEntityReturn(),
associations,
suffixes
);
return loadQuery.generateSql( uniqueKey, batchSize, getRootEntityReturn().getLockMode() );
}
private EntityReturn getRootEntityReturn() {
return (EntityReturn) loadPlan.getReturns().get( 0 );
}
private OuterJoinLoadable getOuterJoinLoadable() {
return (OuterJoinLoadable) getRootEntityReturn().getEntityPersister();
}
private class LocalVisitationStrategy extends LoadPlanVisitationStrategyAdapter {
private final List<JoinableAssociationImpl> associations = new ArrayList<JoinableAssociationImpl>();
private final List<String> suffixes = new ArrayList<String>();
private EntityReturn entityRootReturn;
@Override
public void handleEntityReturn(EntityReturn rootEntityReturn) {
this.entityRootReturn = rootEntityReturn;
}
@Override
public void startingEntityFetch(EntityFetch entityFetch) {
JoinableAssociationImpl assoc = new JoinableAssociationImpl(
entityFetch,
"", // getWithClause( entityFetch.getPropertyPath() )
false, // hasRestriction( entityFetch.getPropertyPath() )
sessionFactory,
loadQueryInfluencers.getEnabledFilters()
);
associations.add( assoc );
suffixes.add( entityFetch.getEntityAliases().getSuffix() );
}
@Override
public void finishingEntityFetch(EntityFetch entityFetch) {
//To change body of implemented methods use File | Settings | File Templates.
}
@Override
public void startingCollectionFetch(CollectionFetch collectionFetch) {
//To change body of implemented methods use File | Settings | File Templates.
}
@Override
public void finishingCollectionFetch(CollectionFetch collectionFetch) {
//To change body of implemented methods use File | Settings | File Templates.
}
@Override
public void startingCompositeFetch(CompositeFetch fetch) {
//To change body of implemented methods use File | Settings | File Templates.
}
@Override
public void finishingCompositeFetch(CompositeFetch fetch) {
//To change body of implemented methods use File | Settings | File Templates.
}
@Override
public void finish(LoadPlan loadPlan) {
//suffixes.add( entityRootReturn.getEntityAliases().getSuffix() );
}
}
}

View File

@ -0,0 +1,61 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* 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, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY 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
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.internal;
import java.util.Collections;
import java.util.List;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.MappingException;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.plan.spi.EntityReturn;
/**
* A walker for loaders that fetch entities
*
* @see org.hibernate.loader.entity.EntityLoader
* @author Gavin King
*/
public class EntityLoadQueryImpl extends AbstractEntityLoadQueryImpl {
public EntityLoadQueryImpl(
final SessionFactoryImplementor factory,
EntityReturn entityReturn,
List<JoinableAssociationImpl> associations,
List<String> suffixes) throws MappingException {
super( factory, entityReturn, associations, suffixes );
}
public String generateSql(String[] uniqueKey, int batchSize, LockMode lockMode) {
StringBuilder whereCondition = whereString( getAlias(), uniqueKey, batchSize )
//include the discriminator and class-level where, but not filters
.append( getPersister().filterFragment( getAlias(), Collections.EMPTY_MAP ) );
return generateSql( whereCondition.toString(), "", new LockOptions().setLockMode( lockMode ) );
}
public String getComment() {
return "load " + getPersister().getEntityName();
}
}

View File

@ -0,0 +1,227 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* 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, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY 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
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.internal;
import java.util.List;
import java.util.Map;
import org.hibernate.MappingException;
import org.hibernate.cfg.NotYetImplementedException;
import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.internal.JoinHelper;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.PropertyPath;
import org.hibernate.loader.plan.spi.EntityFetch;
import org.hibernate.loader.plan.spi.EntityReference;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Joinable;
import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.sql.JoinFragment;
import org.hibernate.sql.JoinType;
import org.hibernate.type.AssociationType;
import org.hibernate.type.EntityType;
/**
* Part of the Hibernate SQL rendering internals. This class represents
* a joinable association.
*
* @author Gavin King
* @author Gail Badner
*/
public final class JoinableAssociationImpl {
private final PropertyPath propertyPath;
private final AssociationType joinableType;
private final Joinable joinable;
private final String lhsAlias; // belong to other persister
private final String[] lhsColumns; // belong to other persister
private final String rhsAlias;
private final String[] rhsColumns;
private final JoinType joinType;
private final String on;
private final Map enabledFilters;
private final boolean hasRestriction;
public JoinableAssociationImpl(
EntityFetch entityFetch,
String withClause,
boolean hasRestriction,
SessionFactoryImplementor factory,
Map enabledFilters) throws MappingException {
this.propertyPath = entityFetch.getPropertyPath();
this.joinableType = entityFetch.getAssociationType();
// TODO: this is not correct
final EntityPersister fetchSourcePersister = entityFetch.getOwner().retrieveFetchSourcePersister();
final int propertyNumber = fetchSourcePersister.getEntityMetamodel().getPropertyIndex( entityFetch.getOwnerPropertyName() );
if ( EntityReference.class.isInstance( entityFetch.getOwner() ) ) {
this.lhsAlias = ( (EntityReference) entityFetch.getOwner() ).getSqlTableAlias();
}
else {
throw new NotYetImplementedException( "Cannot determine LHS alias for a FetchOwner that is not an EntityReference." );
}
final OuterJoinLoadable ownerPersister = (OuterJoinLoadable) entityFetch.getOwner().retrieveFetchSourcePersister();
this.lhsColumns = JoinHelper.getAliasedLHSColumnNames(
entityFetch.getAssociationType(), lhsAlias, propertyNumber, ownerPersister, factory
);
this.rhsAlias = entityFetch.getSqlTableAlias();
final boolean isNullable = ownerPersister.isSubclassPropertyNullable( propertyNumber );
if ( entityFetch.getFetchStrategy().getStyle() == FetchStyle.JOIN ) {
joinType = isNullable ? JoinType.LEFT_OUTER_JOIN : JoinType.INNER_JOIN;
}
else {
joinType = JoinType.NONE;
}
this.joinable = joinableType.getAssociatedJoinable(factory);
this.rhsColumns = JoinHelper.getRHSColumnNames(joinableType, factory);
this.on = joinableType.getOnCondition( rhsAlias, factory, enabledFilters )
+ ( withClause == null || withClause.trim().length() == 0 ? "" : " and ( " + withClause + " )" );
this.hasRestriction = hasRestriction;
this.enabledFilters = enabledFilters; // needed later for many-to-many/filter application
}
public PropertyPath getPropertyPath() {
return propertyPath;
}
public JoinType getJoinType() {
return joinType;
}
public String getLhsAlias() {
return lhsAlias;
}
public String getRHSAlias() {
return rhsAlias;
}
private boolean isOneToOne() {
if ( joinableType.isEntityType() ) {
EntityType etype = (EntityType) joinableType;
return etype.isOneToOne() /*&& etype.isReferenceToPrimaryKey()*/;
}
else {
return false;
}
}
public AssociationType getJoinableType() {
return joinableType;
}
public String getRHSUniqueKeyName() {
return joinableType.getRHSUniqueKeyPropertyName();
}
public boolean isCollection() {
return joinableType.isCollectionType();
}
public Joinable getJoinable() {
return joinable;
}
public boolean hasRestriction() {
return hasRestriction;
}
public int getOwner(final List associations) {
if ( isOneToOne() || isCollection() ) {
return getPosition(lhsAlias, associations);
}
else {
return -1;
}
}
/**
* Get the position of the join with the given alias in the
* list of joins
*/
private static int getPosition(String lhsAlias, List associations) {
int result = 0;
for ( int i=0; i<associations.size(); i++ ) {
JoinableAssociationImpl oj = (JoinableAssociationImpl) associations.get(i);
if ( oj.getJoinable().consumesEntityAlias() /*|| oj.getJoinable().consumesCollectionAlias() */ ) {
if ( oj.rhsAlias.equals(lhsAlias) ) return result;
result++;
}
}
return -1;
}
public void addJoins(JoinFragment outerjoin) throws MappingException {
outerjoin.addJoin(
joinable.getTableName(),
rhsAlias,
lhsColumns,
rhsColumns,
joinType,
on
);
outerjoin.addJoins(
joinable.fromJoinFragment(rhsAlias, false, true),
joinable.whereJoinFragment(rhsAlias, false, true)
);
}
public void validateJoin(String path) throws MappingException {
if ( rhsColumns==null || lhsColumns==null
|| lhsColumns.length!=rhsColumns.length || lhsColumns.length==0 ) {
throw new MappingException("invalid join columns for association: " + path);
}
}
public boolean isManyToManyWith(JoinableAssociationImpl other) {
if ( joinable.isCollection() ) {
QueryableCollection persister = ( QueryableCollection ) joinable;
if ( persister.isManyToMany() ) {
return persister.getElementType() == other.getJoinableType();
}
}
return false;
}
public void addManyToManyJoin(JoinFragment outerjoin, QueryableCollection collection) throws MappingException {
String manyToManyFilter = collection.getManyToManyFilterFragment( rhsAlias, enabledFilters );
String condition = "".equals( manyToManyFilter )
? on
: "".equals( on )
? manyToManyFilter
: on + " and " + manyToManyFilter;
outerjoin.addJoin(
joinable.getTableName(),
rhsAlias,
lhsColumns,
rhsColumns,
joinType,
condition
);
outerjoin.addJoins(
joinable.fromJoinFragment(rhsAlias, false, true),
joinable.whereJoinFragment(rhsAlias, false, true)
);
}
}

View File

@ -27,6 +27,7 @@ import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -181,6 +182,13 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
return context;
}
@Override
public Set<IdentifierResolutionContext> getIdentifierResolutionContexts() {
return Collections.unmodifiableSet(
new HashSet<IdentifierResolutionContext>( identifierResolutionContextMap.values() )
);
}
@Override
public void checkVersion(
ResultSet resultSet,

View File

@ -1,66 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* 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, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY 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
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan.internal;
import org.hibernate.LockMode;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.entity.EntityJoinWalker;
import org.hibernate.loader.plan.spi.LoadQuery;
import org.hibernate.persister.entity.OuterJoinLoadable;
/**
* @author Gail Badner
*/
public class EntityLoadQueryImpl implements LoadQuery {
final SessionFactoryImplementor sessionFactory;
final LoadQueryInfluencers loadQueryInfluencers;
final LockMode lockMode;
final OuterJoinLoadable entityPersister;
public EntityLoadQueryImpl(
SessionFactoryImplementor sessionFactory,
LoadQueryInfluencers loadQueryInfluencers,
LockMode lockMode,
OuterJoinLoadable entityPersister) {
this.sessionFactory = sessionFactory;
this.loadQueryInfluencers = loadQueryInfluencers;
this.lockMode = lockMode;
this.entityPersister = entityPersister;
}
@Override
public String generateSql(int batchSize) {
final EntityJoinWalker entityJoinWalker = new EntityJoinWalker(
entityPersister,
entityPersister.getIdentifierColumnNames(),
batchSize,
lockMode,
sessionFactory,
loadQueryInfluencers
);
return entityJoinWalker.getSQLString();
}
}

View File

@ -64,6 +64,7 @@ public class LoadPlanBuildingHelper {
FetchOwner fetchOwner,
AssociationAttributeDefinition attributeDefinition,
FetchStrategy fetchStrategy,
String sqlTableAlias,
LoadPlanBuildingContext loadPlanBuildingContext) {
return new EntityFetch(
@ -73,7 +74,7 @@ public class LoadPlanBuildingHelper {
fetchOwner,
attributeDefinition.getName(),
fetchStrategy,
null, // sql table alias
sqlTableAlias,
loadPlanBuildingContext.resolveEntityColumnAliases( attributeDefinition )
);
}

View File

@ -26,9 +26,7 @@ package org.hibernate.loader.plan.internal;
import java.util.Collections;
import java.util.List;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.loader.plan.spi.LoadPlan;
import org.hibernate.loader.plan.spi.LoadQuery;
import org.hibernate.loader.plan.spi.Return;
/**
@ -39,16 +37,14 @@ import org.hibernate.loader.plan.spi.Return;
public class LoadPlanImpl implements LoadPlan {
private final boolean hasScalars;
private final List<Return> returns;
private final LoadQuery loadQuery;
public LoadPlanImpl(LoadQuery loadQuery, boolean hasScalars, List<Return> returns) {
this.loadQuery = loadQuery;
public LoadPlanImpl(boolean hasScalars, List<Return> returns) {
this.hasScalars = hasScalars;
this.returns = returns;
}
public LoadPlanImpl(LoadQuery loadQuery, boolean hasScalars, Return rootReturn) {
this( loadQuery, hasScalars, Collections.singletonList( rootReturn ) );
public LoadPlanImpl(boolean hasScalars, Return rootReturn) {
this( hasScalars, Collections.singletonList( rootReturn ) );
}
@Override
@ -60,9 +56,4 @@ public class LoadPlanImpl implements LoadPlan {
public List<Return> getReturns() {
return returns;
}
@Override
public LoadQuery getLoadQuery() {
return loadQuery;
}
}

View File

@ -39,11 +39,9 @@ import org.hibernate.loader.plan.spi.CollectionReturn;
import org.hibernate.loader.plan.spi.EntityReturn;
import org.hibernate.loader.plan.spi.LoadPlan;
import org.hibernate.loader.plan.spi.LoadPlanBuilderStrategy;
import org.hibernate.loader.plan.spi.LoadQuery;
import org.hibernate.loader.plan.spi.Return;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.CollectionDefinition;
import org.hibernate.persister.walking.spi.EntityDefinition;
@ -103,23 +101,7 @@ public class SingleRootReturnLoadPlanBuilderStrategy
@Override
public LoadPlan buildLoadPlan() {
return new LoadPlanImpl( createLoadQuery(), false, rootReturn );
}
private LoadQuery createLoadQuery() {
if ( EntityReturn.class.isInstance( rootReturn ) ) {
final EntityReturn entityReturn = (EntityReturn) rootReturn;
return new EntityLoadQueryImpl(
sessionFactory(),
loadQueryInfluencers,
entityReturn.getLockMode(),
(OuterJoinLoadable) entityReturn.getEntityPersister()
);
}
else {
// TODO: create a LoadQuery for other types of returns.
return null;
}
return new LoadPlanImpl( false, rootReturn );
}
@Override

View File

@ -26,9 +26,12 @@ package org.hibernate.loader.plan.spi;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.AssertionFailure;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.spi.ResultSetProcessingContext;
/**
* @author Steve Ebersole

View File

@ -401,7 +401,12 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
associationFetch = fetchOwner.buildCollectionFetch( attributeDefinition, fetchStrategy, this );
}
else {
associationFetch = fetchOwner.buildEntityFetch( attributeDefinition, fetchStrategy, this );
associationFetch = fetchOwner.buildEntityFetch(
attributeDefinition,
fetchStrategy,
generateEntityFetchSqlTableAlias( attributeDefinition.toEntityDefinition().getEntityPersister().getEntityName() ),
this
);
}
if ( FetchOwner.class.isInstance( associationFetch ) ) {
@ -477,6 +482,10 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
return sessionFactory();
}
protected String generateEntityFetchSqlTableAlias(String entityName) {
return StringHelper.generateAlias( StringHelper.unqualifyEntityName( entityName ), currentDepth() );
}
@Override
public EntityAliases resolveEntityColumnAliases(AssociationAttributeDefinition attributeDefinition) {
return generateEntityColumnAliases( attributeDefinition.toEntityDefinition().getEntityPersister() );
@ -539,6 +548,11 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
return entityReference.getAlias();
}
@Override
public String getSqlTableAlias() {
return entityReference.getSqlTableAlias();
}
@Override
public LockMode getLockMode() {
return entityReference.getLockMode();
@ -566,6 +580,7 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
public EntityFetch buildEntityFetch(
AssociationAttributeDefinition attributeDefinition,
FetchStrategy fetchStrategy,
String sqlTableAlias,
LoadPlanBuildingContext loadPlanBuildingContext) {
// we have a key-many-to-one
//
@ -575,6 +590,7 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
this,
attributeDefinition,
fetchStrategy,
sqlTableAlias,
loadPlanBuildingContext
);
fetchToHydratedStateExtractorMap.put( fetch, attributeDefinition.getHydratedCompoundValueExtractor() );

View File

@ -33,14 +33,14 @@ import org.hibernate.loader.PropertyPath;
/**
* @author Steve Ebersole
*/
public abstract class AbstractFetch extends AbstractFetchOwner implements Fetch {
public abstract class AbstractSingularAttributeFetch extends AbstractFetchOwner implements Fetch {
private final FetchOwner owner;
private final String ownerProperty;
private final FetchStrategy fetchStrategy;
private final PropertyPath propertyPath;
public AbstractFetch(
public AbstractSingularAttributeFetch(
SessionFactoryImplementor factory,
String alias,
LockMode lockMode,

View File

@ -31,13 +31,12 @@ import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.CollectionAliases;
import org.hibernate.loader.EntityAliases;
import org.hibernate.loader.PropertyPath;
import org.hibernate.loader.spi.ResultSetProcessingContext;
/**
* @author Steve Ebersole
*/
public class CollectionFetch extends AbstractCollectionReference implements CollectionReference, Fetch {
public class CollectionFetch extends AbstractCollectionReference implements Fetch {
private final FetchOwner fetchOwner;
private final FetchStrategy fetchStrategy;

View File

@ -36,7 +36,7 @@ import org.hibernate.loader.spi.ResultSetProcessingContext;
/**
* @author Steve Ebersole
*/
public class CollectionReturn extends AbstractCollectionReference implements Return, CollectionReference {
public class CollectionReturn extends AbstractCollectionReference implements Return {
private final String ownerEntityName;
private final String ownerProperty;

View File

@ -72,11 +72,13 @@ public class CompositeElementGraph extends AbstractPlanNode implements FetchOwne
public EntityFetch buildEntityFetch(
AssociationAttributeDefinition attributeDefinition,
FetchStrategy fetchStrategy,
String sqlTableAlias,
LoadPlanBuildingContext loadPlanBuildingContext) {
return LoadPlanBuildingHelper.buildStandardEntityFetch(
this,
attributeDefinition,
fetchStrategy,
sqlTableAlias,
loadPlanBuildingContext
);
}

View File

@ -39,7 +39,7 @@ import org.hibernate.persister.walking.spi.CompositionDefinition;
/**
* @author Steve Ebersole
*/
public class CompositeFetch extends AbstractFetch implements Fetch {
public class CompositeFetch extends AbstractSingularAttributeFetch {
public static final FetchStrategy FETCH_PLAN = new FetchStrategy( FetchTiming.IMMEDIATE, FetchStyle.JOIN );
public CompositeFetch(
@ -67,7 +67,7 @@ public class CompositeFetch extends AbstractFetch implements Fetch {
public EntityFetch buildEntityFetch(
AssociationAttributeDefinition attributeDefinition,
FetchStrategy fetchStrategy,
LoadPlanBuildingContext loadPlanBuildingContext) {
String sqlTableAlias, LoadPlanBuildingContext loadPlanBuildingContext) {
return null; //To change body of implemented methods use File | Settings | File Templates.
}

View File

@ -72,11 +72,13 @@ public class CompositeIndexGraph extends AbstractPlanNode implements FetchOwner
public EntityFetch buildEntityFetch(
AssociationAttributeDefinition attributeDefinition,
FetchStrategy fetchStrategy,
String sqlTableAlias,
LoadPlanBuildingContext loadPlanBuildingContext) {
return LoadPlanBuildingHelper.buildStandardEntityFetch(
this,
attributeDefinition,
fetchStrategy,
sqlTableAlias,
loadPlanBuildingContext
);
}

View File

@ -47,6 +47,11 @@ public class EntityElementGraph extends AbstractPlanNode implements FetchOwner,
return null;
}
@Override
public String getSqlTableAlias() {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public LockMode getLockMode() {
return null;
@ -111,11 +116,13 @@ public class EntityElementGraph extends AbstractPlanNode implements FetchOwner,
public EntityFetch buildEntityFetch(
AssociationAttributeDefinition attributeDefinition,
FetchStrategy fetchStrategy,
String sqlTableAlias,
LoadPlanBuildingContext loadPlanBuildingContext) {
return LoadPlanBuildingHelper.buildStandardEntityFetch(
this,
attributeDefinition,
fetchStrategy,
sqlTableAlias,
loadPlanBuildingContext
);
}

View File

@ -38,12 +38,13 @@ import org.hibernate.loader.spi.ResultSetProcessingContext;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.CompositionDefinition;
import org.hibernate.type.AssociationType;
import org.hibernate.type.EntityType;
/**
* @author Steve Ebersole
*/
public class EntityFetch extends AbstractFetch implements EntityReference, FetchOwner {
public class EntityFetch extends AbstractSingularAttributeFetch implements EntityReference {
private final String sqlTableAlias;
private final EntityAliases entityAliases;
@ -69,6 +70,10 @@ public class EntityFetch extends AbstractFetch implements EntityReference, Fetch
this.persister = sessionFactory.getEntityPersister( associationType.getAssociatedEntityName() );
}
public EntityType getAssociationType() {
return associationType;
}
@Override
public EntityPersister getEntityPersister() {
return persister;
@ -79,6 +84,11 @@ public class EntityFetch extends AbstractFetch implements EntityReference, Fetch
return identifierDescription;
}
@Override
public String getSqlTableAlias() {
return sqlTableAlias;
}
@Override
public EntityAliases getEntityAliases() {
return entityAliases;
@ -107,11 +117,13 @@ public class EntityFetch extends AbstractFetch implements EntityReference, Fetch
public EntityFetch buildEntityFetch(
AssociationAttributeDefinition attributeDefinition,
FetchStrategy fetchStrategy,
String sqlTableAlias,
LoadPlanBuildingContext loadPlanBuildingContext) {
return LoadPlanBuildingHelper.buildStandardEntityFetch(
this,
attributeDefinition,
fetchStrategy,
sqlTableAlias,
loadPlanBuildingContext
);
}

View File

@ -69,6 +69,11 @@ public class EntityIndexGraph extends AbstractPlanNode implements FetchOwner, En
return null;
}
@Override
public String getSqlTableAlias() {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public LockMode getLockMode() {
return null;
@ -133,11 +138,13 @@ public class EntityIndexGraph extends AbstractPlanNode implements FetchOwner, En
public EntityFetch buildEntityFetch(
AssociationAttributeDefinition attributeDefinition,
FetchStrategy fetchStrategy,
String sqlTableAlias,
LoadPlanBuildingContext loadPlanBuildingContext) {
return LoadPlanBuildingHelper.buildStandardEntityFetch(
this,
attributeDefinition,
fetchStrategy,
sqlTableAlias,
loadPlanBuildingContext
);
}

View File

@ -23,8 +23,11 @@
*/
package org.hibernate.loader.plan.spi;
import org.hibernate.AssertionFailure;
import org.hibernate.LockMode;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.loader.EntityAliases;
import org.hibernate.loader.spi.ResultSetProcessingContext;
import org.hibernate.persister.entity.EntityPersister;
/**
@ -32,7 +35,7 @@ import org.hibernate.persister.entity.EntityPersister;
*
* @author Steve Ebersole
*/
public interface EntityReference extends IdentifierDescriptionInjectable {
public interface EntityReference extends IdentifierDescriptionInjectable, ResultSetProcessingContext.EntityKeyResolutionContext {
/**
* Retrieve the alias associated with the persister (entity/collection).
*
@ -40,6 +43,13 @@ public interface EntityReference extends IdentifierDescriptionInjectable {
*/
public String getAlias();
/**
* Retrieve the SQL table alias.
*
* @return The SQL table alias
*/
public String getSqlTableAlias();
/**
* Retrieve the lock mode associated with this return.
*

View File

@ -44,9 +44,7 @@ import static org.hibernate.loader.spi.ResultSetProcessingContext.IdentifierReso
/**
* @author Steve Ebersole
*/
public class EntityReturn
extends AbstractFetchOwner
implements Return, FetchOwner, EntityReference, ResultSetProcessingContext.EntityKeyResolutionContext {
public class EntityReturn extends AbstractFetchOwner implements Return, EntityReference {
private final EntityAliases entityAliases;
private final String sqlTableAlias;
@ -76,6 +74,11 @@ public class EntityReturn
return super.getAlias();
}
@Override
public String getSqlTableAlias() {
return sqlTableAlias;
}
@Override
public LockMode getLockMode() {
return super.getLockMode();
@ -127,11 +130,13 @@ public class EntityReturn
public EntityFetch buildEntityFetch(
AssociationAttributeDefinition attributeDefinition,
FetchStrategy fetchStrategy,
String sqlTableAlias,
LoadPlanBuildingContext loadPlanBuildingContext) {
return LoadPlanBuildingHelper.buildStandardEntityFetch(
this,
attributeDefinition,
fetchStrategy,
sqlTableAlias,
loadPlanBuildingContext
);
}
@ -176,13 +181,19 @@ public class EntityReturn
@Override
public Object read(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
final IdentifierResolutionContext identifierResolutionContext = context.getIdentifierResolutionContext( this );
EntityKey entityKey = identifierResolutionContext.getEntityKey();
if ( entityKey == null ) {
throw new AssertionFailure( "Could not locate resolved EntityKey");
Object objectForThisEntityReturn = null;
for ( IdentifierResolutionContext identifierResolutionContext : context.getIdentifierResolutionContexts() ) {
final EntityReference entityReference = identifierResolutionContext.getEntityReference();
final EntityKey entityKey = identifierResolutionContext.getEntityKey();
if ( entityKey == null ) {
throw new AssertionFailure( "Could not locate resolved EntityKey");
}
final Object object = context.resolveEntityKey( entityKey, entityReference );
if ( this == entityReference ) {
objectForThisEntityReturn = object;
}
}
return context.resolveEntityKey( entityKey, this );
return objectForThisEntityReturn;
}
@Override

View File

@ -84,6 +84,7 @@ public interface FetchOwner {
public EntityFetch buildEntityFetch(
AssociationAttributeDefinition attributeDefinition,
FetchStrategy fetchStrategy,
String sqlTableAlias,
LoadPlanBuildingContext loadPlanBuildingContext);
public CompositeFetch buildCompositeFetch(

View File

@ -25,8 +25,6 @@ package org.hibernate.loader.plan.spi;
import java.util.List;
import org.hibernate.engine.spi.LoadQueryInfluencers;
/**
* Describes a plan for performing a load of results.
*
@ -57,8 +55,6 @@ public interface LoadPlan {
public List<Return> getReturns();
public LoadQuery getLoadQuery();
// todo : would also like to see "call back" style access for handling "subsequent actions" such as:
// 1) follow-on locking
// 2) join fetch conversions to subselect fetches

View File

@ -21,12 +21,12 @@
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan.spi;
package org.hibernate.loader.spi;
/**
* @author Gail Badner
*/
public interface LoadQuery {
public interface LoadQueryBuilder {
String generateSql(int batchSize);
}

View File

@ -25,6 +25,7 @@ package org.hibernate.loader.spi;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Set;
import org.hibernate.LockMode;
import org.hibernate.engine.spi.EntityKey;
@ -59,6 +60,8 @@ public interface ResultSetProcessingContext {
public IdentifierResolutionContext getIdentifierResolutionContext(EntityReference entityReference);
public Set<IdentifierResolutionContext> getIdentifierResolutionContexts();
public void registerHydratedEntity(EntityPersister persister, EntityKey entityKey, Object entityInstance);
public static interface EntityKeyResolutionContext {

View File

@ -96,6 +96,9 @@ import org.hibernate.loader.entity.BatchingEntityLoaderBuilder;
import org.hibernate.loader.entity.CascadeEntityLoader;
import org.hibernate.loader.entity.EntityLoader;
import org.hibernate.loader.entity.UniqueEntityLoader;
import org.hibernate.loader.plan.internal.SingleRootReturnLoadPlanBuilderStrategy;
import org.hibernate.loader.plan.spi.LoadPlan;
import org.hibernate.loader.plan.spi.LoadPlanBuilder;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.PersistentClass;
@ -127,6 +130,10 @@ import org.hibernate.sql.SelectFragment;
import org.hibernate.sql.SimpleSelect;
import org.hibernate.sql.Template;
import org.hibernate.sql.Update;
import org.hibernate.sql.ordering.antlr.ColumnMapper;
import org.hibernate.sql.ordering.antlr.ColumnReference;
import org.hibernate.sql.ordering.antlr.FormulaReference;
import org.hibernate.sql.ordering.antlr.SqlValueReference;
import org.hibernate.tuple.entity.EntityMetamodel;
import org.hibernate.tuple.entity.EntityTuplizer;
import org.hibernate.type.AssociationType;

View File

@ -45,6 +45,7 @@ import org.hibernate.id.IdentifierGenerator;
import org.hibernate.internal.FilterAliasGenerator;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.persister.walking.spi.EntityDefinition;
import org.hibernate.sql.ordering.antlr.ColumnMapper;
import org.hibernate.tuple.entity.EntityMetamodel;
import org.hibernate.tuple.entity.EntityTuplizer;
import org.hibernate.type.Type;

View File

@ -0,0 +1,312 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* 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, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY 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
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import org.junit.Test;
import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.jdbc.Work;
import org.hibernate.loader.internal.EntityLoadQueryBuilderImpl;
import org.hibernate.loader.internal.ResultSetProcessorImpl;
import org.hibernate.loader.plan.internal.SingleRootReturnLoadPlanBuilderStrategy;
import org.hibernate.loader.plan.spi.LoadPlan;
import org.hibernate.loader.plan.spi.LoadPlanBuilder;
import org.hibernate.loader.spi.NamedParameterContext;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.hibernate.testing.junit4.ExtraAssertions;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
* @author Steve Ebersole
*/
public class AssociationResultSetProcessorTest extends BaseCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { Message.class, Poster.class, ReportedMessage.class };
}
@Test
public void testManyToOneEntityProcessing() throws Exception {
final EntityPersister entityPersister = sessionFactory().getEntityPersister( Message.class.getName() );
// create some test data
Session session = openSession();
session.beginTransaction();
Message message = new Message( 1, "the message" );
Poster poster = new Poster( 2, "the poster" );
session.save( message );
session.save( poster );
message.poster = poster;
poster.messages.add( message );
session.getTransaction().commit();
session.close();
{
final SingleRootReturnLoadPlanBuilderStrategy strategy = new SingleRootReturnLoadPlanBuilderStrategy(
sessionFactory(),
LoadQueryInfluencers.NONE,
"abc",
0
);
final LoadPlan plan = LoadPlanBuilder.buildRootEntityLoadPlan( strategy, entityPersister );
final EntityLoadQueryBuilderImpl queryBuilder = new EntityLoadQueryBuilderImpl(
sessionFactory(),
LoadQueryInfluencers.NONE,
plan
);
final String sql = queryBuilder.generateSql( 1 );
final ResultSetProcessorImpl resultSetProcessor = new ResultSetProcessorImpl( plan );
final List results = new ArrayList();
final Session workSession = openSession();
workSession.beginTransaction();
workSession.doWork(
new Work() {
@Override
public void execute(Connection connection) throws SQLException {
PreparedStatement ps = connection.prepareStatement( sql );
ps.setInt( 1, 1 );
ResultSet resultSet = ps.executeQuery();
results.addAll(
resultSetProcessor.extractResults(
resultSet,
(SessionImplementor) workSession,
new QueryParameters(),
new NamedParameterContext() {
@Override
public int[] getNamedParameterLocations(String name) {
return new int[0];
}
},
true,
false,
null,
null
)
);
resultSet.close();
ps.close();
}
}
);
assertEquals( 1, results.size() );
Object result = results.get( 0 );
assertNotNull( result );
Message workMessage = ExtraAssertions.assertTyping( Message.class, result );
assertEquals( 1, workMessage.mid.intValue() );
assertEquals( "the message", workMessage.msgTxt );
assertTrue( Hibernate.isInitialized( workMessage.poster ) );
Poster workPoster = workMessage.poster;
assertEquals( 2, workPoster.pid.intValue() );
assertEquals( "the poster", workPoster.name );
assertFalse( Hibernate.isInitialized( workPoster.messages ) );
workSession.getTransaction().commit();
workSession.close();
}
// clean up test data
session = openSession();
session.beginTransaction();
session.createQuery( "delete Message" ).executeUpdate();
session.createQuery( "delete Poster" ).executeUpdate();
session.getTransaction().commit();
session.close();
}
@Test
public void testNestedManyToOneEntityProcessing() throws Exception {
final EntityPersister entityPersister = sessionFactory().getEntityPersister( ReportedMessage.class.getName() );
// create some test data
Session session = openSession();
session.beginTransaction();
Message message = new Message( 1, "the message" );
Poster poster = new Poster( 2, "the poster" );
session.save( message );
session.save( poster );
message.poster = poster;
poster.messages.add( message );
ReportedMessage reportedMessage = new ReportedMessage( 0, "inappropriate", message );
session.save( reportedMessage );
session.getTransaction().commit();
session.close();
{
final SingleRootReturnLoadPlanBuilderStrategy strategy = new SingleRootReturnLoadPlanBuilderStrategy(
sessionFactory(),
LoadQueryInfluencers.NONE,
"abc",
0
);
final LoadPlan plan = LoadPlanBuilder.buildRootEntityLoadPlan( strategy, entityPersister );
final EntityLoadQueryBuilderImpl queryBuilder = new EntityLoadQueryBuilderImpl(
sessionFactory(),
LoadQueryInfluencers.NONE,
plan
);
final String sql = queryBuilder.generateSql( 1 );
final ResultSetProcessorImpl resultSetProcessor = new ResultSetProcessorImpl( plan );
final List results = new ArrayList();
final Session workSession = openSession();
workSession.beginTransaction();
workSession.doWork(
new Work() {
@Override
public void execute(Connection connection) throws SQLException {
PreparedStatement ps = connection.prepareStatement( sql );
ps.setInt( 1, 0 );
ResultSet resultSet = ps.executeQuery();
results.addAll(
resultSetProcessor.extractResults(
resultSet,
(SessionImplementor) workSession,
new QueryParameters(),
new NamedParameterContext() {
@Override
public int[] getNamedParameterLocations(String name) {
return new int[0];
}
},
true,
false,
null,
null
)
);
resultSet.close();
ps.close();
}
}
);
assertEquals( 1, results.size() );
Object result = results.get( 0 );
assertNotNull( result );
ReportedMessage workReportedMessage = ExtraAssertions.assertTyping( ReportedMessage.class, result );
assertEquals( 0, workReportedMessage.id.intValue() );
assertEquals( "inappropriate", workReportedMessage.reason );
Message workMessage = workReportedMessage.message;
assertNotNull( workMessage );
assertTrue( Hibernate.isInitialized( workMessage ) );
assertEquals( 1, workMessage.mid.intValue() );
assertEquals( "the message", workMessage.msgTxt );
assertTrue( Hibernate.isInitialized( workMessage.poster ) );
Poster workPoster = workMessage.poster;
assertEquals( 2, workPoster.pid.intValue() );
assertEquals( "the poster", workPoster.name );
assertFalse( Hibernate.isInitialized( workPoster.messages ) );
workSession.getTransaction().commit();
workSession.close();
}
// clean up test data
session = openSession();
session.beginTransaction();
session.createQuery( "delete ReportedMessage" ).executeUpdate();
session.createQuery( "delete Message" ).executeUpdate();
session.createQuery( "delete Poster" ).executeUpdate();
session.getTransaction().commit();
session.close();
}
@Entity( name = "ReportedMessage" )
public static class ReportedMessage {
@Id
private Integer id;
private String reason;
@ManyToOne
@JoinColumn
private Message message;
public ReportedMessage() {}
public ReportedMessage(Integer id, String reason, Message message) {
this.id = id;
this.reason = reason;
this.message = message;
}
}
@Entity( name = "Message" )
public static class Message {
@Id
private Integer mid;
private String msgTxt;
@ManyToOne( cascade = CascadeType.MERGE )
@JoinColumn
private Poster poster;
public Message() {}
public Message(Integer mid, String msgTxt) {
this.mid = mid;
this.msgTxt = msgTxt;
}
}
@Entity( name = "Poster" )
public static class Poster {
@Id
private Integer pid;
private String name;
@OneToMany(mappedBy = "poster")
private List<Message> messages = new ArrayList<Message>();
public Poster() {}
public Poster(Integer pid, String name) {
this.pid = pid;
this.name = name;
}
}
}

View File

@ -31,26 +31,20 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.hibernate.LockMode;
import org.hibernate.Session;
import org.hibernate.engine.jdbc.spi.StatementPreparer;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.jdbc.Work;
import org.hibernate.loader.entity.BatchingEntityLoaderBuilder;
import org.hibernate.loader.entity.EntityLoader;
import org.hibernate.loader.internal.EntityLoadQueryBuilderImpl;
import org.hibernate.loader.internal.ResultSetProcessorImpl;
import org.hibernate.loader.plan.internal.EntityLoadQueryImpl;
import org.hibernate.loader.plan.internal.SingleRootReturnLoadPlanBuilderStrategy;
import org.hibernate.loader.plan.spi.LoadPlan;
import org.hibernate.loader.plan.spi.LoadPlanBuilder;
import org.hibernate.loader.spi.NamedParameterContext;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.OuterJoinLoadable;
import org.junit.Test;
@ -82,14 +76,6 @@ public class SimpleResultSetProcessorTest extends BaseCoreFunctionalTestCase {
session.close();
{
final EntityLoadQueryImpl queryBuilder = new EntityLoadQueryImpl(
sessionFactory(),
LoadQueryInfluencers.NONE,
LockMode.NONE,
(OuterJoinLoadable) entityPersister
);
final String sql = queryBuilder.generateSql( 1 );
final SingleRootReturnLoadPlanBuilderStrategy strategy = new SingleRootReturnLoadPlanBuilderStrategy(
sessionFactory(),
LoadQueryInfluencers.NONE,
@ -97,6 +83,13 @@ public class SimpleResultSetProcessorTest extends BaseCoreFunctionalTestCase {
0
);
final LoadPlan plan = LoadPlanBuilder.buildRootEntityLoadPlan( strategy, entityPersister );
final EntityLoadQueryBuilderImpl queryBuilder = new EntityLoadQueryBuilderImpl(
sessionFactory(),
LoadQueryInfluencers.NONE,
plan
);
final String sql = queryBuilder.generateSql( 1 );
final ResultSetProcessorImpl resultSetProcessor = new ResultSetProcessorImpl( plan );
final List results = new ArrayList();

View File

@ -33,8 +33,10 @@ import java.util.List;
import org.hibernate.engine.spi.CascadingActions;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.loader.internal.EntityLoadQueryBuilderImpl;
import org.hibernate.loader.plan.internal.CascadeLoadPlanBuilderStrategy;
import org.hibernate.loader.plan.internal.SingleRootReturnLoadPlanBuilderStrategy;
import org.hibernate.loader.spi.LoadQueryBuilder;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
@ -78,9 +80,8 @@ public class LoadPlanBuilderTest extends BaseCoreFunctionalTestCase {
assertNotNull( entityFetch.getFetches() );
assertEquals( 0, entityFetch.getFetches().length );
String loadSql = plan.getLoadQuery().generateSql( 1 );
// TODO: assert that aliases used in loadSql match up with those in entityReturn and entityFetch
// (they currently do not match up)
LoadQueryBuilder loadQueryBuilder = new EntityLoadQueryBuilderImpl( sessionFactory(), LoadQueryInfluencers.NONE, plan );
String sql = loadQueryBuilder.generateSql( 1 );
}
@Test