HHH-7841 : Redesign Loader
This commit is contained in:
parent
3f8699c913
commit
b3791bc3c3
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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() + ')';
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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() );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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 )
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() );
|
||||
|
|
|
@ -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,
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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();
|
||||
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");
|
||||
}
|
||||
|
||||
return context.resolveEntityKey( entityKey, this );
|
||||
final Object object = context.resolveEntityKey( entityKey, entityReference );
|
||||
if ( this == entityReference ) {
|
||||
objectForThisEntityReturn = object;
|
||||
}
|
||||
}
|
||||
return objectForThisEntityReturn;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -84,6 +84,7 @@ public interface FetchOwner {
|
|||
public EntityFetch buildEntityFetch(
|
||||
AssociationAttributeDefinition attributeDefinition,
|
||||
FetchStrategy fetchStrategy,
|
||||
String sqlTableAlias,
|
||||
LoadPlanBuildingContext loadPlanBuildingContext);
|
||||
|
||||
public CompositeFetch buildCompositeFetch(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue