HHH-3414 : fetch profiles, phase 1 : join fetching
git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@15091 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
parent
8f3ce7a6e7
commit
fbae6db0ab
|
@ -813,4 +813,40 @@ public interface Session extends Serializable {
|
|||
* @see #disconnect()
|
||||
*/
|
||||
void reconnect(Connection connection) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Is a particular fetch profile enabled on this session?
|
||||
*
|
||||
* @param name The name of the profile to be checked.
|
||||
* @return True if fetch profile is enabled; false if not.
|
||||
* @throws UnknownProfileException Indicates that the given name does not
|
||||
* match any known profile names
|
||||
*
|
||||
* @see org.hibernate.engine.profile.FetchProfile for discussion of this feature
|
||||
*/
|
||||
public boolean isFetchProfileEnabled(String name) throws UnknownProfileException;
|
||||
|
||||
/**
|
||||
* Enable a particular fetch profile on this session. No-op if requested
|
||||
* profile is already enabled.
|
||||
*
|
||||
* @param name The name of the fetch profile to be enabled.
|
||||
* @throws UnknownProfileException Indicates that the given name does not
|
||||
* match any known profile names
|
||||
*
|
||||
* @see org.hibernate.engine.profile.FetchProfile for discussion of this feature
|
||||
*/
|
||||
public void enableFetchProfile(String name) throws UnknownProfileException;
|
||||
|
||||
/**
|
||||
* Disable a particular fetch profile on this session. No-op if requested
|
||||
* profile is already disabled.
|
||||
*
|
||||
* @param name The name of the fetch profile to be disabled.
|
||||
* @throws UnknownProfileException Indicates that the given name does not
|
||||
* match any known profile names
|
||||
*
|
||||
* @see org.hibernate.engine.profile.FetchProfile for discussion of this feature
|
||||
*/
|
||||
public void disableFetchProfile(String name) throws UnknownProfileException;
|
||||
}
|
||||
|
|
|
@ -244,4 +244,13 @@ public interface SessionFactory extends Referenceable, Serializable {
|
|||
* @throws HibernateException If no filter defined with the given name.
|
||||
*/
|
||||
public FilterDefinition getFilterDefinition(String filterName) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Determine if this session factory contains a fetch profile definition
|
||||
* registered under the given name.
|
||||
*
|
||||
* @param name The name to check
|
||||
* @return True if there is such a fetch profile; false otherwise.
|
||||
*/
|
||||
public boolean containsFetchProfileDefition(String name);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Used to indicate a request against an unknown profile name.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class UnknownProfileException extends HibernateException {
|
||||
private final String name;
|
||||
|
||||
public UnknownProfileException(String name) {
|
||||
super( "Unknow fetch profile [" + name + "]" );
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* The unknown fetch profile name.
|
||||
*
|
||||
* @return The unknown fetch profile name.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
|
@ -114,6 +114,7 @@ import org.hibernate.mapping.RootClass;
|
|||
import org.hibernate.mapping.SimpleValue;
|
||||
import org.hibernate.mapping.Table;
|
||||
import org.hibernate.mapping.UniqueKey;
|
||||
import org.hibernate.mapping.FetchProfile;
|
||||
import org.hibernate.proxy.EntityNotFoundDelegate;
|
||||
import org.hibernate.secure.JACCConfiguration;
|
||||
import org.hibernate.tool.hbm2ddl.DatabaseMetadata;
|
||||
|
@ -163,6 +164,7 @@ public class Configuration implements Serializable {
|
|||
*/
|
||||
protected Map sqlResultSetMappings;
|
||||
protected Map filterDefinitions;
|
||||
protected Map fetchProfiles;
|
||||
protected List secondPasses;
|
||||
protected List propertyReferences;
|
||||
// protected List extendsQueue;
|
||||
|
@ -202,6 +204,7 @@ public class Configuration implements Serializable {
|
|||
entityResolver = XMLHelper.DEFAULT_DTD_RESOLVER;
|
||||
eventListeners = new EventListeners();
|
||||
filterDefinitions = new HashMap();
|
||||
fetchProfiles = new HashMap();
|
||||
// extendsQueue = new ArrayList();
|
||||
extendsQueue = new HashMap();
|
||||
auxiliaryDatabaseObjects = new ArrayList();
|
||||
|
@ -720,6 +723,7 @@ public class Configuration implements Serializable {
|
|||
namingStrategy,
|
||||
typeDefs,
|
||||
filterDefinitions,
|
||||
fetchProfiles,
|
||||
extendsQueue,
|
||||
auxiliaryDatabaseObjects,
|
||||
tableNameBinding,
|
||||
|
@ -2181,6 +2185,14 @@ public class Configuration implements Serializable {
|
|||
filterDefinitions.put( definition.getFilterName(), definition );
|
||||
}
|
||||
|
||||
public Map getFetchProfiles() {
|
||||
return fetchProfiles;
|
||||
}
|
||||
|
||||
public void addFetchProfile(FetchProfile fetchProfile) {
|
||||
fetchProfiles.put( fetchProfile.getName(), fetchProfile );
|
||||
}
|
||||
|
||||
public void addAuxiliaryDatabaseObject(AuxiliaryDatabaseObject object) {
|
||||
auxiliaryDatabaseObjects.add( object );
|
||||
}
|
||||
|
|
|
@ -89,6 +89,7 @@ import org.hibernate.mapping.TypeDef;
|
|||
import org.hibernate.mapping.UnionSubclass;
|
||||
import org.hibernate.mapping.UniqueKey;
|
||||
import org.hibernate.mapping.Value;
|
||||
import org.hibernate.mapping.FetchProfile;
|
||||
import org.hibernate.persister.entity.JoinedSubclassEntityPersister;
|
||||
import org.hibernate.persister.entity.SingleTableEntityPersister;
|
||||
import org.hibernate.persister.entity.UnionSubclassEntityPersister;
|
||||
|
@ -158,6 +159,9 @@ public final class HbmBinder {
|
|||
if ( "filter-def".equals( elementName ) ) {
|
||||
parseFilterDef( element, mappings );
|
||||
}
|
||||
else if ( "fetch-profile".equals( elementName ) ) {
|
||||
parseFetchProfile( element, mappings, null );
|
||||
}
|
||||
else if ( "typedef".equals( elementName ) ) {
|
||||
bindTypeDef( element, mappings );
|
||||
}
|
||||
|
@ -546,8 +550,13 @@ public final class HbmBinder {
|
|||
bindDom4jRepresentation( node, persistentClass, mappings, inheritedMetas );
|
||||
bindMapRepresentation( node, persistentClass, mappings, inheritedMetas );
|
||||
|
||||
bindPersistentClassCommonValues( node, persistentClass, mappings, inheritedMetas );
|
||||
Iterator itr = node.elementIterator( "fetch-profile" );
|
||||
while ( itr.hasNext() ) {
|
||||
final Element profileElement = ( Element ) itr.next();
|
||||
parseFetchProfile( profileElement, mappings, entityName );
|
||||
}
|
||||
|
||||
bindPersistentClassCommonValues( node, persistentClass, mappings, inheritedMetas );
|
||||
}
|
||||
|
||||
private static void bindPojoRepresentation(Element node, PersistentClass entity,
|
||||
|
@ -2963,6 +2972,25 @@ public final class HbmBinder {
|
|||
filterable.addFilter( name, condition );
|
||||
}
|
||||
|
||||
private static void parseFetchProfile(Element element, Mappings mappings, String containingEntityName) {
|
||||
String profileName = element.attributeValue( "name" );
|
||||
FetchProfile profile = mappings.findOrCreateFetchProfile( profileName );
|
||||
Iterator itr = element.elementIterator( "fetch" );
|
||||
while ( itr.hasNext() ) {
|
||||
final Element fetchElement = ( Element ) itr.next();
|
||||
final String association = fetchElement.attributeValue( "association" );
|
||||
final String style = fetchElement.attributeValue( "style" );
|
||||
String entityName = fetchElement.attributeValue( "entity" );
|
||||
if ( entityName == null ) {
|
||||
entityName = containingEntityName;
|
||||
}
|
||||
if ( entityName == null ) {
|
||||
throw new MappingException( "could not determine entity for fetch-profile fetch [" + profileName + "]:[" + association + "]" );
|
||||
}
|
||||
profile.addFetch( entityName, association, style );
|
||||
}
|
||||
}
|
||||
|
||||
private static String getSubselect(Element element) {
|
||||
String subselect = element.attributeValue( "subselect" );
|
||||
if ( subselect != null ) {
|
||||
|
|
|
@ -46,6 +46,7 @@ import org.hibernate.mapping.Table;
|
|||
import org.hibernate.mapping.TypeDef;
|
||||
import org.hibernate.mapping.AuxiliaryDatabaseObject;
|
||||
import org.hibernate.mapping.Column;
|
||||
import org.hibernate.mapping.FetchProfile;
|
||||
import org.hibernate.util.StringHelper;
|
||||
|
||||
/**
|
||||
|
@ -77,6 +78,7 @@ public class Mappings implements Serializable {
|
|||
protected final List propertyReferences;
|
||||
protected final NamingStrategy namingStrategy;
|
||||
protected final Map filterDefinitions;
|
||||
protected final Map fetchProfiles;
|
||||
protected final List auxiliaryDatabaseObjects;
|
||||
|
||||
protected final Map extendsQueue;
|
||||
|
@ -111,12 +113,12 @@ public class Mappings implements Serializable {
|
|||
final NamingStrategy namingStrategy,
|
||||
final Map typeDefs,
|
||||
final Map filterDefinitions,
|
||||
final Map fetchProfiles,
|
||||
// final List extendsQueue,
|
||||
final Map extendsQueue,
|
||||
final List auxiliaryDatabaseObjects,
|
||||
final Map tableNamebinding,
|
||||
final Map columnNameBindingPerTable
|
||||
) {
|
||||
final Map columnNameBindingPerTable) {
|
||||
this.classes = classes;
|
||||
this.collections = collections;
|
||||
this.queries = queries;
|
||||
|
@ -129,6 +131,7 @@ public class Mappings implements Serializable {
|
|||
this.namingStrategy = namingStrategy;
|
||||
this.typeDefs = typeDefs;
|
||||
this.filterDefinitions = filterDefinitions;
|
||||
this.fetchProfiles = fetchProfiles;
|
||||
this.extendsQueue = extendsQueue;
|
||||
this.auxiliaryDatabaseObjects = auxiliaryDatabaseObjects;
|
||||
this.tableNameBinding = tableNamebinding;
|
||||
|
@ -418,7 +421,20 @@ public class Mappings implements Serializable {
|
|||
public FilterDefinition getFilterDefinition(String name) {
|
||||
return (FilterDefinition) filterDefinitions.get(name);
|
||||
}
|
||||
|
||||
|
||||
public Map getFetchProfiles() {
|
||||
return fetchProfiles;
|
||||
}
|
||||
|
||||
public FetchProfile findOrCreateFetchProfile(String name) {
|
||||
FetchProfile profile = ( FetchProfile ) fetchProfiles.get( name );
|
||||
if ( profile == null ) {
|
||||
profile = new FetchProfile( name );
|
||||
fetchProfiles.put( name, profile );
|
||||
}
|
||||
return profile;
|
||||
}
|
||||
|
||||
public boolean isDefaultLazy() {
|
||||
return defaultLazy;
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.hibernate.HibernateException;
|
|||
import org.hibernate.engine.QueryParameters;
|
||||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.TypedValue;
|
||||
import org.hibernate.engine.LoadQueryInfluencers;
|
||||
import org.hibernate.impl.CriteriaImpl;
|
||||
import org.hibernate.loader.criteria.CriteriaJoinWalker;
|
||||
import org.hibernate.loader.criteria.CriteriaQueryTranslator;
|
||||
|
@ -76,8 +77,9 @@ public abstract class SubqueryExpression implements Criterion {
|
|||
factory,
|
||||
criteriaImpl,
|
||||
criteriaImpl.getEntityOrClassName(),
|
||||
new HashMap(),
|
||||
innerQuery.getRootSQLALias());
|
||||
LoadQueryInfluencers.NONE,
|
||||
innerQuery.getRootSQLALias()
|
||||
);
|
||||
|
||||
String sql = walker.getSQLString();
|
||||
|
||||
|
|
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
* 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.engine;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Iterator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.hibernate.Filter;
|
||||
import org.hibernate.UnknownProfileException;
|
||||
import org.hibernate.type.Type;
|
||||
import org.hibernate.impl.FilterImpl;
|
||||
|
||||
/**
|
||||
* Centralize all options which can influence the SQL query needed to load and
|
||||
* entity. Currently such influencers are defined as:<ul>
|
||||
* <li>filters</li>
|
||||
* <li>fetch profiles</li>
|
||||
* <li>internal fetch profile (merge profile, etc)</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class LoadQueryInfluencers {
|
||||
/**
|
||||
* Static reference useful for cases where we are creating load SQL
|
||||
* outside the context of any influencers. One such example is
|
||||
* anything created by the session factory.
|
||||
*/
|
||||
public static LoadQueryInfluencers NONE = new LoadQueryInfluencers();
|
||||
|
||||
private final SessionFactoryImplementor sessionFactory;
|
||||
private String internalFetchProfile;
|
||||
private Map enabledFilters;
|
||||
private Set enabledFetchProfileNames;
|
||||
|
||||
public LoadQueryInfluencers() {
|
||||
this( null, java.util.Collections.EMPTY_MAP, java.util.Collections.EMPTY_SET );
|
||||
}
|
||||
|
||||
public LoadQueryInfluencers(SessionFactoryImplementor sessionFactory) {
|
||||
this( sessionFactory, new HashMap(), new HashSet() );
|
||||
}
|
||||
|
||||
private LoadQueryInfluencers(SessionFactoryImplementor sessionFactory, Map enabledFilters, Set enabledFetchProfileNames) {
|
||||
this.sessionFactory = sessionFactory;
|
||||
this.enabledFilters = enabledFilters;
|
||||
this.enabledFetchProfileNames = enabledFetchProfileNames;
|
||||
}
|
||||
|
||||
public SessionFactoryImplementor getSessionFactory() {
|
||||
return sessionFactory;
|
||||
}
|
||||
|
||||
|
||||
// internal fetch profile support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
public String getInternalFetchProfile() {
|
||||
return internalFetchProfile;
|
||||
}
|
||||
|
||||
public void setInternalFetchProfile(String internalFetchProfile) {
|
||||
if ( sessionFactory == null ) {
|
||||
// thats the signal that this is the immutable, context-less
|
||||
// variety
|
||||
throw new IllegalStateException( "Cannot modify context-less LoadQueryInfluencers" );
|
||||
}
|
||||
this.internalFetchProfile = internalFetchProfile;
|
||||
}
|
||||
|
||||
|
||||
// filter support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
public boolean hasEnabledFilters() {
|
||||
return enabledFilters != null && !enabledFilters.isEmpty();
|
||||
}
|
||||
|
||||
public Map getEnabledFilters() {
|
||||
// First, validate all the enabled filters...
|
||||
//TODO: this implementation has bad performance
|
||||
Iterator itr = enabledFilters.values().iterator();
|
||||
while ( itr.hasNext() ) {
|
||||
final Filter filter = ( Filter ) itr.next();
|
||||
filter.validate();
|
||||
}
|
||||
return enabledFilters;
|
||||
}
|
||||
|
||||
public Filter getEnabledFilter(String filterName) {
|
||||
return ( Filter ) enabledFilters.get( filterName );
|
||||
}
|
||||
|
||||
public Filter enableFilter(String filterName) {
|
||||
FilterImpl filter = new FilterImpl( sessionFactory.getFilterDefinition( filterName ) );
|
||||
enabledFilters.put( filterName, filter );
|
||||
return filter;
|
||||
}
|
||||
|
||||
public void disableFilter(String filterName) {
|
||||
enabledFilters.remove( filterName );
|
||||
}
|
||||
|
||||
public Object getFilterParameterValue(String filterParameterName) {
|
||||
String[] parsed = parseFilterParameterName( filterParameterName );
|
||||
FilterImpl filter = ( FilterImpl ) enabledFilters.get( parsed[0] );
|
||||
if ( filter == null ) {
|
||||
throw new IllegalArgumentException( "Filter [" + parsed[0] + "] currently not enabled" );
|
||||
}
|
||||
return filter.getParameter( parsed[1] );
|
||||
}
|
||||
|
||||
public Type getFilterParameterType(String filterParameterName) {
|
||||
String[] parsed = parseFilterParameterName( filterParameterName );
|
||||
FilterDefinition filterDef = sessionFactory.getFilterDefinition( parsed[0] );
|
||||
if ( filterDef == null ) {
|
||||
throw new IllegalArgumentException( "Filter [" + parsed[0] + "] not defined" );
|
||||
}
|
||||
Type type = filterDef.getParameterType( parsed[1] );
|
||||
if ( type == null ) {
|
||||
// this is an internal error of some sort...
|
||||
throw new InternalError( "Unable to locate type for filter parameter" );
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
public static String[] parseFilterParameterName(String filterParameterName) {
|
||||
int dot = filterParameterName.indexOf( '.' );
|
||||
if ( dot <= 0 ) {
|
||||
throw new IllegalArgumentException( "Invalid filter-parameter name format" );
|
||||
}
|
||||
String filterName = filterParameterName.substring( 0, dot );
|
||||
String parameterName = filterParameterName.substring( dot + 1 );
|
||||
return new String[] { filterName, parameterName };
|
||||
}
|
||||
|
||||
|
||||
// fetch profile support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
public boolean hasEnabledFetchProfiles() {
|
||||
return enabledFetchProfileNames != null && !enabledFetchProfileNames.isEmpty();
|
||||
}
|
||||
|
||||
public Set getEnabledFetchProfileNames() {
|
||||
return enabledFetchProfileNames;
|
||||
}
|
||||
|
||||
private void checkFetchProfileName(String name) {
|
||||
if ( !sessionFactory.containsFetchProfileDefition( name ) ) {
|
||||
throw new UnknownProfileException( name );
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isFetchProfileEnabled(String name) throws UnknownProfileException {
|
||||
checkFetchProfileName( name );
|
||||
return enabledFetchProfileNames.contains( name );
|
||||
}
|
||||
|
||||
public void enableFetchProfile(String name) throws UnknownProfileException {
|
||||
checkFetchProfileName( name );
|
||||
enabledFetchProfileNames.add( name );
|
||||
}
|
||||
|
||||
public void disableFetchProfile(String name) throws UnknownProfileException {
|
||||
checkFetchProfileName( name );
|
||||
enabledFetchProfileNames.remove( name );
|
||||
}
|
||||
|
||||
}
|
|
@ -37,6 +37,7 @@ import org.hibernate.SessionFactory;
|
|||
import org.hibernate.ConnectionReleaseMode;
|
||||
import org.hibernate.proxy.EntityNotFoundDelegate;
|
||||
import org.hibernate.engine.query.QueryPlanCache;
|
||||
import org.hibernate.engine.profile.FetchProfile;
|
||||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.cache.QueryCache;
|
||||
|
@ -219,5 +220,13 @@ public interface SessionFactoryImplementor extends Mapping, SessionFactory {
|
|||
public EntityNotFoundDelegate getEntityNotFoundDelegate();
|
||||
|
||||
public SQLFunctionRegistry getSqlFunctionRegistry();
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve fetch profile by name.
|
||||
*
|
||||
* @param name The name of the profile to retrieve.
|
||||
* @return The profile definition
|
||||
*/
|
||||
public FetchProfile getFetchProfile(String name);
|
||||
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import java.sql.Connection;
|
|||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.CacheMode;
|
||||
import org.hibernate.EntityMode;
|
||||
|
@ -237,6 +238,7 @@ public interface SessionImplementor extends Serializable {
|
|||
* @param filterParameterName The filter parameter name in the format
|
||||
* {FILTER_NAME.PARAMETER_NAME}.
|
||||
* @return The filter parameter value.
|
||||
* @deprecated use #getLoadQueryInfluencers instead
|
||||
*/
|
||||
public Object getFilterParameterValue(String filterParameterName);
|
||||
|
||||
|
@ -245,6 +247,8 @@ public interface SessionImplementor extends Serializable {
|
|||
*
|
||||
* @param filterParameterName The filter parameter name in the format
|
||||
* {FILTER_NAME.PARAMETER_NAME}.
|
||||
* @return The filter param type
|
||||
* @deprecated use #getLoadQueryInfluencers instead
|
||||
*/
|
||||
public Type getFilterParameterType(String filterParameterName);
|
||||
|
||||
|
@ -253,6 +257,7 @@ public interface SessionImplementor extends Serializable {
|
|||
* name, with values corresponding to the {@link org.hibernate.impl.FilterImpl}
|
||||
* instance.
|
||||
* @return The currently enabled filters.
|
||||
* @deprecated use #getLoadQueryInfluencers instead
|
||||
*/
|
||||
public Map getEnabledFilters();
|
||||
|
||||
|
@ -307,10 +312,22 @@ public interface SessionImplementor extends Serializable {
|
|||
|
||||
public void afterScrollOperation();
|
||||
|
||||
public void setFetchProfile(String name);
|
||||
|
||||
/**
|
||||
* Get the <i>internal</i> fetch profile currently associated with this session.
|
||||
*
|
||||
* @return The current internal fetch profile, or null if none currently associated.
|
||||
* @deprecated use #getLoadQueryInfluencers instead
|
||||
*/
|
||||
public String getFetchProfile();
|
||||
|
||||
/**
|
||||
* Set the current <i>internal</i> fetch profile for this session.
|
||||
*
|
||||
* @param name The internal fetch profile name to use
|
||||
* @deprecated use #getLoadQueryInfluencers instead
|
||||
*/
|
||||
public void setFetchProfile(String name);
|
||||
|
||||
public JDBCContext getJDBCContext();
|
||||
|
||||
/**
|
||||
|
@ -322,4 +339,12 @@ public interface SessionImplementor extends Serializable {
|
|||
* @return True if the session is closed; false otherwise.
|
||||
*/
|
||||
public boolean isClosed();
|
||||
|
||||
/**
|
||||
* Get the load query influencers associated with this session.
|
||||
*
|
||||
* @return the load query influencers associated with this session;
|
||||
* should never be null.
|
||||
*/
|
||||
public LoadQueryInfluencers getLoadQueryInfluencers();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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.engine.profile;
|
||||
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
/**
|
||||
* Models the association of a given fetch.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class Association {
|
||||
private final EntityPersister owner;
|
||||
private final String associationPath;
|
||||
private final String role;
|
||||
|
||||
public Association(EntityPersister owner, String associationPath) {
|
||||
this.owner = owner;
|
||||
this.associationPath = associationPath;
|
||||
this.role = owner.getEntityName() + '.' + associationPath;
|
||||
}
|
||||
|
||||
public EntityPersister getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public String getAssociationPath() {
|
||||
return associationPath;
|
||||
}
|
||||
|
||||
public String getRole() {
|
||||
return role;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* 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.engine.profile;
|
||||
|
||||
/**
|
||||
* Models an individual fetch within a profile.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class Fetch {
|
||||
private final Association association;
|
||||
private final Style style;
|
||||
|
||||
public Fetch(Association association, Style style) {
|
||||
this.association = association;
|
||||
this.style = style;
|
||||
}
|
||||
|
||||
public Association getAssociation() {
|
||||
return association;
|
||||
}
|
||||
|
||||
public Style getStyle() {
|
||||
return style;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type or style of fetch. For the moment we limit this to
|
||||
* join and select, though technically subselect would be valid
|
||||
* here as as well; however, to support subselect here would
|
||||
* require major changes to the subselect loading code (which is
|
||||
* needed for other things as well anyway).
|
||||
*/
|
||||
public static class Style {
|
||||
public static final Style JOIN = new Style( "join" );
|
||||
public static final Style SELECT = new Style( "select" );
|
||||
|
||||
private final String name;
|
||||
|
||||
private Style(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public static Style parse(String name) {
|
||||
if ( SELECT.name.equals( name ) ) {
|
||||
return SELECT;
|
||||
}
|
||||
else {
|
||||
// the default...
|
||||
return JOIN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Fetch[" + style + "{" + association.getRole() + "}]";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* 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.engine.profile;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
import org.hibernate.type.Type;
|
||||
import org.hibernate.type.BagType;
|
||||
import org.hibernate.type.AssociationType;
|
||||
|
||||
/**
|
||||
* A 'fetch profile' allows a user to dynamically modify the fetching
|
||||
* strategy used for particular associations at runtime, whereas that
|
||||
* information was historically only statically defined in the metadata.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class FetchProfile {
|
||||
private static final Logger log = LoggerFactory.getLogger( FetchProfile.class );
|
||||
|
||||
private final String name;
|
||||
private Map fetches = new HashMap();
|
||||
|
||||
private boolean containsJoinFetchedCollection = false;
|
||||
private boolean containsJoinFetchedBag = false;
|
||||
private Fetch bagJoinFetch;
|
||||
|
||||
/**
|
||||
* A 'fetch profile' is uniquely named within a
|
||||
* {@link SessionFactoryImplementor SessionFactory}, thus it is also
|
||||
* uniquely and easily identifiable within that
|
||||
* {@link SessionFactoryImplementor SessionFactory}.
|
||||
*
|
||||
* @param name The name under which we are bound in the sessionFactory
|
||||
*/
|
||||
public FetchProfile(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a fetch to the profile.
|
||||
*
|
||||
* @param association The association to be fetched
|
||||
* @param fetchStyleName The name of the fetch style to apply
|
||||
*/
|
||||
public void addFetch(Association association, String fetchStyleName) {
|
||||
addFetch( association, Fetch.Style.parse( fetchStyleName ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a fetch to the profile.
|
||||
*
|
||||
* @param association The association to be fetched
|
||||
* @param style The style to apply
|
||||
*/
|
||||
public void addFetch(Association association, Fetch.Style style) {
|
||||
addFetch( new Fetch( association, style ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a fetch to the profile.
|
||||
*
|
||||
* @param fetch The fetch to add.
|
||||
*/
|
||||
public void addFetch(Fetch fetch) {
|
||||
Type associationType = fetch.getAssociation().getOwner().getPropertyType( fetch.getAssociation().getAssociationPath() );
|
||||
if ( associationType.isCollectionType() ) {
|
||||
log.trace( "handling request to add collection fetch [{}]", fetch.getAssociation().getRole() );
|
||||
|
||||
// couple of things for whcih to account in the case of collection
|
||||
// join fetches
|
||||
if ( Fetch.Style.JOIN == fetch.getStyle() ) {
|
||||
// first, if this is a bag we need to ignore it if we previously
|
||||
// processed collection join fetches
|
||||
if ( BagType.class.isInstance( associationType ) ) {
|
||||
if ( containsJoinFetchedCollection ) {
|
||||
log.warn( "Ignoring bag join fetch [{}] due to prior collection join fetch", fetch.getAssociation().getRole() );
|
||||
return; // EARLY EXIT!!!
|
||||
}
|
||||
}
|
||||
|
||||
// also, in cases where we are asked to add a collection join
|
||||
// fetch where we had already added a bag join fetch previously,
|
||||
// we need to go back and ignore that previous bag join fetch.
|
||||
if ( containsJoinFetchedBag ) {
|
||||
if ( fetches.remove( bagJoinFetch.getAssociation().getRole() ) != bagJoinFetch ) {
|
||||
// just for safety...
|
||||
log.warn( "Unable to erase previously added bag join fetch" );
|
||||
}
|
||||
bagJoinFetch = null;
|
||||
containsJoinFetchedBag = false;
|
||||
}
|
||||
|
||||
containsJoinFetchedCollection = true;
|
||||
}
|
||||
}
|
||||
fetches.put( fetch.getAssociation().getRole(), fetch );
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for property 'name'.
|
||||
*
|
||||
* @return Value for property 'name'.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for property 'fetches'. Map of {@link Fetch} instances,
|
||||
* keyed by associaion <tt>role</tt>
|
||||
*
|
||||
* @return Value for property 'fetches'.
|
||||
*/
|
||||
public Map getFetches() {
|
||||
return fetches;
|
||||
}
|
||||
|
||||
public Fetch getFetchByRole(String role) {
|
||||
return ( Fetch ) fetches.get( role );
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for property 'containsJoinFetchedCollection', which flags whether
|
||||
* this fetch profile contained any collection join fetches.
|
||||
*
|
||||
* @return Value for property 'containsJoinFetchedCollection'.
|
||||
*/
|
||||
public boolean isContainsJoinFetchedCollection() {
|
||||
return containsJoinFetchedCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for property 'containsJoinFetchedBag', which flags whether this
|
||||
* fetch profile contained any bag join fetches
|
||||
*
|
||||
* @return Value for property 'containsJoinFetchedBag'.
|
||||
*/
|
||||
public boolean isContainsJoinFetchedBag() {
|
||||
return containsJoinFetchedBag;
|
||||
}
|
||||
}
|
|
@ -84,6 +84,9 @@ import org.hibernate.engine.NamedQueryDefinition;
|
|||
import org.hibernate.engine.NamedSQLQueryDefinition;
|
||||
import org.hibernate.engine.ResultSetMappingDefinition;
|
||||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.profile.FetchProfile;
|
||||
import org.hibernate.engine.profile.Fetch;
|
||||
import org.hibernate.engine.profile.Association;
|
||||
import org.hibernate.engine.query.QueryPlanCache;
|
||||
import org.hibernate.engine.query.sql.NativeSQLQuerySpecification;
|
||||
import org.hibernate.event.EventListeners;
|
||||
|
@ -100,6 +103,7 @@ import org.hibernate.persister.PersisterFactory;
|
|||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.persister.entity.Queryable;
|
||||
import org.hibernate.persister.entity.Loadable;
|
||||
import org.hibernate.pretty.MessageHelper;
|
||||
import org.hibernate.proxy.EntityNotFoundDelegate;
|
||||
import org.hibernate.stat.Statistics;
|
||||
|
@ -156,6 +160,7 @@ public final class SessionFactoryImpl implements SessionFactory, SessionFactoryI
|
|||
private final transient Map namedSqlQueries;
|
||||
private final transient Map sqlResultSetMappings;
|
||||
private final transient Map filters;
|
||||
private final transient Map fetchProfiles;
|
||||
private final transient Map imports;
|
||||
private final transient Interceptor interceptor;
|
||||
private final transient Settings settings;
|
||||
|
@ -198,6 +203,7 @@ public final class SessionFactoryImpl implements SessionFactory, SessionFactoryI
|
|||
public void sessionFactoryClosed(SessionFactory factory) {
|
||||
}
|
||||
};
|
||||
|
||||
this.filters = new HashMap();
|
||||
this.filters.putAll( cfg.getFilterDefinitions() );
|
||||
|
||||
|
@ -412,6 +418,43 @@ public final class SessionFactoryImpl implements SessionFactory, SessionFactoryI
|
|||
}
|
||||
this.entityNotFoundDelegate = entityNotFoundDelegate;
|
||||
|
||||
// this needs to happen after persisters are all ready to go...
|
||||
this.fetchProfiles = new HashMap();
|
||||
itr = cfg.getFetchProfiles().values().iterator();
|
||||
while ( itr.hasNext() ) {
|
||||
final org.hibernate.mapping.FetchProfile mappingProfile =
|
||||
( org.hibernate.mapping.FetchProfile ) itr.next();
|
||||
final FetchProfile fetchProfile = new FetchProfile( mappingProfile.getName() );
|
||||
Iterator fetches = mappingProfile.getFetches().iterator();
|
||||
while ( fetches.hasNext() ) {
|
||||
final org.hibernate.mapping.FetchProfile.Fetch mappingFetch =
|
||||
( org.hibernate.mapping.FetchProfile.Fetch ) fetches.next();
|
||||
// resolve the persister owning the fetch
|
||||
final String entityName = getImportedClassName( mappingFetch.getEntity() );
|
||||
final EntityPersister owner = ( EntityPersister ) ( entityName == null ? null : entityPersisters.get( entityName ) );
|
||||
if ( owner == null ) {
|
||||
throw new HibernateException(
|
||||
"Unable to resolve entity reference [" + mappingFetch.getEntity()
|
||||
+ "] in fetch profile [" + fetchProfile.getName() + "]"
|
||||
);
|
||||
}
|
||||
|
||||
// validate the specified association fetch
|
||||
Type associationType = owner.getPropertyType( mappingFetch.getAssociation() );
|
||||
if ( associationType == null || !associationType.isAssociationType() ) {
|
||||
throw new HibernateException( "Fetch profile [" + fetchProfile.getName() + "] specified an invalid association" );
|
||||
}
|
||||
|
||||
// resolve the style
|
||||
final Fetch.Style fetchStyle = Fetch.Style.parse( mappingFetch.getStyle() );
|
||||
|
||||
// then construct the fetch instance...
|
||||
fetchProfile.addFetch( new Association( owner, mappingFetch.getAssociation() ), fetchStyle );
|
||||
( ( Loadable ) owner ).registerAffectingFetchProfile( fetchProfile.getName() );
|
||||
}
|
||||
fetchProfiles.put( fetchProfile.getName(), fetchProfile );
|
||||
}
|
||||
|
||||
this.observer.sessionFactoryCreated( this );
|
||||
}
|
||||
|
||||
|
@ -1004,6 +1047,10 @@ public final class SessionFactoryImpl implements SessionFactory, SessionFactoryI
|
|||
return def;
|
||||
}
|
||||
|
||||
public boolean containsFetchProfileDefition(String name) {
|
||||
return fetchProfiles.containsKey( name );
|
||||
}
|
||||
|
||||
public Set getDefinedFilterNames() {
|
||||
return filters.keySet();
|
||||
}
|
||||
|
@ -1061,6 +1108,14 @@ public final class SessionFactoryImpl implements SessionFactory, SessionFactoryI
|
|||
return entityNotFoundDelegate;
|
||||
}
|
||||
|
||||
public SQLFunctionRegistry getSqlFunctionRegistry() {
|
||||
return sqlFunctionRegistry;
|
||||
}
|
||||
|
||||
public FetchProfile getFetchProfile(String name) {
|
||||
return ( FetchProfile ) fetchProfiles.get( name );
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom serialization hook used during Session serialization.
|
||||
*
|
||||
|
@ -1102,8 +1157,4 @@ public final class SessionFactoryImpl implements SessionFactory, SessionFactoryI
|
|||
}
|
||||
return ( SessionFactoryImpl ) result;
|
||||
}
|
||||
|
||||
public SQLFunctionRegistry getSqlFunctionRegistry() {
|
||||
return sqlFunctionRegistry;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,6 +66,7 @@ import org.hibernate.SessionFactory;
|
|||
import org.hibernate.Transaction;
|
||||
import org.hibernate.TransientObjectException;
|
||||
import org.hibernate.UnresolvableObjectException;
|
||||
import org.hibernate.UnknownProfileException;
|
||||
import org.hibernate.collection.PersistentCollection;
|
||||
import org.hibernate.engine.ActionQueue;
|
||||
import org.hibernate.engine.CollectionEntry;
|
||||
|
@ -76,6 +77,7 @@ import org.hibernate.engine.PersistenceContext;
|
|||
import org.hibernate.engine.QueryParameters;
|
||||
import org.hibernate.engine.StatefulPersistenceContext;
|
||||
import org.hibernate.engine.Status;
|
||||
import org.hibernate.engine.LoadQueryInfluencers;
|
||||
import org.hibernate.engine.query.FilterQueryPlan;
|
||||
import org.hibernate.engine.query.HQLQueryPlan;
|
||||
import org.hibernate.engine.query.NativeSQLQueryPlan;
|
||||
|
@ -167,10 +169,8 @@ public final class SessionImpl extends AbstractSessionImpl
|
|||
private transient boolean flushBeforeCompletionEnabled;
|
||||
private transient boolean autoCloseSessionEnabled;
|
||||
private transient ConnectionReleaseMode connectionReleaseMode;
|
||||
|
||||
private transient String fetchProfile;
|
||||
|
||||
private transient Map enabledFilters = new HashMap();
|
||||
private transient LoadQueryInfluencers loadQueryInfluencers;
|
||||
|
||||
private transient Session rootSession;
|
||||
private transient Map childSessionsByEntityMode;
|
||||
|
@ -195,6 +195,8 @@ public final class SessionImpl extends AbstractSessionImpl
|
|||
this.autoCloseSessionEnabled = false;
|
||||
this.connectionReleaseMode = null;
|
||||
|
||||
loadQueryInfluencers = new LoadQueryInfluencers( factory );
|
||||
|
||||
if ( factory.getStatistics().isStatisticsEnabled() ) {
|
||||
factory.getStatisticsImplementor().openSession();
|
||||
}
|
||||
|
@ -239,6 +241,8 @@ public final class SessionImpl extends AbstractSessionImpl
|
|||
this.connectionReleaseMode = connectionReleaseMode;
|
||||
this.jdbcContext = new JDBCContext( this, connection, interceptor );
|
||||
|
||||
loadQueryInfluencers = new LoadQueryInfluencers( factory );
|
||||
|
||||
if ( factory.getStatistics().isStatisticsEnabled() ) {
|
||||
factory.getStatisticsImplementor().openSession();
|
||||
}
|
||||
|
@ -1048,75 +1052,6 @@ public final class SessionImpl extends AbstractSessionImpl
|
|||
flush();
|
||||
}
|
||||
|
||||
public Filter getEnabledFilter(String filterName) {
|
||||
checkTransactionSynchStatus();
|
||||
return (Filter) enabledFilters.get(filterName);
|
||||
}
|
||||
|
||||
public Filter enableFilter(String filterName) {
|
||||
errorIfClosed();
|
||||
checkTransactionSynchStatus();
|
||||
FilterImpl filter = new FilterImpl( factory.getFilterDefinition(filterName) );
|
||||
enabledFilters.put(filterName, filter);
|
||||
return filter;
|
||||
}
|
||||
|
||||
public void disableFilter(String filterName) {
|
||||
errorIfClosed();
|
||||
checkTransactionSynchStatus();
|
||||
enabledFilters.remove(filterName);
|
||||
}
|
||||
|
||||
public Object getFilterParameterValue(String filterParameterName) {
|
||||
errorIfClosed();
|
||||
checkTransactionSynchStatus();
|
||||
String[] parsed = parseFilterParameterName(filterParameterName);
|
||||
FilterImpl filter = (FilterImpl) enabledFilters.get( parsed[0] );
|
||||
if (filter == null) {
|
||||
throw new IllegalArgumentException("Filter [" + parsed[0] + "] currently not enabled");
|
||||
}
|
||||
return filter.getParameter( parsed[1] );
|
||||
}
|
||||
|
||||
public Type getFilterParameterType(String filterParameterName) {
|
||||
errorIfClosed();
|
||||
checkTransactionSynchStatus();
|
||||
String[] parsed = parseFilterParameterName(filterParameterName);
|
||||
FilterDefinition filterDef = factory.getFilterDefinition( parsed[0] );
|
||||
if (filterDef == null) {
|
||||
throw new IllegalArgumentException("Filter [" + parsed[0] + "] not defined");
|
||||
}
|
||||
Type type = filterDef.getParameterType( parsed[1] );
|
||||
if (type == null) {
|
||||
// this is an internal error of some sort...
|
||||
throw new InternalError("Unable to locate type for filter parameter");
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
public Map getEnabledFilters() {
|
||||
errorIfClosed();
|
||||
checkTransactionSynchStatus();
|
||||
// First, validate all the enabled filters...
|
||||
//TODO: this implementation has bad performance
|
||||
Iterator itr = enabledFilters.values().iterator();
|
||||
while ( itr.hasNext() ) {
|
||||
final Filter filter = (Filter) itr.next();
|
||||
filter.validate();
|
||||
}
|
||||
return enabledFilters;
|
||||
}
|
||||
|
||||
private String[] parseFilterParameterName(String filterParameterName) {
|
||||
int dot = filterParameterName.indexOf('.');
|
||||
if (dot <= 0) {
|
||||
throw new IllegalArgumentException("Invalid filter-parameter name format"); // TODO: what type?
|
||||
}
|
||||
String filterName = filterParameterName.substring(0, dot);
|
||||
String parameterName = filterParameterName.substring(dot+1);
|
||||
return new String[] {filterName, parameterName};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve a list of persistent objects using a hibernate query
|
||||
|
@ -1432,7 +1367,7 @@ public final class SessionImpl extends AbstractSessionImpl
|
|||
return listFilter( collection, filter, new QueryParameters( new Type[]{null, type}, new Object[]{null, value} ) );
|
||||
}
|
||||
|
||||
public Collection filter(Object collection, String filter, Object[] values, Type[] types)
|
||||
public Collection filter(Object collection, String filter, Object[] values, Type[] types)
|
||||
throws HibernateException {
|
||||
Object[] vals = new Object[values.length + 1];
|
||||
Type[] typs = new Type[types.length + 1];
|
||||
|
@ -1491,7 +1426,7 @@ public final class SessionImpl extends AbstractSessionImpl
|
|||
return plan;
|
||||
}
|
||||
|
||||
public List listFilter(Object collection, String filter, QueryParameters queryParameters)
|
||||
public List listFilter(Object collection, String filter, QueryParameters queryParameters)
|
||||
throws HibernateException {
|
||||
errorIfClosed();
|
||||
checkTransactionSynchStatus();
|
||||
|
@ -1511,7 +1446,7 @@ public final class SessionImpl extends AbstractSessionImpl
|
|||
return results;
|
||||
}
|
||||
|
||||
public Iterator iterateFilter(Object collection, String filter, QueryParameters queryParameters)
|
||||
public Iterator iterateFilter(Object collection, String filter, QueryParameters queryParameters)
|
||||
throws HibernateException {
|
||||
errorIfClosed();
|
||||
checkTransactionSynchStatus();
|
||||
|
@ -1552,7 +1487,7 @@ public final class SessionImpl extends AbstractSessionImpl
|
|||
factory,
|
||||
criteria,
|
||||
entityName,
|
||||
getEnabledFilters()
|
||||
getLoadQueryInfluencers()
|
||||
);
|
||||
autoFlushIfRequired( loader.getQuerySpaces() );
|
||||
dontFlushFromFind++;
|
||||
|
@ -1579,7 +1514,7 @@ public final class SessionImpl extends AbstractSessionImpl
|
|||
factory,
|
||||
criteria,
|
||||
implementors[i],
|
||||
getEnabledFilters()
|
||||
getLoadQueryInfluencers()
|
||||
);
|
||||
|
||||
spaces.addAll( loaders[i].getQuerySpaces() );
|
||||
|
@ -1680,7 +1615,7 @@ public final class SessionImpl extends AbstractSessionImpl
|
|||
);
|
||||
}
|
||||
|
||||
public ScrollableResults scrollCustomQuery(CustomQuery customQuery, QueryParameters queryParameters)
|
||||
public ScrollableResults scrollCustomQuery(CustomQuery customQuery, QueryParameters queryParameters)
|
||||
throws HibernateException {
|
||||
errorIfClosed();
|
||||
checkTransactionSynchStatus();
|
||||
|
@ -1734,7 +1669,7 @@ public final class SessionImpl extends AbstractSessionImpl
|
|||
return factory;
|
||||
}
|
||||
|
||||
public void initializeCollection(PersistentCollection collection, boolean writing)
|
||||
public void initializeCollection(PersistentCollection collection, boolean writing)
|
||||
throws HibernateException {
|
||||
errorIfClosed();
|
||||
checkTransactionSynchStatus();
|
||||
|
@ -1882,23 +1817,107 @@ public final class SessionImpl extends AbstractSessionImpl
|
|||
// nothing to do in a stateful session
|
||||
}
|
||||
|
||||
public String getFetchProfile() {
|
||||
checkTransactionSynchStatus();
|
||||
return fetchProfile;
|
||||
}
|
||||
|
||||
public JDBCContext getJDBCContext() {
|
||||
errorIfClosed();
|
||||
checkTransactionSynchStatus();
|
||||
return jdbcContext;
|
||||
}
|
||||
|
||||
public LoadQueryInfluencers getLoadQueryInfluencers() {
|
||||
return loadQueryInfluencers;
|
||||
}
|
||||
|
||||
// filter support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public Filter getEnabledFilter(String filterName) {
|
||||
checkTransactionSynchStatus();
|
||||
return loadQueryInfluencers.getEnabledFilter( filterName );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public Filter enableFilter(String filterName) {
|
||||
errorIfClosed();
|
||||
checkTransactionSynchStatus();
|
||||
return loadQueryInfluencers.enableFilter( filterName );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void disableFilter(String filterName) {
|
||||
errorIfClosed();
|
||||
checkTransactionSynchStatus();
|
||||
loadQueryInfluencers.disableFilter( filterName );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public Object getFilterParameterValue(String filterParameterName) {
|
||||
errorIfClosed();
|
||||
checkTransactionSynchStatus();
|
||||
return loadQueryInfluencers.getFilterParameterValue( filterParameterName );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public Type getFilterParameterType(String filterParameterName) {
|
||||
errorIfClosed();
|
||||
checkTransactionSynchStatus();
|
||||
return loadQueryInfluencers.getFilterParameterType( filterParameterName );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public Map getEnabledFilters() {
|
||||
errorIfClosed();
|
||||
checkTransactionSynchStatus();
|
||||
return loadQueryInfluencers.getEnabledFilters();
|
||||
}
|
||||
|
||||
|
||||
// internal fetch profile support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public String getFetchProfile() {
|
||||
checkTransactionSynchStatus();
|
||||
return loadQueryInfluencers.getInternalFetchProfile();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void setFetchProfile(String fetchProfile) {
|
||||
errorIfClosed();
|
||||
checkTransactionSynchStatus();
|
||||
this.fetchProfile = fetchProfile;
|
||||
loadQueryInfluencers.setInternalFetchProfile( fetchProfile );
|
||||
}
|
||||
|
||||
|
||||
// fetch profile support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
public boolean isFetchProfileEnabled(String name) throws UnknownProfileException {
|
||||
return loadQueryInfluencers.isFetchProfileEnabled( name );
|
||||
}
|
||||
|
||||
public void enableFetchProfile(String name) throws UnknownProfileException {
|
||||
loadQueryInfluencers.enableFetchProfile( name );
|
||||
}
|
||||
|
||||
public void disableFetchProfile(String name) throws UnknownProfileException {
|
||||
loadQueryInfluencers.disableFetchProfile( name );
|
||||
}
|
||||
|
||||
|
||||
private void checkTransactionSynchStatus() {
|
||||
if ( jdbcContext != null && !isClosed() ) {
|
||||
jdbcContext.registerSynchronizationIfPossible();
|
||||
|
@ -1923,7 +1942,6 @@ public final class SessionImpl extends AbstractSessionImpl
|
|||
cacheMode = CacheMode.parse( ( String ) ois.readObject() );
|
||||
flushBeforeCompletionEnabled = ois.readBoolean();
|
||||
autoCloseSessionEnabled = ois.readBoolean();
|
||||
fetchProfile = ( String ) ois.readObject();
|
||||
interceptor = ( Interceptor ) ois.readObject();
|
||||
|
||||
factory = SessionFactoryImpl.deserialize( ois );
|
||||
|
@ -1936,12 +1954,13 @@ public final class SessionImpl extends AbstractSessionImpl
|
|||
persistenceContext = StatefulPersistenceContext.deserialize( ois, this );
|
||||
actionQueue = ActionQueue.deserialize( ois, this );
|
||||
|
||||
enabledFilters = ( Map ) ois.readObject();
|
||||
loadQueryInfluencers = ( LoadQueryInfluencers ) ois.readObject();
|
||||
|
||||
childSessionsByEntityMode = ( Map ) ois.readObject();
|
||||
|
||||
Iterator iter = enabledFilters.values().iterator();
|
||||
Iterator iter = loadQueryInfluencers.getEnabledFilters().values().iterator();
|
||||
while ( iter.hasNext() ) {
|
||||
( ( FilterImpl ) iter.next() ).afterDeserialize(factory);
|
||||
( ( FilterImpl ) iter.next() ).afterDeserialize( factory );
|
||||
}
|
||||
|
||||
if ( isRootSession && childSessionsByEntityMode != null ) {
|
||||
|
@ -1975,7 +1994,6 @@ public final class SessionImpl extends AbstractSessionImpl
|
|||
oos.writeObject( cacheMode.toString() );
|
||||
oos.writeBoolean( flushBeforeCompletionEnabled );
|
||||
oos.writeBoolean( autoCloseSessionEnabled );
|
||||
oos.writeObject( fetchProfile );
|
||||
// we need to writeObject() on this since interceptor is user defined
|
||||
oos.writeObject( interceptor );
|
||||
|
||||
|
@ -1989,7 +2007,7 @@ public final class SessionImpl extends AbstractSessionImpl
|
|||
actionQueue.serialize( oos );
|
||||
|
||||
// todo : look at optimizing these...
|
||||
oos.writeObject( enabledFilters );
|
||||
oos.writeObject( loadQueryInfluencers );
|
||||
oos.writeObject( childSessionsByEntityMode );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,6 +57,7 @@ import org.hibernate.engine.PersistenceContext;
|
|||
import org.hibernate.engine.QueryParameters;
|
||||
import org.hibernate.engine.StatefulPersistenceContext;
|
||||
import org.hibernate.engine.Versioning;
|
||||
import org.hibernate.engine.LoadQueryInfluencers;
|
||||
import org.hibernate.engine.query.HQLQueryPlan;
|
||||
import org.hibernate.engine.query.NativeSQLQueryPlan;
|
||||
import org.hibernate.engine.query.sql.NativeSQLQuerySpecification;
|
||||
|
@ -527,12 +528,12 @@ public class StatelessSessionImpl extends AbstractSessionImpl
|
|||
errorIfClosed();
|
||||
String entityName = criteria.getEntityOrClassName();
|
||||
CriteriaLoader loader = new CriteriaLoader(
|
||||
getOuterJoinLoadable(entityName),
|
||||
getOuterJoinLoadable( entityName ),
|
||||
factory,
|
||||
criteria,
|
||||
entityName,
|
||||
getEnabledFilters()
|
||||
);
|
||||
getLoadQueryInfluencers()
|
||||
);
|
||||
return loader.scroll(this, scrollMode);
|
||||
}
|
||||
|
||||
|
@ -548,7 +549,7 @@ public class StatelessSessionImpl extends AbstractSessionImpl
|
|||
factory,
|
||||
criteria,
|
||||
implementors[i],
|
||||
getEnabledFilters()
|
||||
getLoadQueryInfluencers()
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -623,6 +624,10 @@ public class StatelessSessionImpl extends AbstractSessionImpl
|
|||
return jdbcContext;
|
||||
}
|
||||
|
||||
public LoadQueryInfluencers getLoadQueryInfluencers() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setFetchProfile(String name) {}
|
||||
|
||||
public void afterTransactionBegin(Transaction tx) {}
|
||||
|
|
|
@ -26,13 +26,16 @@ package org.hibernate.loader;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.hibernate.FetchMode;
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.engine.CascadeStyle;
|
||||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.LoadQueryInfluencers;
|
||||
import org.hibernate.engine.profile.FetchProfile;
|
||||
import org.hibernate.engine.profile.Fetch;
|
||||
import org.hibernate.persister.entity.Loadable;
|
||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
||||
import org.hibernate.sql.JoinFragment;
|
||||
|
@ -51,55 +54,60 @@ public abstract class AbstractEntityJoinWalker extends JoinWalker {
|
|||
private final OuterJoinLoadable persister;
|
||||
private final String alias;
|
||||
|
||||
public AbstractEntityJoinWalker(OuterJoinLoadable persister, SessionFactoryImplementor factory, Map enabledFilters) {
|
||||
this( persister, factory, enabledFilters, null );
|
||||
public AbstractEntityJoinWalker(
|
||||
OuterJoinLoadable persister,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers loadQueryInfluencers) {
|
||||
this( persister, factory, loadQueryInfluencers, null );
|
||||
}
|
||||
|
||||
public AbstractEntityJoinWalker(OuterJoinLoadable persister, SessionFactoryImplementor factory, Map enabledFilters, String alias) {
|
||||
super( factory, enabledFilters );
|
||||
public AbstractEntityJoinWalker(
|
||||
OuterJoinLoadable persister,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers loadQueryInfluencers,
|
||||
String alias) {
|
||||
super( factory, loadQueryInfluencers );
|
||||
this.persister = persister;
|
||||
this.alias = ( alias == null ) ? generateRootAlias( persister.getEntityName() ) : alias;
|
||||
}
|
||||
|
||||
protected final void initAll(
|
||||
final String whereString,
|
||||
final String orderByString,
|
||||
final LockMode lockMode)
|
||||
throws MappingException {
|
||||
final String whereString,
|
||||
final String orderByString,
|
||||
final LockMode lockMode) throws MappingException {
|
||||
walkEntityTree( persister, getAlias() );
|
||||
List allAssociations = new ArrayList();
|
||||
allAssociations.addAll(associations);
|
||||
allAssociations.add( new OuterJoinableAssociation(
|
||||
persister.getEntityType(),
|
||||
null,
|
||||
null,
|
||||
alias,
|
||||
JoinFragment.LEFT_OUTER_JOIN,
|
||||
getFactory(),
|
||||
CollectionHelper.EMPTY_MAP
|
||||
) );
|
||||
|
||||
allAssociations.add(
|
||||
new OuterJoinableAssociation(
|
||||
persister.getEntityType(),
|
||||
null,
|
||||
null,
|
||||
alias,
|
||||
JoinFragment.LEFT_OUTER_JOIN,
|
||||
getFactory(),
|
||||
CollectionHelper.EMPTY_MAP
|
||||
)
|
||||
);
|
||||
initPersisters(allAssociations, lockMode);
|
||||
initStatementString( whereString, orderByString, lockMode);
|
||||
}
|
||||
|
||||
protected final void initProjection(
|
||||
final String projectionString,
|
||||
final String whereString,
|
||||
final String orderByString,
|
||||
final String groupByString,
|
||||
final LockMode lockMode)
|
||||
throws MappingException {
|
||||
final String projectionString,
|
||||
final String whereString,
|
||||
final String orderByString,
|
||||
final String groupByString,
|
||||
final LockMode lockMode) throws MappingException {
|
||||
walkEntityTree( persister, getAlias() );
|
||||
persisters = new Loadable[0];
|
||||
initStatementString(projectionString, whereString, orderByString, groupByString, lockMode);
|
||||
}
|
||||
|
||||
private void initStatementString(
|
||||
final String condition,
|
||||
final String orderBy,
|
||||
final LockMode lockMode)
|
||||
throws MappingException {
|
||||
final String condition,
|
||||
final String orderBy,
|
||||
final LockMode lockMode) throws MappingException {
|
||||
initStatementString(null, condition, orderBy, "", lockMode);
|
||||
}
|
||||
|
||||
|
@ -149,7 +157,33 @@ public abstract class AbstractEntityJoinWalker extends JoinWalker {
|
|||
* The superclass deliberately excludes collections
|
||||
*/
|
||||
protected boolean isJoinedFetchEnabled(AssociationType type, FetchMode config, CascadeStyle cascadeStyle) {
|
||||
return isJoinedFetchEnabledInMapping(config, type);
|
||||
return isJoinedFetchEnabledInMapping( config, type );
|
||||
}
|
||||
|
||||
protected final boolean isJoinFetchEnabledByProfile(OuterJoinLoadable persister, String path, int propertyNumber) {
|
||||
if ( !getLoadQueryInfluencers().hasEnabledFetchProfiles() ) {
|
||||
// perf optimization
|
||||
return false;
|
||||
}
|
||||
|
||||
// ugh, this stuff has to be made easier...
|
||||
String rootPropertyName = persister.getSubclassPropertyName( propertyNumber );
|
||||
int pos = path.lastIndexOf( rootPropertyName );
|
||||
String relativePropertyPath = pos >= 0
|
||||
? path.substring( pos )
|
||||
: rootPropertyName;
|
||||
String fetchRole = persister.getEntityName() + "." + relativePropertyPath;
|
||||
|
||||
Iterator profiles = getLoadQueryInfluencers().getEnabledFetchProfileNames().iterator();
|
||||
while ( profiles.hasNext() ) {
|
||||
final String profileName = ( String ) profiles.next();
|
||||
final FetchProfile profile = getFactory().getFetchProfile( profileName );
|
||||
final Fetch fetch = profile.getFetchByRole( fetchRole );
|
||||
if ( fetch != null && Fetch.Style.JOIN == fetch.getStyle() ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract String getComment();
|
||||
|
|
|
@ -29,7 +29,6 @@ import java.util.Arrays;
|
|||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.FetchMode;
|
||||
|
@ -39,6 +38,9 @@ import org.hibernate.dialect.Dialect;
|
|||
import org.hibernate.engine.CascadeStyle;
|
||||
import org.hibernate.engine.JoinHelper;
|
||||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.LoadQueryInfluencers;
|
||||
import org.hibernate.engine.profile.FetchProfile;
|
||||
import org.hibernate.engine.profile.Fetch;
|
||||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
import org.hibernate.persister.collection.QueryableCollection;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
@ -69,7 +71,7 @@ public class JoinWalker {
|
|||
private final SessionFactoryImplementor factory;
|
||||
protected final List associations = new ArrayList();
|
||||
private final Set visitedAssociationKeys = new HashSet();
|
||||
private final Map enabledFilters;
|
||||
private final LoadQueryInfluencers loadQueryInfluencers;
|
||||
|
||||
protected String[] suffixes;
|
||||
protected String[] collectionSuffixes;
|
||||
|
@ -81,6 +83,14 @@ public class JoinWalker {
|
|||
protected String[] aliases;
|
||||
protected LockMode[] lockModeArray;
|
||||
protected String sql;
|
||||
|
||||
protected JoinWalker(
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers loadQueryInfluencers) {
|
||||
this.factory = factory;
|
||||
this.loadQueryInfluencers = loadQueryInfluencers;
|
||||
|
||||
}
|
||||
|
||||
public String[] getCollectionSuffixes() {
|
||||
return collectionSuffixes;
|
||||
|
@ -169,14 +179,9 @@ public class JoinWalker {
|
|||
protected Dialect getDialect() {
|
||||
return factory.getDialect();
|
||||
}
|
||||
|
||||
protected Map getEnabledFilters() {
|
||||
return enabledFilters;
|
||||
}
|
||||
|
||||
protected JoinWalker(SessionFactoryImplementor factory, Map enabledFilters) {
|
||||
this.factory = factory;
|
||||
this.enabledFilters = enabledFilters;
|
||||
public LoadQueryInfluencers getLoadQueryInfluencers() {
|
||||
return loadQueryInfluencers;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -184,15 +189,13 @@ public class JoinWalker {
|
|||
* of associations to be fetched by outerjoin (if necessary)
|
||||
*/
|
||||
private void addAssociationToJoinTreeIfNecessary(
|
||||
final AssociationType type,
|
||||
final String[] aliasedLhsColumns,
|
||||
final String alias,
|
||||
final String path,
|
||||
int currentDepth,
|
||||
final int joinType)
|
||||
throws MappingException {
|
||||
|
||||
if (joinType>=0) {
|
||||
final AssociationType type,
|
||||
final String[] aliasedLhsColumns,
|
||||
final String alias,
|
||||
final String path,
|
||||
int currentDepth,
|
||||
final int joinType) throws MappingException {
|
||||
if ( joinType >= 0 ) {
|
||||
addAssociationToJoinTree(
|
||||
type,
|
||||
aliasedLhsColumns,
|
||||
|
@ -200,9 +203,8 @@ public class JoinWalker {
|
|||
path,
|
||||
currentDepth,
|
||||
joinType
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -210,35 +212,37 @@ public class JoinWalker {
|
|||
* of associations to be fetched by outerjoin
|
||||
*/
|
||||
private void addAssociationToJoinTree(
|
||||
final AssociationType type,
|
||||
final String[] aliasedLhsColumns,
|
||||
final String alias,
|
||||
final String path,
|
||||
final int currentDepth,
|
||||
final int joinType)
|
||||
throws MappingException {
|
||||
final AssociationType type,
|
||||
final String[] aliasedLhsColumns,
|
||||
final String alias,
|
||||
String path,
|
||||
final int currentDepth,
|
||||
final int joinType) throws MappingException {
|
||||
|
||||
Joinable joinable = type.getAssociatedJoinable( getFactory() );
|
||||
|
||||
String subalias = generateTableAlias(
|
||||
associations.size()+1, //before adding to collection!
|
||||
path,
|
||||
joinable
|
||||
);
|
||||
// important to generate alias based on size of association collection
|
||||
// *before* adding this join to that collection
|
||||
String subalias = generateTableAlias( associations.size() + 1, path, joinable );
|
||||
|
||||
// NOTE : it should be fine to continue to pass only filters below
|
||||
// (instead of LoadQueryInfluencers) since "from that point on" we
|
||||
// only need to worry about restrictions (and not say adding more
|
||||
// joins)
|
||||
OuterJoinableAssociation assoc = new OuterJoinableAssociation(
|
||||
type,
|
||||
alias,
|
||||
aliasedLhsColumns,
|
||||
subalias,
|
||||
joinType,
|
||||
getFactory(),
|
||||
enabledFilters
|
||||
);
|
||||
assoc.validateJoin(path);
|
||||
associations.add(assoc);
|
||||
getFactory(),
|
||||
loadQueryInfluencers.getEnabledFilters()
|
||||
);
|
||||
assoc.validateJoin( path );
|
||||
associations.add( assoc );
|
||||
|
||||
int nextDepth = currentDepth+1;
|
||||
int nextDepth = currentDepth + 1;
|
||||
// path = "";
|
||||
if ( !joinable.isCollection() ) {
|
||||
if (joinable instanceof OuterJoinLoadable) {
|
||||
walkEntityTree(
|
||||
|
@ -263,11 +267,19 @@ public class JoinWalker {
|
|||
}
|
||||
|
||||
/**
|
||||
* For an entity class, return a list of associations to be fetched by outerjoin
|
||||
* Walk the association tree for an entity, adding associations which should
|
||||
* be join fetched to the {@link #associations} inst var. This form is the
|
||||
* entry point into the walking for a given entity, starting the recursive
|
||||
* calls into {@link #walkEntityTree(OuterJoinLoadable, String, String, int)}.
|
||||
*
|
||||
* @param persister The persister representing the entity to be walked.
|
||||
* @param alias The (root) alias to use for this entity/persister.
|
||||
* @throws org.hibernate.MappingException ???
|
||||
*/
|
||||
protected final void walkEntityTree(OuterJoinLoadable persister, String alias)
|
||||
throws MappingException {
|
||||
walkEntityTree(persister, alias, "", 0);
|
||||
protected final void walkEntityTree(
|
||||
OuterJoinLoadable persister,
|
||||
String alias) throws MappingException {
|
||||
walkEntityTree( persister, alias, "", 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -344,38 +356,49 @@ public class JoinWalker {
|
|||
}
|
||||
|
||||
/**
|
||||
* Walk the tree for a particular entity association
|
||||
* Process a particular association owned by the entity
|
||||
*
|
||||
* @param associationType The type representing the association to be
|
||||
* processed.
|
||||
* @param persister The owner of the association to be processed.
|
||||
* @param propertyNumber The property number for the association
|
||||
* (relative to the persister).
|
||||
* @param alias The entity alias
|
||||
* @param path The path to the association
|
||||
* @param nullable is the association nullable (which I think is supposed
|
||||
* to indicate inner/outer join semantics).
|
||||
* @param currentDepth The current join depth
|
||||
* @throws org.hibernate.MappingException ???
|
||||
*/
|
||||
private final void walkEntityAssociationTree(
|
||||
final AssociationType associationType,
|
||||
final OuterJoinLoadable persister,
|
||||
final int propertyNumber,
|
||||
final String alias,
|
||||
final String path,
|
||||
final boolean nullable,
|
||||
final int currentDepth)
|
||||
throws MappingException {
|
||||
|
||||
private void walkEntityAssociationTree(
|
||||
final AssociationType associationType,
|
||||
final OuterJoinLoadable persister,
|
||||
final int propertyNumber,
|
||||
final String alias,
|
||||
final String path,
|
||||
final boolean nullable,
|
||||
final int currentDepth) throws MappingException {
|
||||
String[] aliasedLhsColumns = JoinHelper.getAliasedLHSColumnNames(
|
||||
associationType, alias, propertyNumber, persister, getFactory()
|
||||
);
|
||||
|
||||
);
|
||||
String[] lhsColumns = JoinHelper.getLHSColumnNames(
|
||||
associationType, propertyNumber, persister, getFactory()
|
||||
);
|
||||
);
|
||||
String lhsTable = JoinHelper.getLHSTableName(associationType, propertyNumber, persister);
|
||||
|
||||
String subpath = subPath( path, persister.getSubclassPropertyName(propertyNumber) );
|
||||
int joinType = getJoinType(
|
||||
associationType,
|
||||
persister.getFetchMode(propertyNumber),
|
||||
persister,
|
||||
subpath,
|
||||
propertyNumber,
|
||||
associationType,
|
||||
persister.getFetchMode( propertyNumber ),
|
||||
persister.getCascadeStyle( propertyNumber ),
|
||||
lhsTable,
|
||||
lhsColumns,
|
||||
nullable,
|
||||
currentDepth,
|
||||
persister.getCascadeStyle(propertyNumber)
|
||||
);
|
||||
currentDepth
|
||||
);
|
||||
addAssociationToJoinTreeIfNecessary(
|
||||
associationType,
|
||||
aliasedLhsColumns,
|
||||
|
@ -383,27 +406,110 @@ public class JoinWalker {
|
|||
subpath,
|
||||
currentDepth,
|
||||
joinType
|
||||
);
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* For an entity class, add to a list of associations to be fetched
|
||||
* by outerjoin
|
||||
* Determine the appropriate type of join (if any) to use to fetch the
|
||||
* given association.
|
||||
*
|
||||
* @param persister The owner of the association.
|
||||
* @param path The path to the association
|
||||
* @param propertyNumber The property number representing the association.
|
||||
* @param associationType The association type.
|
||||
* @param metadataFetchMode The metadata-defined fetch mode.
|
||||
* @param metadataCascadeStyle The metadata-defined cascade style.
|
||||
* @param lhsTable The owner table
|
||||
* @param lhsColumns The owner join columns
|
||||
* @param nullable Is the association nullable.
|
||||
* @param currentDepth Current join depth
|
||||
* @return type of join to use ({@link JoinFragment#INNER_JOIN},
|
||||
* {@link JoinFragment#LEFT_OUTER_JOIN}, or -1 to indicate no joining.
|
||||
* @throws MappingException ??
|
||||
*/
|
||||
private final void walkEntityTree(
|
||||
final OuterJoinLoadable persister,
|
||||
final String alias,
|
||||
final String path,
|
||||
final int currentDepth)
|
||||
throws MappingException {
|
||||
protected int getJoinType(
|
||||
OuterJoinLoadable persister,
|
||||
final String path,
|
||||
int propertyNumber,
|
||||
AssociationType associationType,
|
||||
FetchMode metadataFetchMode,
|
||||
CascadeStyle metadataCascadeStyle,
|
||||
String lhsTable,
|
||||
String[] lhsColumns,
|
||||
final boolean nullable,
|
||||
final int currentDepth) throws MappingException {
|
||||
return getJoinType(
|
||||
associationType,
|
||||
metadataFetchMode,
|
||||
path,
|
||||
lhsTable,
|
||||
lhsColumns,
|
||||
nullable,
|
||||
currentDepth,
|
||||
metadataCascadeStyle
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the appropriate associationType of join (if any) to use to fetch the
|
||||
* given association.
|
||||
*
|
||||
* @param associationType The association associationType.
|
||||
* @param config The metadata-defined fetch mode.
|
||||
* @param path The path to the association
|
||||
* @param lhsTable The owner table
|
||||
* @param lhsColumns The owner join columns
|
||||
* @param nullable Is the association nullable.
|
||||
* @param currentDepth Current join depth
|
||||
* @param cascadeStyle The metadata-defined cascade style.
|
||||
* @return type of join to use ({@link JoinFragment#INNER_JOIN},
|
||||
* {@link JoinFragment#LEFT_OUTER_JOIN}, or -1 to indicate no joining.
|
||||
* @throws MappingException ??
|
||||
*/
|
||||
private int getJoinType(
|
||||
AssociationType associationType,
|
||||
FetchMode config,
|
||||
String path,
|
||||
String lhsTable,
|
||||
String[] lhsColumns,
|
||||
boolean nullable,
|
||||
int currentDepth,
|
||||
CascadeStyle cascadeStyle) throws MappingException {
|
||||
if ( !isJoinedFetchEnabled( associationType, config, cascadeStyle ) ) {
|
||||
return -1;
|
||||
}
|
||||
if ( isTooDeep(currentDepth) || ( associationType.isCollectionType() && isTooManyCollections() ) ) {
|
||||
return -1;
|
||||
}
|
||||
if ( isDuplicateAssociation( lhsTable, lhsColumns, associationType ) ) {
|
||||
return -1;
|
||||
}
|
||||
return getJoinType( nullable, currentDepth );
|
||||
}
|
||||
|
||||
/**
|
||||
* Walk the association tree for an entity, adding associations which should
|
||||
* be join fetched to the {@link #associations} inst var. This form is the
|
||||
* entry point into the walking for a given entity, starting the recursive
|
||||
* calls into {@link #walkEntityTree(OuterJoinLoadable, String, String, int)}.
|
||||
*
|
||||
* @param persister The persister representing the entity to be walked.
|
||||
* @param alias The (root) alias to use for this entity/persister.
|
||||
* @param path todo this seems to be rooted at the *root* persister
|
||||
* @param currentDepth The current join depth
|
||||
* @throws org.hibernate.MappingException ???
|
||||
*/
|
||||
private void walkEntityTree(
|
||||
final OuterJoinLoadable persister,
|
||||
final String alias,
|
||||
final String path,
|
||||
final int currentDepth) throws MappingException {
|
||||
int n = persister.countSubclassProperties();
|
||||
for ( int i=0; i<n; i++ ) {
|
||||
for ( int i = 0; i < n; i++ ) {
|
||||
Type type = persister.getSubclassPropertyType(i);
|
||||
if ( type.isAssociationType() ) {
|
||||
walkEntityAssociationTree(
|
||||
(AssociationType) type,
|
||||
( AssociationType ) type,
|
||||
persister,
|
||||
i,
|
||||
alias,
|
||||
|
@ -414,7 +520,7 @@ public class JoinWalker {
|
|||
}
|
||||
else if ( type.isComponentType() ) {
|
||||
walkComponentTree(
|
||||
(AbstractComponentType) type,
|
||||
( AbstractComponentType ) type,
|
||||
i,
|
||||
0,
|
||||
persister,
|
||||
|
@ -428,28 +534,34 @@ public class JoinWalker {
|
|||
|
||||
/**
|
||||
* For a component, add to a list of associations to be fetched by outerjoin
|
||||
*
|
||||
*
|
||||
* @param componentType The component type to be walked.
|
||||
* @param propertyNumber The property number for the component property (relative to
|
||||
* persister).
|
||||
* @param begin todo unknowm
|
||||
* @param persister The owner of the component property
|
||||
* @param alias The root alias
|
||||
* @param path The property access path
|
||||
* @param currentDepth The current join depth
|
||||
* @throws org.hibernate.MappingException ???
|
||||
*/
|
||||
private void walkComponentTree(
|
||||
final AbstractComponentType componentType,
|
||||
final int propertyNumber,
|
||||
int begin,
|
||||
final OuterJoinLoadable persister,
|
||||
final String alias,
|
||||
final String path,
|
||||
final int currentDepth
|
||||
) throws MappingException {
|
||||
|
||||
final AbstractComponentType componentType,
|
||||
final int propertyNumber,
|
||||
int begin,
|
||||
final OuterJoinLoadable persister,
|
||||
final String alias,
|
||||
final String path,
|
||||
final int currentDepth) throws MappingException {
|
||||
Type[] types = componentType.getSubtypes();
|
||||
String[] propertyNames = componentType.getPropertyNames();
|
||||
for ( int i=0; i <types.length; i++ ) {
|
||||
|
||||
for ( int i = 0; i < types.length; i++ ) {
|
||||
if ( types[i].isAssociationType() ) {
|
||||
AssociationType associationType = (AssociationType) types[i];
|
||||
|
||||
String[] aliasedLhsColumns = JoinHelper.getAliasedLHSColumnNames(
|
||||
associationType, alias, propertyNumber, begin, persister, getFactory()
|
||||
);
|
||||
|
||||
String[] lhsColumns = JoinHelper.getLHSColumnNames(
|
||||
associationType, propertyNumber, begin, persister, getFactory()
|
||||
);
|
||||
|
@ -458,15 +570,17 @@ public class JoinWalker {
|
|||
String subpath = subPath( path, propertyNames[i] );
|
||||
final boolean[] propertyNullability = componentType.getPropertyNullability();
|
||||
final int joinType = getJoinType(
|
||||
persister,
|
||||
subpath,
|
||||
propertyNumber,
|
||||
associationType,
|
||||
componentType.getFetchMode(i),
|
||||
subpath,
|
||||
componentType.getCascadeStyle(i),
|
||||
lhsTable,
|
||||
lhsColumns,
|
||||
propertyNullability==null || propertyNullability[i],
|
||||
currentDepth,
|
||||
componentType.getCascadeStyle(i)
|
||||
);
|
||||
currentDepth
|
||||
);
|
||||
addAssociationToJoinTreeIfNecessary(
|
||||
associationType,
|
||||
aliasedLhsColumns,
|
||||
|
@ -474,23 +588,22 @@ public class JoinWalker {
|
|||
subpath,
|
||||
currentDepth,
|
||||
joinType
|
||||
);
|
||||
);
|
||||
|
||||
}
|
||||
else if ( types[i].isComponentType() ) {
|
||||
String subpath = subPath( path, propertyNames[i] );
|
||||
walkComponentTree(
|
||||
(AbstractComponentType) types[i],
|
||||
( AbstractComponentType ) types[i],
|
||||
propertyNumber,
|
||||
begin,
|
||||
persister,
|
||||
alias,
|
||||
subpath,
|
||||
currentDepth
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
begin+=types[i].getColumnSpan( getFactory() );
|
||||
begin += types[i].getColumnSpan( getFactory() );
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -499,13 +612,12 @@ public class JoinWalker {
|
|||
* For a composite element, add to a list of associations to be fetched by outerjoin
|
||||
*/
|
||||
private void walkCompositeElementTree(
|
||||
final AbstractComponentType compositeType,
|
||||
final String[] cols,
|
||||
final QueryableCollection persister,
|
||||
final String alias,
|
||||
final String path,
|
||||
final int currentDepth)
|
||||
throws MappingException {
|
||||
final AbstractComponentType compositeType,
|
||||
final String[] cols,
|
||||
final QueryableCollection persister,
|
||||
final String alias,
|
||||
final String path,
|
||||
final int currentDepth) throws MappingException {
|
||||
|
||||
Type[] types = compositeType.getSubtypes();
|
||||
String[] propertyNames = compositeType.getPropertyNames();
|
||||
|
@ -570,33 +682,6 @@ public class JoinWalker {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the join type (inner, outer, etc) or -1 if the
|
||||
* association should not be joined. Override on
|
||||
* subclasses.
|
||||
*/
|
||||
protected int getJoinType(
|
||||
AssociationType type,
|
||||
FetchMode config,
|
||||
String path,
|
||||
String lhsTable,
|
||||
String[] lhsColumns,
|
||||
boolean nullable,
|
||||
int currentDepth,
|
||||
CascadeStyle cascadeStyle)
|
||||
throws MappingException {
|
||||
|
||||
if ( !isJoinedFetchEnabled(type, config, cascadeStyle) ) return -1;
|
||||
|
||||
if ( isTooDeep(currentDepth) || ( type.isCollectionType() && isTooManyCollections() ) ) return -1;
|
||||
|
||||
final boolean dupe = isDuplicateAssociation(lhsTable, lhsColumns, type);
|
||||
if (dupe) return -1;
|
||||
|
||||
return getJoinType(nullable, currentDepth);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Use an inner join if it is a non-null association and this
|
||||
* is the "first" join in a series
|
||||
|
@ -604,9 +689,9 @@ public class JoinWalker {
|
|||
protected int getJoinType(boolean nullable, int currentDepth) {
|
||||
//TODO: this is too conservative; if all preceding joins were
|
||||
// also inner joins, we could use an inner join here
|
||||
return !nullable && currentDepth==0 ?
|
||||
JoinFragment.INNER_JOIN :
|
||||
JoinFragment.LEFT_OUTER_JOIN;
|
||||
return !nullable && currentDepth == 0
|
||||
? JoinFragment.INNER_JOIN
|
||||
: JoinFragment.LEFT_OUTER_JOIN;
|
||||
}
|
||||
|
||||
protected boolean isTooDeep(int currentDepth) {
|
||||
|
@ -654,8 +739,7 @@ public class JoinWalker {
|
|||
protected String generateTableAlias(
|
||||
final int n,
|
||||
final String path,
|
||||
final Joinable joinable
|
||||
) {
|
||||
final Joinable joinable) {
|
||||
return StringHelper.generateAlias( joinable.getName(), n );
|
||||
}
|
||||
|
||||
|
|
|
@ -25,10 +25,12 @@
|
|||
package org.hibernate.loader;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.LoadQueryInfluencers;
|
||||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
import org.hibernate.persister.entity.Loadable;
|
||||
import org.hibernate.type.EntityType;
|
||||
|
@ -54,15 +56,17 @@ public abstract class OuterJoinLoader extends BasicLoader {
|
|||
protected String[] suffixes;
|
||||
protected String[] collectionSuffixes;
|
||||
|
||||
private Map enabledFilters;
|
||||
|
||||
protected final Dialect getDialect() {
|
||||
private LoadQueryInfluencers loadQueryInfluencers;
|
||||
|
||||
protected final Dialect getDialect() {
|
||||
return getFactory().getDialect();
|
||||
}
|
||||
|
||||
public OuterJoinLoader(SessionFactoryImplementor factory, Map enabledFilters) {
|
||||
super(factory);
|
||||
this.enabledFilters = enabledFilters;
|
||||
public OuterJoinLoader(
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers loadQueryInfluencers) {
|
||||
super( factory );
|
||||
this.loadQueryInfluencers = loadQueryInfluencers;
|
||||
}
|
||||
|
||||
protected String[] getSuffixes() {
|
||||
|
@ -92,9 +96,9 @@ public abstract class OuterJoinLoader extends BasicLoader {
|
|||
protected LockMode[] getLockModes(Map lockModes) {
|
||||
return lockModeArray;
|
||||
}
|
||||
|
||||
public Map getEnabledFilters() {
|
||||
return enabledFilters;
|
||||
|
||||
public LoadQueryInfluencers getLoadQueryInfluencers() {
|
||||
return loadQueryInfluencers;
|
||||
}
|
||||
|
||||
protected final String[] getAliases() {
|
||||
|
|
|
@ -36,6 +36,12 @@ import org.hibernate.sql.JoinFragment;
|
|||
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
|
||||
*/
|
||||
public final class OuterJoinableAssociation {
|
||||
private final AssociationType joinableType;
|
||||
private final Joinable joinable;
|
||||
|
@ -48,14 +54,13 @@ public final class OuterJoinableAssociation {
|
|||
private final Map enabledFilters;
|
||||
|
||||
public OuterJoinableAssociation(
|
||||
AssociationType joinableType,
|
||||
String lhsAlias,
|
||||
String[] lhsColumns,
|
||||
String rhsAlias,
|
||||
int joinType,
|
||||
SessionFactoryImplementor factory,
|
||||
Map enabledFilters)
|
||||
throws MappingException {
|
||||
AssociationType joinableType,
|
||||
String lhsAlias,
|
||||
String[] lhsColumns,
|
||||
String rhsAlias,
|
||||
int joinType,
|
||||
SessionFactoryImplementor factory,
|
||||
Map enabledFilters) throws MappingException {
|
||||
this.joinableType = joinableType;
|
||||
this.lhsAlias = lhsAlias;
|
||||
this.lhsColumns = lhsColumns;
|
||||
|
|
|
@ -34,9 +34,12 @@ import org.hibernate.FetchMode;
|
|||
import org.hibernate.LockMode;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.LoadQueryInfluencers;
|
||||
import org.hibernate.engine.CascadeStyle;
|
||||
import org.hibernate.loader.BasicLoader;
|
||||
import org.hibernate.loader.OuterJoinableAssociation;
|
||||
import org.hibernate.persister.collection.QueryableCollection;
|
||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
||||
import org.hibernate.sql.JoinFragment;
|
||||
import org.hibernate.sql.Select;
|
||||
import org.hibernate.type.AssociationType;
|
||||
|
@ -58,10 +61,9 @@ public class BasicCollectionJoinWalker extends CollectionJoinWalker {
|
|||
int batchSize,
|
||||
String subquery,
|
||||
SessionFactoryImplementor factory,
|
||||
Map enabledFilters)
|
||||
throws MappingException {
|
||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||
|
||||
super(factory, enabledFilters);
|
||||
super( factory, loadQueryInfluencers );
|
||||
|
||||
this.collectionPersister = collectionPersister;
|
||||
|
||||
|
@ -71,26 +73,25 @@ public class BasicCollectionJoinWalker extends CollectionJoinWalker {
|
|||
|
||||
List allAssociations = new ArrayList();
|
||||
allAssociations.addAll(associations);
|
||||
allAssociations.add( new OuterJoinableAssociation(
|
||||
collectionPersister.getCollectionType(),
|
||||
null,
|
||||
null,
|
||||
alias,
|
||||
JoinFragment.LEFT_OUTER_JOIN,
|
||||
getFactory(),
|
||||
CollectionHelper.EMPTY_MAP
|
||||
) );
|
||||
|
||||
allAssociations.add(
|
||||
new OuterJoinableAssociation(
|
||||
collectionPersister.getCollectionType(),
|
||||
null,
|
||||
null,
|
||||
alias,
|
||||
JoinFragment.LEFT_OUTER_JOIN,
|
||||
getFactory(),
|
||||
CollectionHelper.EMPTY_MAP
|
||||
)
|
||||
);
|
||||
initPersisters(allAssociations, LockMode.NONE);
|
||||
initStatementString(alias, batchSize, subquery);
|
||||
|
||||
}
|
||||
|
||||
private void initStatementString(
|
||||
final String alias,
|
||||
final int batchSize,
|
||||
final String subquery)
|
||||
throws MappingException {
|
||||
final String subquery) throws MappingException {
|
||||
|
||||
final int joins = countEntityPersisters( associations );
|
||||
final int collectionJoins = countCollectionPersisters( associations ) + 1;
|
||||
|
@ -106,7 +107,7 @@ public class BasicCollectionJoinWalker extends CollectionJoinWalker {
|
|||
);
|
||||
|
||||
String manyToManyOrderBy = "";
|
||||
String filter = collectionPersister.filterFragment( alias, getEnabledFilters() );
|
||||
String filter = collectionPersister.filterFragment( alias, getLoadQueryInfluencers().getEnabledFilters() );
|
||||
if ( collectionPersister.isManyToMany() ) {
|
||||
// from the collection of associations, locate OJA for the
|
||||
// ManyToOne corresponding to this persister to fully
|
||||
|
@ -121,9 +122,9 @@ public class BasicCollectionJoinWalker extends CollectionJoinWalker {
|
|||
// we found it
|
||||
filter += collectionPersister.getManyToManyFilterFragment(
|
||||
oja.getRHSAlias(),
|
||||
getEnabledFilters()
|
||||
getLoadQueryInfluencers().getEnabledFilters()
|
||||
);
|
||||
manyToManyOrderBy += collectionPersister.getManyToManyOrderByString( oja.getRHSAlias() );
|
||||
manyToManyOrderBy += collectionPersister.getManyToManyOrderByString( oja.getRHSAlias() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -151,37 +152,36 @@ public class BasicCollectionJoinWalker extends CollectionJoinWalker {
|
|||
sql = select.toStatementString();
|
||||
}
|
||||
|
||||
/**
|
||||
* We can use an inner join for first many-to-many association
|
||||
*/
|
||||
protected int getJoinType(
|
||||
AssociationType type,
|
||||
FetchMode config,
|
||||
String path,
|
||||
Set visitedAssociations,
|
||||
OuterJoinLoadable persister,
|
||||
String path,
|
||||
int propertyNumber,
|
||||
AssociationType associationType,
|
||||
FetchMode metadataFetchMode,
|
||||
CascadeStyle metadataCascadeStyle,
|
||||
String lhsTable,
|
||||
String[] lhsColumns,
|
||||
boolean nullable,
|
||||
int currentDepth)
|
||||
throws MappingException {
|
||||
|
||||
int currentDepth) throws MappingException {
|
||||
int joinType = super.getJoinType(
|
||||
type,
|
||||
config,
|
||||
path,
|
||||
lhsTable,
|
||||
lhsColumns,
|
||||
nullable,
|
||||
currentDepth,
|
||||
null
|
||||
);
|
||||
persister,
|
||||
path,
|
||||
propertyNumber,
|
||||
associationType,
|
||||
metadataFetchMode,
|
||||
metadataCascadeStyle,
|
||||
lhsTable,
|
||||
lhsColumns,
|
||||
nullable,
|
||||
currentDepth
|
||||
);
|
||||
//we can use an inner join for the many-to-many
|
||||
if ( joinType==JoinFragment.LEFT_OUTER_JOIN && "".equals(path) ) {
|
||||
joinType=JoinFragment.INNER_JOIN;
|
||||
}
|
||||
return joinType;
|
||||
}
|
||||
|
||||
|
||||
public String toString() {
|
||||
return getClass().getName() + '(' + collectionPersister.getRole() + ')';
|
||||
}
|
||||
|
|
|
@ -24,12 +24,11 @@
|
|||
*/
|
||||
package org.hibernate.loader.collection;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.LoadQueryInfluencers;
|
||||
import org.hibernate.loader.JoinWalker;
|
||||
import org.hibernate.persister.collection.QueryableCollection;
|
||||
|
||||
|
@ -49,18 +48,16 @@ public class BasicCollectionLoader extends CollectionLoader {
|
|||
public BasicCollectionLoader(
|
||||
QueryableCollection collectionPersister,
|
||||
SessionFactoryImplementor session,
|
||||
Map enabledFilters)
|
||||
throws MappingException {
|
||||
this(collectionPersister, 1, session, enabledFilters);
|
||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||
this( collectionPersister, 1, session, loadQueryInfluencers );
|
||||
}
|
||||
|
||||
public BasicCollectionLoader(
|
||||
QueryableCollection collectionPersister,
|
||||
int batchSize,
|
||||
SessionFactoryImplementor factory,
|
||||
Map enabledFilters)
|
||||
throws MappingException {
|
||||
this(collectionPersister, batchSize, null, factory, enabledFilters);
|
||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||
this( collectionPersister, batchSize, null, factory, loadQueryInfluencers );
|
||||
}
|
||||
|
||||
protected BasicCollectionLoader(
|
||||
|
@ -68,18 +65,16 @@ public class BasicCollectionLoader extends CollectionLoader {
|
|||
int batchSize,
|
||||
String subquery,
|
||||
SessionFactoryImplementor factory,
|
||||
Map enabledFilters)
|
||||
throws MappingException {
|
||||
|
||||
super(collectionPersister, factory, enabledFilters);
|
||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||
super( collectionPersister, factory, loadQueryInfluencers );
|
||||
|
||||
JoinWalker walker = new BasicCollectionJoinWalker(
|
||||
collectionPersister,
|
||||
batchSize,
|
||||
subquery,
|
||||
factory,
|
||||
enabledFilters
|
||||
);
|
||||
loadQueryInfluencers
|
||||
);
|
||||
initFromWalker( walker );
|
||||
|
||||
postInstantiate();
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.hibernate.HibernateException;
|
|||
import org.hibernate.MappingException;
|
||||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.SessionImplementor;
|
||||
import org.hibernate.engine.LoadQueryInfluencers;
|
||||
import org.hibernate.loader.Loader;
|
||||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
import org.hibernate.persister.collection.QueryableCollection;
|
||||
|
@ -77,42 +78,38 @@ public class BatchingCollectionInitializer implements CollectionInitializer {
|
|||
}
|
||||
|
||||
public static CollectionInitializer createBatchingOneToManyInitializer(
|
||||
final QueryableCollection persister,
|
||||
final int maxBatchSize,
|
||||
final SessionFactoryImplementor factory,
|
||||
final Map enabledFilters)
|
||||
throws MappingException {
|
||||
|
||||
if ( maxBatchSize>1 ) {
|
||||
final QueryableCollection persister,
|
||||
final int maxBatchSize,
|
||||
final SessionFactoryImplementor factory,
|
||||
final LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||
if ( maxBatchSize > 1 ) {
|
||||
int[] batchSizesToCreate = ArrayHelper.getBatchSizes(maxBatchSize);
|
||||
Loader[] loadersToCreate = new Loader[ batchSizesToCreate.length ];
|
||||
for ( int i=0; i<batchSizesToCreate.length; i++ ) {
|
||||
loadersToCreate[i] = new OneToManyLoader(persister, batchSizesToCreate[i], factory, enabledFilters);
|
||||
loadersToCreate[i] = new OneToManyLoader( persister, batchSizesToCreate[i], factory, loadQueryInfluencers );
|
||||
}
|
||||
return new BatchingCollectionInitializer(persister, batchSizesToCreate, loadersToCreate);
|
||||
return new BatchingCollectionInitializer( persister, batchSizesToCreate, loadersToCreate );
|
||||
}
|
||||
else {
|
||||
return new OneToManyLoader(persister, factory, enabledFilters);
|
||||
return new OneToManyLoader( persister, factory, loadQueryInfluencers );
|
||||
}
|
||||
}
|
||||
|
||||
public static CollectionInitializer createBatchingCollectionInitializer(
|
||||
final QueryableCollection persister,
|
||||
final int maxBatchSize,
|
||||
final SessionFactoryImplementor factory,
|
||||
final Map enabledFilters)
|
||||
throws MappingException {
|
||||
|
||||
if ( maxBatchSize>1 ) {
|
||||
final QueryableCollection persister,
|
||||
final int maxBatchSize,
|
||||
final SessionFactoryImplementor factory,
|
||||
final LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||
if ( maxBatchSize > 1 ) {
|
||||
int[] batchSizesToCreate = ArrayHelper.getBatchSizes(maxBatchSize);
|
||||
Loader[] loadersToCreate = new Loader[ batchSizesToCreate.length ];
|
||||
for ( int i=0; i<batchSizesToCreate.length; i++ ) {
|
||||
loadersToCreate[i] = new BasicCollectionLoader(persister, batchSizesToCreate[i], factory, enabledFilters);
|
||||
loadersToCreate[i] = new BasicCollectionLoader( persister, batchSizesToCreate[i], factory, loadQueryInfluencers );
|
||||
}
|
||||
return new BatchingCollectionInitializer(persister, batchSizesToCreate, loadersToCreate);
|
||||
}
|
||||
else {
|
||||
return new BasicCollectionLoader(persister, factory, enabledFilters);
|
||||
return new BasicCollectionLoader( persister, factory, loadQueryInfluencers );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,9 +24,8 @@
|
|||
*/
|
||||
package org.hibernate.loader.collection;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.LoadQueryInfluencers;
|
||||
import org.hibernate.loader.JoinWalker;
|
||||
import org.hibernate.util.StringHelper;
|
||||
|
||||
|
@ -40,8 +39,8 @@ import org.hibernate.util.StringHelper;
|
|||
*/
|
||||
public abstract class CollectionJoinWalker extends JoinWalker {
|
||||
|
||||
public CollectionJoinWalker(SessionFactoryImplementor factory, Map enabledFilters) {
|
||||
super( factory, enabledFilters );
|
||||
public CollectionJoinWalker(SessionFactoryImplementor factory, LoadQueryInfluencers loadQueryInfluencers) {
|
||||
super( factory, loadQueryInfluencers );
|
||||
}
|
||||
|
||||
protected StringBuffer whereString(String alias, String[] columnNames, String subselect, int batchSize) {
|
||||
|
|
|
@ -25,11 +25,11 @@
|
|||
package org.hibernate.loader.collection;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.SessionImplementor;
|
||||
import org.hibernate.engine.LoadQueryInfluencers;
|
||||
import org.hibernate.loader.OuterJoinLoader;
|
||||
import org.hibernate.persister.collection.QueryableCollection;
|
||||
import org.hibernate.type.Type;
|
||||
|
@ -45,8 +45,11 @@ public class CollectionLoader extends OuterJoinLoader implements CollectionIniti
|
|||
|
||||
private final QueryableCollection collectionPersister;
|
||||
|
||||
public CollectionLoader(QueryableCollection collectionPersister, SessionFactoryImplementor factory, Map enabledFilters) {
|
||||
super( factory, enabledFilters );
|
||||
public CollectionLoader(
|
||||
QueryableCollection collectionPersister,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers loadQueryInfluencers) {
|
||||
super( factory, loadQueryInfluencers );
|
||||
this.collectionPersister = collectionPersister;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,11 +27,11 @@ package org.hibernate.loader.collection;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.LoadQueryInfluencers;
|
||||
import org.hibernate.loader.BasicLoader;
|
||||
import org.hibernate.loader.OuterJoinableAssociation;
|
||||
import org.hibernate.persister.collection.QueryableCollection;
|
||||
|
@ -67,10 +67,8 @@ public class OneToManyJoinWalker extends CollectionJoinWalker {
|
|||
int batchSize,
|
||||
String subquery,
|
||||
SessionFactoryImplementor factory,
|
||||
Map enabledFilters)
|
||||
throws MappingException {
|
||||
|
||||
super(factory, enabledFilters);
|
||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||
super( factory, loadQueryInfluencers );
|
||||
|
||||
this.oneToManyPersister = oneToManyPersister;
|
||||
|
||||
|
@ -93,7 +91,6 @@ public class OneToManyJoinWalker extends CollectionJoinWalker {
|
|||
|
||||
initPersisters(allAssociations, LockMode.NONE);
|
||||
initStatementString(elementPersister, alias, batchSize, subquery);
|
||||
|
||||
}
|
||||
|
||||
private void initStatementString(
|
||||
|
@ -115,7 +112,7 @@ public class OneToManyJoinWalker extends CollectionJoinWalker {
|
|||
subquery,
|
||||
batchSize
|
||||
);
|
||||
String filter = oneToManyPersister.filterFragment( alias, getEnabledFilters() );
|
||||
String filter = oneToManyPersister.filterFragment( alias, getLoadQueryInfluencers().getEnabledFilters() );
|
||||
whereString.insert( 0, StringHelper.moveAndToBeginning(filter) );
|
||||
|
||||
JoinFragment ojf = mergeOuterJoins(associations);
|
||||
|
|
|
@ -24,12 +24,11 @@
|
|||
*/
|
||||
package org.hibernate.loader.collection;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.LoadQueryInfluencers;
|
||||
import org.hibernate.loader.JoinWalker;
|
||||
import org.hibernate.persister.collection.QueryableCollection;
|
||||
|
||||
|
@ -49,18 +48,16 @@ public class OneToManyLoader extends CollectionLoader {
|
|||
public OneToManyLoader(
|
||||
QueryableCollection oneToManyPersister,
|
||||
SessionFactoryImplementor session,
|
||||
Map enabledFilters)
|
||||
throws MappingException {
|
||||
this(oneToManyPersister, 1, session, enabledFilters);
|
||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||
this( oneToManyPersister, 1, session, loadQueryInfluencers );
|
||||
}
|
||||
|
||||
public OneToManyLoader(
|
||||
QueryableCollection oneToManyPersister,
|
||||
int batchSize,
|
||||
SessionFactoryImplementor factory,
|
||||
Map enabledFilters)
|
||||
throws MappingException {
|
||||
this(oneToManyPersister, batchSize, null, factory, enabledFilters);
|
||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||
this( oneToManyPersister, batchSize, null, factory, loadQueryInfluencers );
|
||||
}
|
||||
|
||||
public OneToManyLoader(
|
||||
|
@ -68,22 +65,19 @@ public class OneToManyLoader extends CollectionLoader {
|
|||
int batchSize,
|
||||
String subquery,
|
||||
SessionFactoryImplementor factory,
|
||||
Map enabledFilters)
|
||||
throws MappingException {
|
||||
|
||||
super(oneToManyPersister, factory, enabledFilters);
|
||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||
super( oneToManyPersister, factory, loadQueryInfluencers );
|
||||
|
||||
JoinWalker walker = new OneToManyJoinWalker(
|
||||
oneToManyPersister,
|
||||
batchSize,
|
||||
subquery,
|
||||
factory,
|
||||
enabledFilters
|
||||
);
|
||||
loadQueryInfluencers
|
||||
);
|
||||
initFromWalker( walker );
|
||||
|
||||
postInstantiate();
|
||||
|
||||
log.debug( "Static select for one-to-many " + oneToManyPersister.getRole() + ": " + getSQLString() );
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ import org.hibernate.engine.EntityKey;
|
|||
import org.hibernate.engine.QueryParameters;
|
||||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.SessionImplementor;
|
||||
import org.hibernate.engine.LoadQueryInfluencers;
|
||||
import org.hibernate.persister.collection.QueryableCollection;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
|
@ -57,10 +58,8 @@ public class SubselectCollectionLoader extends BasicCollectionLoader {
|
|||
QueryParameters queryParameters,
|
||||
Map namedParameterLocMap,
|
||||
SessionFactoryImplementor factory,
|
||||
Map enabledFilters)
|
||||
throws MappingException {
|
||||
|
||||
super(persister, 1, subquery, factory, enabledFilters);
|
||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||
super( persister, 1, subquery, factory, loadQueryInfluencers );
|
||||
|
||||
keys = new Serializable[ entityKeys.size() ];
|
||||
Iterator iter = entityKeys.iterator();
|
||||
|
@ -77,7 +76,7 @@ public class SubselectCollectionLoader extends BasicCollectionLoader {
|
|||
}
|
||||
|
||||
public void initialize(Serializable id, SessionImplementor session)
|
||||
throws HibernateException {
|
||||
throws HibernateException {
|
||||
loadCollectionSubselect(
|
||||
session,
|
||||
keys,
|
||||
|
@ -85,7 +84,7 @@ public class SubselectCollectionLoader extends BasicCollectionLoader {
|
|||
types,
|
||||
namedParameters,
|
||||
getKeyType()
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
public int[] getNamedParameterLocs(String name) {
|
||||
|
|
|
@ -35,6 +35,7 @@ import org.hibernate.engine.EntityKey;
|
|||
import org.hibernate.engine.QueryParameters;
|
||||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.SessionImplementor;
|
||||
import org.hibernate.engine.LoadQueryInfluencers;
|
||||
import org.hibernate.persister.collection.QueryableCollection;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
|
@ -57,10 +58,8 @@ public class SubselectOneToManyLoader extends OneToManyLoader {
|
|||
QueryParameters queryParameters,
|
||||
Map namedParameterLocMap,
|
||||
SessionFactoryImplementor factory,
|
||||
Map enabledFilters)
|
||||
throws MappingException {
|
||||
|
||||
super(persister, 1, subquery, factory, enabledFilters);
|
||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||
super( persister, 1, subquery, factory, loadQueryInfluencers );
|
||||
|
||||
keys = new Serializable[ entityKeys.size() ];
|
||||
Iterator iter = entityKeys.iterator();
|
||||
|
@ -73,11 +72,9 @@ public class SubselectOneToManyLoader extends OneToManyLoader {
|
|||
this.types = queryParameters.getFilteredPositionalParameterTypes();
|
||||
this.values = queryParameters.getFilteredPositionalParameterValues();
|
||||
this.namedParameterLocMap = namedParameterLocMap;
|
||||
|
||||
}
|
||||
|
||||
public void initialize(Serializable id, SessionImplementor session)
|
||||
throws HibernateException {
|
||||
public void initialize(Serializable id, SessionImplementor session) throws HibernateException {
|
||||
loadCollectionSubselect(
|
||||
session,
|
||||
keys,
|
||||
|
@ -85,7 +82,7 @@ public class SubselectOneToManyLoader extends OneToManyLoader {
|
|||
types,
|
||||
namedParameters,
|
||||
getKeyType()
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
public int[] getNamedParameterLocs(String name) {
|
||||
|
|
|
@ -26,7 +26,6 @@ package org.hibernate.loader.criteria;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.Criteria;
|
||||
|
@ -35,6 +34,7 @@ import org.hibernate.LockMode;
|
|||
import org.hibernate.MappingException;
|
||||
import org.hibernate.engine.CascadeStyle;
|
||||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.LoadQueryInfluencers;
|
||||
import org.hibernate.impl.CriteriaImpl;
|
||||
import org.hibernate.loader.AbstractEntityJoinWalker;
|
||||
import org.hibernate.persister.entity.Joinable;
|
||||
|
@ -78,8 +78,8 @@ public class CriteriaJoinWalker extends AbstractEntityJoinWalker {
|
|||
final SessionFactoryImplementor factory,
|
||||
final CriteriaImpl criteria,
|
||||
final String rootEntityName,
|
||||
final Map enabledFilters) {
|
||||
this(persister, translator, factory, criteria, rootEntityName, enabledFilters, null);
|
||||
final LoadQueryInfluencers loadQueryInfluencers) {
|
||||
this( persister, translator, factory, criteria, rootEntityName, loadQueryInfluencers, null );
|
||||
}
|
||||
|
||||
public CriteriaJoinWalker(
|
||||
|
@ -88,9 +88,9 @@ public class CriteriaJoinWalker extends AbstractEntityJoinWalker {
|
|||
final SessionFactoryImplementor factory,
|
||||
final CriteriaImpl criteria,
|
||||
final String rootEntityName,
|
||||
final Map enabledFilters,
|
||||
final LoadQueryInfluencers loadQueryInfluencers,
|
||||
final String alias) {
|
||||
super(persister, factory, enabledFilters, alias);
|
||||
super( persister, factory, loadQueryInfluencers, alias );
|
||||
|
||||
this.translator = translator;
|
||||
|
||||
|
@ -119,16 +119,17 @@ public class CriteriaJoinWalker extends AbstractEntityJoinWalker {
|
|||
}
|
||||
|
||||
protected int getJoinType(
|
||||
AssociationType type,
|
||||
FetchMode config,
|
||||
String path,
|
||||
OuterJoinLoadable persister,
|
||||
final String path,
|
||||
int propertyNumber,
|
||||
AssociationType associationType,
|
||||
FetchMode metadataFetchMode,
|
||||
CascadeStyle metadataCascadeStyle,
|
||||
String lhsTable,
|
||||
String[] lhsColumns,
|
||||
boolean nullable,
|
||||
int currentDepth, CascadeStyle cascadeStyle)
|
||||
throws MappingException {
|
||||
|
||||
if ( translator.isJoin(path) ) {
|
||||
final boolean nullable,
|
||||
final int currentDepth) throws MappingException {
|
||||
if ( translator.isJoin( path ) ) {
|
||||
return translator.getJoinType( path );
|
||||
}
|
||||
else {
|
||||
|
@ -136,23 +137,30 @@ public class CriteriaJoinWalker extends AbstractEntityJoinWalker {
|
|||
return -1;
|
||||
}
|
||||
else {
|
||||
FetchMode fetchMode = translator.getRootCriteria()
|
||||
.getFetchMode(path);
|
||||
if ( isDefaultFetchMode(fetchMode) ) {
|
||||
return super.getJoinType(
|
||||
type,
|
||||
config,
|
||||
path,
|
||||
lhsTable,
|
||||
lhsColumns,
|
||||
nullable,
|
||||
currentDepth, cascadeStyle
|
||||
FetchMode fetchMode = translator.getRootCriteria().getFetchMode( path );
|
||||
if ( isDefaultFetchMode( fetchMode ) ) {
|
||||
if ( isJoinFetchEnabledByProfile( persister, path, propertyNumber ) ) {
|
||||
return getJoinType( nullable, currentDepth );
|
||||
}
|
||||
else {
|
||||
return super.getJoinType(
|
||||
persister,
|
||||
path,
|
||||
propertyNumber,
|
||||
associationType,
|
||||
metadataFetchMode,
|
||||
metadataCascadeStyle,
|
||||
lhsTable,
|
||||
lhsColumns,
|
||||
nullable,
|
||||
currentDepth
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ( fetchMode==FetchMode.JOIN ) {
|
||||
isDuplicateAssociation(lhsTable, lhsColumns, type); //deliberately ignore return value!
|
||||
return getJoinType(nullable, currentDepth);
|
||||
if ( fetchMode == FetchMode.JOIN ) {
|
||||
isDuplicateAssociation( lhsTable, lhsColumns, associationType ); //deliberately ignore return value!
|
||||
return getJoinType( nullable, currentDepth );
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
|
@ -172,7 +180,7 @@ public class CriteriaJoinWalker extends AbstractEntityJoinWalker {
|
|||
*/
|
||||
protected String getWhereFragment() throws MappingException {
|
||||
return super.getWhereFragment() +
|
||||
( (Queryable) getPersister() ).filterFragment( getAlias(), getEnabledFilters() );
|
||||
( (Queryable) getPersister() ).filterFragment( getAlias(), getLoadQueryInfluencers().getEnabledFilters() );
|
||||
}
|
||||
|
||||
protected String generateTableAlias(int n, String path, Joinable joinable) {
|
||||
|
|
|
@ -30,7 +30,6 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.LockMode;
|
||||
|
@ -41,6 +40,7 @@ import org.hibernate.dialect.Dialect;
|
|||
import org.hibernate.engine.QueryParameters;
|
||||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.SessionImplementor;
|
||||
import org.hibernate.engine.LoadQueryInfluencers;
|
||||
import org.hibernate.impl.CriteriaImpl;
|
||||
import org.hibernate.loader.OuterJoinLoader;
|
||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
||||
|
@ -75,9 +75,8 @@ public class CriteriaLoader extends OuterJoinLoader {
|
|||
final SessionFactoryImplementor factory,
|
||||
final CriteriaImpl criteria,
|
||||
final String rootEntityName,
|
||||
final Map enabledFilters)
|
||||
throws HibernateException {
|
||||
super(factory, enabledFilters);
|
||||
final LoadQueryInfluencers loadQueryInfluencers) throws HibernateException {
|
||||
super( factory, loadQueryInfluencers );
|
||||
|
||||
translator = new CriteriaQueryTranslator(
|
||||
factory,
|
||||
|
@ -94,7 +93,7 @@ public class CriteriaLoader extends OuterJoinLoader {
|
|||
factory,
|
||||
criteria,
|
||||
rootEntityName,
|
||||
enabledFilters
|
||||
loadQueryInfluencers
|
||||
);
|
||||
|
||||
initFromWalker(walker);
|
||||
|
|
|
@ -29,12 +29,14 @@ import java.sql.ResultSet;
|
|||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.SessionImplementor;
|
||||
import org.hibernate.engine.LoadQueryInfluencers;
|
||||
import org.hibernate.loader.OuterJoinLoader;
|
||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
||||
import org.hibernate.transform.ResultTransformer;
|
||||
|
@ -52,8 +54,8 @@ public abstract class AbstractEntityLoader extends OuterJoinLoader
|
|||
OuterJoinLoadable persister,
|
||||
Type uniqueKeyType,
|
||||
SessionFactoryImplementor factory,
|
||||
Map enabledFilters) {
|
||||
super( factory, enabledFilters );
|
||||
LoadQueryInfluencers loadQueryInfluencers) {
|
||||
super( factory, loadQueryInfluencers );
|
||||
this.uniqueKeyType = uniqueKeyType;
|
||||
this.entityName = persister.getEntityName();
|
||||
this.persister = persister;
|
||||
|
|
|
@ -28,12 +28,14 @@ import java.io.Serializable;
|
|||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.SessionImplementor;
|
||||
import org.hibernate.engine.LoadQueryInfluencers;
|
||||
import org.hibernate.loader.Loader;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
||||
|
@ -111,19 +113,18 @@ public class BatchingEntityLoader implements UniqueEntityLoader {
|
|||
final int maxBatchSize,
|
||||
final LockMode lockMode,
|
||||
final SessionFactoryImplementor factory,
|
||||
final Map enabledFilters)
|
||||
throws MappingException {
|
||||
final LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||
|
||||
if ( maxBatchSize>1 ) {
|
||||
int[] batchSizesToCreate = ArrayHelper.getBatchSizes(maxBatchSize);
|
||||
Loader[] loadersToCreate = new Loader[ batchSizesToCreate.length ];
|
||||
for ( int i=0; i<batchSizesToCreate.length; i++ ) {
|
||||
loadersToCreate[i] = new EntityLoader(persister, batchSizesToCreate[i], lockMode, factory, enabledFilters);
|
||||
loadersToCreate[i] = new EntityLoader(persister, batchSizesToCreate[i], lockMode, factory, loadQueryInfluencers);
|
||||
}
|
||||
return new BatchingEntityLoader(persister, batchSizesToCreate, loadersToCreate);
|
||||
}
|
||||
else {
|
||||
return new EntityLoader(persister, lockMode, factory, enabledFilters);
|
||||
return new EntityLoader(persister, lockMode, factory, loadQueryInfluencers);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.hibernate.MappingException;
|
|||
import org.hibernate.engine.CascadeStyle;
|
||||
import org.hibernate.engine.CascadingAction;
|
||||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.LoadQueryInfluencers;
|
||||
import org.hibernate.loader.AbstractEntityJoinWalker;
|
||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
||||
import org.hibernate.type.AssociationType;
|
||||
|
@ -41,7 +42,7 @@ public class CascadeEntityJoinWalker extends AbstractEntityJoinWalker {
|
|||
|
||||
public CascadeEntityJoinWalker(OuterJoinLoadable persister, CascadingAction action, SessionFactoryImplementor factory)
|
||||
throws MappingException {
|
||||
super( persister, factory, CollectionHelper.EMPTY_MAP );
|
||||
super( persister, factory, LoadQueryInfluencers.NONE );
|
||||
this.cascadeAction = action;
|
||||
StringBuffer whereCondition = whereString( getAlias(), persister.getIdentifierColumnNames(), 1 )
|
||||
//include the discriminator and class-level where, but not filters
|
||||
|
|
|
@ -27,29 +27,28 @@ package org.hibernate.loader.entity;
|
|||
import org.hibernate.MappingException;
|
||||
import org.hibernate.engine.CascadingAction;
|
||||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.LoadQueryInfluencers;
|
||||
import org.hibernate.loader.JoinWalker;
|
||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
||||
import org.hibernate.util.CollectionHelper;
|
||||
|
||||
public class CascadeEntityLoader extends AbstractEntityLoader {
|
||||
|
||||
public CascadeEntityLoader(
|
||||
OuterJoinLoadable persister,
|
||||
CascadingAction action,
|
||||
SessionFactoryImplementor factory)
|
||||
throws MappingException {
|
||||
SessionFactoryImplementor factory) throws MappingException {
|
||||
super(
|
||||
persister,
|
||||
persister.getIdentifierType(),
|
||||
factory,
|
||||
CollectionHelper.EMPTY_MAP
|
||||
);
|
||||
factory,
|
||||
LoadQueryInfluencers.NONE
|
||||
);
|
||||
|
||||
JoinWalker walker = new CascadeEntityJoinWalker(
|
||||
persister,
|
||||
action,
|
||||
factory
|
||||
);
|
||||
);
|
||||
initFromWalker( walker );
|
||||
|
||||
postInstantiate();
|
||||
|
|
|
@ -27,7 +27,6 @@ package org.hibernate.loader.entity;
|
|||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -36,6 +35,7 @@ import org.hibernate.LockMode;
|
|||
import org.hibernate.MappingException;
|
||||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.SessionImplementor;
|
||||
import org.hibernate.engine.LoadQueryInfluencers;
|
||||
import org.hibernate.loader.JoinWalker;
|
||||
import org.hibernate.loader.OuterJoinLoader;
|
||||
import org.hibernate.persister.collection.QueryableCollection;
|
||||
|
@ -61,9 +61,8 @@ public class CollectionElementLoader extends OuterJoinLoader {
|
|||
public CollectionElementLoader(
|
||||
QueryableCollection collectionPersister,
|
||||
SessionFactoryImplementor factory,
|
||||
Map enabledFilters)
|
||||
throws MappingException {
|
||||
super(factory, enabledFilters);
|
||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||
super( factory, loadQueryInfluencers );
|
||||
|
||||
this.keyType = collectionPersister.getKeyType();
|
||||
this.indexType = collectionPersister.getIndexType();
|
||||
|
@ -79,7 +78,7 @@ public class CollectionElementLoader extends OuterJoinLoader {
|
|||
1,
|
||||
LockMode.NONE,
|
||||
factory,
|
||||
enabledFilters
|
||||
loadQueryInfluencers
|
||||
);
|
||||
initFromWalker( walker );
|
||||
|
||||
|
@ -130,5 +129,4 @@ public class CollectionElementLoader extends OuterJoinLoader {
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -25,13 +25,16 @@
|
|||
package org.hibernate.loader.entity;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.hibernate.FetchMode;
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.engine.CascadeStyle;
|
||||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.LoadQueryInfluencers;
|
||||
import org.hibernate.engine.profile.FetchProfile;
|
||||
import org.hibernate.engine.profile.Fetch;
|
||||
import org.hibernate.loader.AbstractEntityJoinWalker;
|
||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
||||
import org.hibernate.type.AssociationType;
|
||||
|
@ -52,28 +55,47 @@ public class EntityJoinWalker extends AbstractEntityJoinWalker {
|
|||
int batchSize,
|
||||
LockMode lockMode,
|
||||
SessionFactoryImplementor factory,
|
||||
Map enabledFilters)
|
||||
throws MappingException {
|
||||
super(persister, factory, enabledFilters);
|
||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||
super( persister, factory, loadQueryInfluencers );
|
||||
|
||||
this.lockMode = lockMode;
|
||||
|
||||
StringBuffer whereCondition = whereString( getAlias(), uniqueKey, batchSize )
|
||||
//include the discriminator and class-level where, but not filters
|
||||
.append( persister.filterFragment( getAlias(), Collections.EMPTY_MAP ) );
|
||||
//include the discriminator and class-level where, but not filters
|
||||
.append( persister.filterFragment( getAlias(), Collections.EMPTY_MAP ) );
|
||||
|
||||
initAll( whereCondition.toString(), "", lockMode );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable outer join fetching if this loader obtains an
|
||||
* upgrade lock mode
|
||||
*/
|
||||
protected boolean isJoinedFetchEnabled(AssociationType type, FetchMode config, CascadeStyle cascadeStyle) {
|
||||
return lockMode.greaterThan(LockMode.READ) ?
|
||||
false :
|
||||
super.isJoinedFetchEnabled(type, config, cascadeStyle);
|
||||
protected int getJoinType(
|
||||
OuterJoinLoadable persister,
|
||||
String path,
|
||||
int propertyNumber,
|
||||
AssociationType associationType,
|
||||
FetchMode metadataFetchMode,
|
||||
CascadeStyle metadataCascadeStyle,
|
||||
String lhsTable,
|
||||
String[] lhsColumns,
|
||||
boolean nullable,
|
||||
int currentDepth) throws MappingException {
|
||||
// NOTE : we override this form here specifically to account for
|
||||
// fetch profiles.
|
||||
// TODO : how to best handle criteria queries?
|
||||
if ( lockMode.greaterThan( LockMode.READ ) ) {
|
||||
return -1;
|
||||
}
|
||||
if ( isTooDeep( currentDepth )
|
||||
|| ( associationType.isCollectionType() && isTooManyCollections() ) ) {
|
||||
return -1;
|
||||
}
|
||||
if ( !isJoinedFetchEnabledInMapping( metadataFetchMode, associationType )
|
||||
&& !isJoinFetchEnabledByProfile( persister, path, propertyNumber ) ) {
|
||||
return -1;
|
||||
}
|
||||
if ( isDuplicateAssociation( lhsTable, lhsColumns, associationType ) ) {
|
||||
return -1;
|
||||
}
|
||||
return getJoinType( nullable, currentDepth );
|
||||
}
|
||||
|
||||
public String getComment() {
|
||||
|
|
|
@ -25,12 +25,14 @@
|
|||
package org.hibernate.loader.entity;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.SessionImplementor;
|
||||
import org.hibernate.engine.LoadQueryInfluencers;
|
||||
import org.hibernate.loader.JoinWalker;
|
||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
||||
import org.hibernate.type.Type;
|
||||
|
@ -51,9 +53,8 @@ public class EntityLoader extends AbstractEntityLoader {
|
|||
OuterJoinLoadable persister,
|
||||
LockMode lockMode,
|
||||
SessionFactoryImplementor factory,
|
||||
Map enabledFilters)
|
||||
throws MappingException {
|
||||
this(persister, 1, lockMode, factory, enabledFilters);
|
||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||
this( persister, 1, lockMode, factory, loadQueryInfluencers );
|
||||
}
|
||||
|
||||
public EntityLoader(
|
||||
|
@ -61,16 +62,15 @@ public class EntityLoader extends AbstractEntityLoader {
|
|||
int batchSize,
|
||||
LockMode lockMode,
|
||||
SessionFactoryImplementor factory,
|
||||
Map enabledFilters)
|
||||
throws MappingException {
|
||||
this(
|
||||
persister,
|
||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||
this(
|
||||
persister,
|
||||
persister.getIdentifierColumnNames(),
|
||||
persister.getIdentifierType(),
|
||||
batchSize,
|
||||
lockMode,
|
||||
factory,
|
||||
enabledFilters
|
||||
loadQueryInfluencers
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -81,9 +81,8 @@ public class EntityLoader extends AbstractEntityLoader {
|
|||
int batchSize,
|
||||
LockMode lockMode,
|
||||
SessionFactoryImplementor factory,
|
||||
Map enabledFilters)
|
||||
throws MappingException {
|
||||
super(persister, uniqueKeyType, factory, enabledFilters);
|
||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||
super( persister, uniqueKeyType, factory, loadQueryInfluencers );
|
||||
|
||||
JoinWalker walker = new EntityJoinWalker(
|
||||
persister,
|
||||
|
@ -91,8 +90,8 @@ public class EntityLoader extends AbstractEntityLoader {
|
|||
batchSize,
|
||||
lockMode,
|
||||
factory,
|
||||
enabledFilters
|
||||
);
|
||||
loadQueryInfluencers
|
||||
);
|
||||
initFromWalker( walker );
|
||||
|
||||
postInstantiate();
|
||||
|
@ -103,9 +102,10 @@ public class EntityLoader extends AbstractEntityLoader {
|
|||
|
||||
}
|
||||
|
||||
public Object loadByUniqueKey(SessionImplementor session, Object key)
|
||||
throws HibernateException {
|
||||
return load(session, key, null, null);
|
||||
public Object loadByUniqueKey(
|
||||
SessionImplementor session,
|
||||
Object key) throws HibernateException {
|
||||
return load( session, key, null, null );
|
||||
}
|
||||
|
||||
protected boolean isSingleRowLoader() {
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* 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.mapping;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
|
||||
/**
|
||||
* A fetch profile allows a user to dynamically modify the fetching
|
||||
* strategy used for particular associations at runtime, whereas that
|
||||
* information was historically only statically defined in the metadata.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class FetchProfile {
|
||||
private final String name;
|
||||
private LinkedHashSet fetches = new LinkedHashSet();
|
||||
|
||||
public FetchProfile(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public LinkedHashSet getFetches() {
|
||||
return fetches;
|
||||
}
|
||||
|
||||
public void addFetch(String entity, String association, String style) {
|
||||
fetches.add( new Fetch( entity, association, style ) );
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if ( this == o ) {
|
||||
return true;
|
||||
}
|
||||
if ( o == null || getClass() != o.getClass() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FetchProfile that = ( FetchProfile ) o;
|
||||
|
||||
return name.equals( that.name );
|
||||
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return name.hashCode();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Defines an individual association fetch within the given profile.
|
||||
*/
|
||||
public static class Fetch {
|
||||
private final String entity;
|
||||
private final String association;
|
||||
private final String style;
|
||||
|
||||
public Fetch(String entity, String association, String style) {
|
||||
this.entity = entity;
|
||||
this.association = association;
|
||||
this.style = style;
|
||||
}
|
||||
|
||||
public String getEntity() {
|
||||
return entity;
|
||||
}
|
||||
|
||||
public String getAssociation() {
|
||||
return association;
|
||||
}
|
||||
|
||||
public String getStyle() {
|
||||
return style;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -58,6 +58,7 @@ import org.hibernate.engine.SessionFactoryImplementor;
|
|||
import org.hibernate.engine.SessionImplementor;
|
||||
import org.hibernate.engine.SubselectFetch;
|
||||
import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
|
||||
import org.hibernate.engine.LoadQueryInfluencers;
|
||||
import org.hibernate.exception.JDBCExceptionHelper;
|
||||
import org.hibernate.exception.SQLExceptionConverter;
|
||||
import org.hibernate.id.IdentifierGenerator;
|
||||
|
@ -561,7 +562,7 @@ public abstract class AbstractCollectionPersister
|
|||
|
||||
public void postInstantiate() throws MappingException {
|
||||
initializer = queryLoaderName == null ?
|
||||
createCollectionInitializer( CollectionHelper.EMPTY_MAP ) :
|
||||
createCollectionInitializer( LoadQueryInfluencers.NONE ) :
|
||||
new NamedQueryCollectionInitializer( queryLoaderName, this );
|
||||
}
|
||||
|
||||
|
@ -601,7 +602,7 @@ public abstract class AbstractCollectionPersister
|
|||
return initializer;
|
||||
}
|
||||
else {
|
||||
return createCollectionInitializer( session.getEnabledFilters() );
|
||||
return createCollectionInitializer( session.getLoadQueryInfluencers() );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -637,7 +638,7 @@ public abstract class AbstractCollectionPersister
|
|||
|
||||
protected abstract CollectionInitializer createSubselectInitializer(SubselectFetch subselect, SessionImplementor session);
|
||||
|
||||
protected abstract CollectionInitializer createCollectionInitializer(Map enabledFilters)
|
||||
protected abstract CollectionInitializer createCollectionInitializer(LoadQueryInfluencers loadQueryInfluencers)
|
||||
throws MappingException;
|
||||
|
||||
public CollectionRegionAccessStrategy getCacheAccessStrategy() {
|
||||
|
|
|
@ -42,6 +42,7 @@ import org.hibernate.collection.PersistentCollection;
|
|||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.SessionImplementor;
|
||||
import org.hibernate.engine.SubselectFetch;
|
||||
import org.hibernate.engine.LoadQueryInfluencers;
|
||||
import org.hibernate.exception.JDBCExceptionHelper;
|
||||
import org.hibernate.loader.collection.BatchingCollectionInitializer;
|
||||
import org.hibernate.loader.collection.CollectionInitializer;
|
||||
|
@ -315,9 +316,9 @@ public class BasicCollectionPersister extends AbstractCollectionPersister {
|
|||
*
|
||||
* @see org.hibernate.loader.collection.BasicCollectionLoader
|
||||
*/
|
||||
protected CollectionInitializer createCollectionInitializer(java.util.Map enabledFilters)
|
||||
protected CollectionInitializer createCollectionInitializer(LoadQueryInfluencers loadQueryInfluencers)
|
||||
throws MappingException {
|
||||
return BatchingCollectionInitializer.createBatchingCollectionInitializer( this, batchSize, getFactory(), enabledFilters );
|
||||
return BatchingCollectionInitializer.createBatchingCollectionInitializer( this, batchSize, getFactory(), loadQueryInfluencers );
|
||||
}
|
||||
|
||||
public String fromJoinFragment(String alias, boolean innerJoin, boolean includeSubclasses) {
|
||||
|
@ -336,8 +337,8 @@ public class BasicCollectionPersister extends AbstractCollectionPersister {
|
|||
subselect.getQueryParameters(),
|
||||
subselect.getNamedParameterLocMap(),
|
||||
session.getFactory(),
|
||||
session.getEnabledFilters()
|
||||
);
|
||||
session.getLoadQueryInfluencers()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ import org.hibernate.collection.PersistentCollection;
|
|||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.SessionImplementor;
|
||||
import org.hibernate.engine.SubselectFetch;
|
||||
import org.hibernate.engine.LoadQueryInfluencers;
|
||||
import org.hibernate.exception.JDBCExceptionHelper;
|
||||
import org.hibernate.loader.collection.BatchingCollectionInitializer;
|
||||
import org.hibernate.loader.collection.CollectionInitializer;
|
||||
|
@ -338,8 +339,9 @@ public class OneToManyPersister extends AbstractCollectionPersister {
|
|||
*
|
||||
* @see org.hibernate.loader.collection.OneToManyLoader
|
||||
*/
|
||||
protected CollectionInitializer createCollectionInitializer(java.util.Map enabledFilters) throws MappingException {
|
||||
return BatchingCollectionInitializer.createBatchingOneToManyInitializer( this, batchSize, getFactory(), enabledFilters );
|
||||
protected CollectionInitializer createCollectionInitializer(LoadQueryInfluencers loadQueryInfluencers)
|
||||
throws MappingException {
|
||||
return BatchingCollectionInitializer.createBatchingOneToManyInitializer( this, batchSize, getFactory(), loadQueryInfluencers );
|
||||
}
|
||||
|
||||
public String fromJoinFragment(String alias,
|
||||
|
@ -375,12 +377,12 @@ public class OneToManyPersister extends AbstractCollectionPersister {
|
|||
subselect.getQueryParameters(),
|
||||
subselect.getNamedParameterLocMap(),
|
||||
session.getFactory(),
|
||||
session.getEnabledFilters()
|
||||
session.getLoadQueryInfluencers()
|
||||
);
|
||||
}
|
||||
|
||||
public Object getElementByIndex(Serializable key, Object index, SessionImplementor session, Object owner) {
|
||||
return new CollectionElementLoader( this, getFactory(), session.getEnabledFilters() )
|
||||
return new CollectionElementLoader( this, getFactory(), session.getLoadQueryInfluencers() )
|
||||
.loadElement( session, key, incrementIndexByBase(index) );
|
||||
}
|
||||
|
||||
|
|
|
@ -52,7 +52,6 @@ import org.hibernate.jdbc.Expectation;
|
|||
import org.hibernate.jdbc.Expectations;
|
||||
import org.hibernate.jdbc.TooManyRowsAffectedException;
|
||||
import org.hibernate.dialect.lock.LockingStrategy;
|
||||
import org.hibernate.cache.CacheConcurrencyStrategy;
|
||||
import org.hibernate.cache.CacheKey;
|
||||
import org.hibernate.cache.access.EntityRegionAccessStrategy;
|
||||
import org.hibernate.cache.entry.CacheEntry;
|
||||
|
@ -69,6 +68,7 @@ import org.hibernate.engine.Versioning;
|
|||
import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
|
||||
import org.hibernate.engine.EntityKey;
|
||||
import org.hibernate.engine.ValueInclusion;
|
||||
import org.hibernate.engine.LoadQueryInfluencers;
|
||||
import org.hibernate.exception.JDBCExceptionHelper;
|
||||
import org.hibernate.id.IdentifierGenerator;
|
||||
import org.hibernate.id.PostInsertIdentifierGenerator;
|
||||
|
@ -109,7 +109,6 @@ import org.hibernate.type.Type;
|
|||
import org.hibernate.type.TypeFactory;
|
||||
import org.hibernate.type.VersionType;
|
||||
import org.hibernate.util.ArrayHelper;
|
||||
import org.hibernate.util.CollectionHelper;
|
||||
import org.hibernate.util.FilterHelper;
|
||||
import org.hibernate.util.StringHelper;
|
||||
|
||||
|
@ -196,6 +195,8 @@ public abstract class AbstractEntityPersister
|
|||
// dynamic filters attached to the class-level
|
||||
private final FilterHelper filterHelper;
|
||||
|
||||
private final Set affectingFetchProfileNames = new HashSet();
|
||||
|
||||
private final Map uniqueKeyLoaders = new HashMap();
|
||||
private final Map lockers = new HashMap();
|
||||
private final Map loaders = new HashMap();
|
||||
|
@ -1667,26 +1668,27 @@ public abstract class AbstractEntityPersister
|
|||
|
||||
}
|
||||
|
||||
public Object loadByUniqueKey(String propertyName, Object uniqueKey, SessionImplementor session)
|
||||
throws HibernateException {
|
||||
return getAppropriateUniqueKeyLoader( propertyName, session.getEnabledFilters() )
|
||||
.loadByUniqueKey( session, uniqueKey );
|
||||
public Object loadByUniqueKey(
|
||||
String propertyName,
|
||||
Object uniqueKey,
|
||||
SessionImplementor session) throws HibernateException {
|
||||
return getAppropriateUniqueKeyLoader( propertyName, session ).loadByUniqueKey( session, uniqueKey );
|
||||
}
|
||||
|
||||
private EntityLoader getAppropriateUniqueKeyLoader(String propertyName, Map enabledFilters) {
|
||||
|
||||
final boolean useStaticLoader = ( enabledFilters == null || enabledFilters.isEmpty() )
|
||||
private EntityLoader getAppropriateUniqueKeyLoader(String propertyName, SessionImplementor session) {
|
||||
final boolean useStaticLoader = !session.getLoadQueryInfluencers().hasEnabledFilters()
|
||||
&& !session.getLoadQueryInfluencers().hasEnabledFetchProfiles()
|
||||
&& propertyName.indexOf('.')<0; //ugly little workaround for fact that createUniqueKeyLoaders() does not handle component properties
|
||||
|
||||
if ( useStaticLoader ) {
|
||||
return (EntityLoader) uniqueKeyLoaders.get( propertyName );
|
||||
return ( EntityLoader ) uniqueKeyLoaders.get( propertyName );
|
||||
}
|
||||
else {
|
||||
return createUniqueKeyLoader(
|
||||
propertyMapping.toType(propertyName),
|
||||
propertyMapping.toColumns(propertyName),
|
||||
enabledFilters
|
||||
);
|
||||
propertyMapping.toType( propertyName ),
|
||||
propertyMapping.toColumns( propertyName ),
|
||||
session.getLoadQueryInfluencers()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1705,21 +1707,31 @@ public abstract class AbstractEntityPersister
|
|||
createUniqueKeyLoader(
|
||||
propertyTypes[i],
|
||||
getPropertyColumnNames( i ),
|
||||
CollectionHelper.EMPTY_MAP
|
||||
)
|
||||
);
|
||||
LoadQueryInfluencers.NONE
|
||||
)
|
||||
);
|
||||
//TODO: create uk loaders for component properties
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private EntityLoader createUniqueKeyLoader(Type uniqueKeyType, String[] columns, Map enabledFilters) {
|
||||
private EntityLoader createUniqueKeyLoader(
|
||||
Type uniqueKeyType,
|
||||
String[] columns,
|
||||
LoadQueryInfluencers loadQueryInfluencers) {
|
||||
if ( uniqueKeyType.isEntityType() ) {
|
||||
String className = ( ( EntityType ) uniqueKeyType ).getAssociatedEntityName();
|
||||
uniqueKeyType = getFactory().getEntityPersister( className ).getIdentifierType();
|
||||
}
|
||||
|
||||
return new EntityLoader( this, columns, uniqueKeyType, 1, LockMode.NONE, getFactory(), enabledFilters );
|
||||
return new EntityLoader(
|
||||
this,
|
||||
columns,
|
||||
uniqueKeyType,
|
||||
1,
|
||||
LockMode.NONE,
|
||||
getFactory(),
|
||||
loadQueryInfluencers
|
||||
);
|
||||
}
|
||||
|
||||
protected String getSQLWhereString(String alias) {
|
||||
|
@ -1770,13 +1782,21 @@ public abstract class AbstractEntityPersister
|
|||
}
|
||||
}
|
||||
|
||||
protected UniqueEntityLoader createEntityLoader(LockMode lockMode, Map enabledFilters) throws MappingException {
|
||||
protected UniqueEntityLoader createEntityLoader(
|
||||
LockMode lockMode,
|
||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||
//TODO: disable batch loading if lockMode > READ?
|
||||
return BatchingEntityLoader.createBatchingEntityLoader( this, batchSize, lockMode, getFactory(), enabledFilters );
|
||||
return BatchingEntityLoader.createBatchingEntityLoader(
|
||||
this,
|
||||
batchSize,
|
||||
lockMode,
|
||||
getFactory(),
|
||||
loadQueryInfluencers
|
||||
);
|
||||
}
|
||||
|
||||
protected UniqueEntityLoader createEntityLoader(LockMode lockMode) throws MappingException {
|
||||
return createEntityLoader( lockMode, CollectionHelper.EMPTY_MAP );
|
||||
return createEntityLoader( lockMode, LoadQueryInfluencers.NONE );
|
||||
}
|
||||
|
||||
protected boolean check(int rows, Serializable id, int tableNumber, Expectation expectation, PreparedStatement statement) throws HibernateException {
|
||||
|
@ -3072,21 +3092,49 @@ public abstract class AbstractEntityPersister
|
|||
return loader.load( id, optionalObject, session );
|
||||
}
|
||||
|
||||
public void registerAffectingFetchProfile(String fetchProfileName) {
|
||||
affectingFetchProfileNames.add( fetchProfileName );
|
||||
}
|
||||
|
||||
private boolean isAffectedByEnabledFetchProfiles(SessionImplementor session) {
|
||||
Iterator itr = session.getLoadQueryInfluencers().getEnabledFetchProfileNames().iterator();
|
||||
while ( itr.hasNext() ) {
|
||||
if ( affectingFetchProfileNames.contains( itr.next() ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isAffectedByEnabledFilters(SessionImplementor session) {
|
||||
return session.getLoadQueryInfluencers().hasEnabledFilters()
|
||||
&& filterHelper.isAffectedBy( session.getLoadQueryInfluencers().getEnabledFilters() );
|
||||
}
|
||||
|
||||
private UniqueEntityLoader getAppropriateLoader(LockMode lockMode, SessionImplementor session) {
|
||||
final Map enabledFilters = session.getEnabledFilters();
|
||||
if ( queryLoader != null ) {
|
||||
// if the user specified a custom query loader we need to that
|
||||
// regardless of any other consideration
|
||||
return queryLoader;
|
||||
}
|
||||
else if ( enabledFilters == null || enabledFilters.isEmpty() ) {
|
||||
if ( session.getFetchProfile()!=null && LockMode.UPGRADE.greaterThan(lockMode) ) {
|
||||
return (UniqueEntityLoader) loaders.get( session.getFetchProfile() );
|
||||
}
|
||||
else {
|
||||
return (UniqueEntityLoader) loaders.get( lockMode );
|
||||
}
|
||||
else if ( isAffectedByEnabledFilters( session ) ) {
|
||||
// because filters affect the rows returned (because they add
|
||||
// restirctions) these need to be next in precendence
|
||||
return createEntityLoader( lockMode, session.getLoadQueryInfluencers() );
|
||||
}
|
||||
else if ( session.getLoadQueryInfluencers().getInternalFetchProfile() != null && LockMode.UPGRADE.greaterThan( lockMode ) ) {
|
||||
// Next, we consider whether an 'internal' fetch profile has been set.
|
||||
// This indicates a special fetch profile Hibernate needs applied
|
||||
// (for its merge loading process e.g.).
|
||||
return ( UniqueEntityLoader ) loaders.get( session.getLoadQueryInfluencers().getInternalFetchProfile() );
|
||||
}
|
||||
else if ( isAffectedByEnabledFetchProfiles( session ) ) {
|
||||
// If the session has associated influencers we need to adjust the
|
||||
// SQL query used for loading based on those influencers
|
||||
return createEntityLoader( lockMode, session.getLoadQueryInfluencers() );
|
||||
}
|
||||
else {
|
||||
return createEntityLoader( lockMode, enabledFilters );
|
||||
return ( UniqueEntityLoader ) loaders.get( lockMode );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -108,4 +108,12 @@ public interface Loadable extends EntityPersister {
|
|||
|
||||
public boolean isAbstract();
|
||||
|
||||
/**
|
||||
* Register the name of a fetch profile determined to have an affect on the
|
||||
* underlying loadable in regards to the fact that the underlying load SQL
|
||||
* needs to be adjust when the given fetch profile is enabled.
|
||||
*
|
||||
* @param fetchProfileName The name of the profile affecting this.
|
||||
*/
|
||||
public void registerAffectingFetchProfile(String fetchProfileName);
|
||||
}
|
||||
|
|
|
@ -19,11 +19,12 @@ arbitrary number of queries, and import declarations of arbitrary classes.
|
|||
<!ELEMENT hibernate-mapping (
|
||||
meta*,
|
||||
typedef*,
|
||||
import*,
|
||||
import*,
|
||||
(class|subclass|joined-subclass|union-subclass)*,
|
||||
resultset*,
|
||||
(query|sql-query)*,
|
||||
filter-def*,
|
||||
fetch-profile*,
|
||||
database-object*
|
||||
)>
|
||||
<!ATTLIST hibernate-mapping schema CDATA #IMPLIED> <!-- default: none -->
|
||||
|
@ -78,6 +79,7 @@ arbitrary number of queries, and import declarations of arbitrary classes.
|
|||
((join*,subclass*)|joined-subclass*|union-subclass*),
|
||||
loader?,sql-insert?,sql-update?,sql-delete?,
|
||||
filter*,
|
||||
fetch-profile*,
|
||||
resultset*,
|
||||
(query|sql-query)*
|
||||
)>
|
||||
|
@ -133,6 +135,22 @@ arbitrary number of queries, and import declarations of arbitrary classes.
|
|||
<!ATTLIST filter name CDATA #REQUIRED>
|
||||
<!ATTLIST filter condition CDATA #IMPLIED>
|
||||
|
||||
<!--
|
||||
-->
|
||||
<!ELEMENT fetch-profile (fetch*)>
|
||||
<!ATTLIST fetch-profile name CDATA #REQUIRED>
|
||||
|
||||
<!--
|
||||
The <fetch> element defines a single path to which the fetch
|
||||
refers, as well as the style of fetch to apply. The 'root' of the
|
||||
path is different depending upon the context in which the
|
||||
containing <fetch-profile/> occurs; within a <class/> element,
|
||||
the entity-name of the containing class mapping is assumed...
|
||||
-->
|
||||
<!ELEMENT fetch EMPTY>
|
||||
<!ATTLIST fetch entity CDATA #IMPLIED> <!-- Implied as long as the containing fetch profile is contained in a class mapping -->
|
||||
<!ATTLIST fetch association CDATA #REQUIRED>
|
||||
<!ATTLIST fetch style (join|select) "join">
|
||||
|
||||
<!-- A join allows some properties of a class to be persisted to a second table -->
|
||||
|
||||
|
@ -231,6 +249,7 @@ application through a property of the Java class. -->
|
|||
join*,
|
||||
subclass*,
|
||||
loader?,sql-insert?,sql-update?,sql-delete?,
|
||||
fetch-profile*,
|
||||
resultset*,
|
||||
(query|sql-query)*
|
||||
)>
|
||||
|
@ -263,6 +282,7 @@ application through a property of the Java class. -->
|
|||
(property|many-to-one|one-to-one|component|dynamic-component|properties|any|map|set|list|bag|idbag|array|primitive-array)*,
|
||||
joined-subclass*,
|
||||
loader?,sql-insert?,sql-update?,sql-delete?,
|
||||
fetch-profile*,
|
||||
resultset*,
|
||||
(query|sql-query)*
|
||||
)>
|
||||
|
@ -298,6 +318,7 @@ application through a property of the Java class. -->
|
|||
(property|many-to-one|one-to-one|component|dynamic-component|properties|any|map|set|list|bag|idbag|array|primitive-array)*,
|
||||
union-subclass*,
|
||||
loader?,sql-insert?,sql-update?,sql-delete?,
|
||||
fetch-profile*,
|
||||
resultset*,
|
||||
(query|sql-query)*
|
||||
)>
|
||||
|
|
|
@ -198,4 +198,8 @@ public class SessionFactoryStub implements SessionFactory {
|
|||
public FilterDefinition getFilterDefinition(String filterName) throws HibernateException {
|
||||
return getImpl().getFilterDefinition( filterName );
|
||||
}
|
||||
|
||||
public boolean containsFetchProfileDefition(String name) {
|
||||
return getImpl().containsFetchProfileDefition( name );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,281 @@
|
|||
/*
|
||||
* 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.test.fetchprofiles.basic;
|
||||
|
||||
import org.hibernate.junit.functional.FunctionalTestCase;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.UnknownProfileException;
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.engine.SessionImplementor;
|
||||
|
||||
/**
|
||||
* TODO : javadoc
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class BasicFetchProfileTest extends FunctionalTestCase {
|
||||
public BasicFetchProfileTest(String string) {
|
||||
super( string );
|
||||
}
|
||||
|
||||
public String[] getMappings() {
|
||||
return new String[] { "fetchprofiles/basic/Mappings.hbm.xml" };
|
||||
}
|
||||
|
||||
public String getCacheConcurrencyStrategy() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void configure(Configuration cfg) {
|
||||
cfg.setProperty( Environment.GENERATE_STATISTICS, "true" );
|
||||
}
|
||||
|
||||
private static interface TestData {
|
||||
public Long getStudentId();
|
||||
public Long getDepartmentId();
|
||||
public Long getCourseId();
|
||||
public Long getSectionId();
|
||||
public Long getEnrollmentId();
|
||||
}
|
||||
|
||||
private interface TestCode {
|
||||
public void perform(TestData data);
|
||||
}
|
||||
|
||||
private void performWithStandardData(TestCode testCode) {
|
||||
Session session = openSession();
|
||||
session.beginTransaction();
|
||||
final Department literatureDepartment = new Department( "lit", "Literature" );
|
||||
session.save( literatureDepartment );
|
||||
final Course lit101 = new Course( new Course.Code( literatureDepartment, 101 ), "Introduction to Literature" );
|
||||
session.save( lit101 );
|
||||
final CourseOffering section = new CourseOffering( lit101, 1, 2008 );
|
||||
session.save( section );
|
||||
final Student me = new Student( "Steve" );
|
||||
session.save( me );
|
||||
final Enrollment enrollment = new Enrollment( section, me );
|
||||
section.getEnrollments().add( enrollment );
|
||||
session.save( enrollment );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
|
||||
sfi().getStatistics().clear();
|
||||
|
||||
testCode.perform(
|
||||
new TestData() {
|
||||
public Long getStudentId() {
|
||||
return me.getId();
|
||||
}
|
||||
|
||||
public Long getDepartmentId() {
|
||||
return literatureDepartment.getId();
|
||||
}
|
||||
|
||||
public Long getCourseId() {
|
||||
return lit101.getId();
|
||||
}
|
||||
|
||||
public Long getSectionId() {
|
||||
return section.getId();
|
||||
}
|
||||
|
||||
public Long getEnrollmentId() {
|
||||
return enrollment.getId();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
session = openSession();
|
||||
session.beginTransaction();
|
||||
session.delete( enrollment );
|
||||
session.delete( me );
|
||||
session.delete( enrollment.getOffering() );
|
||||
session.delete( enrollment.getOffering().getCourse() );
|
||||
session.delete( enrollment.getOffering().getCourse().getCode().getDepartment() );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
|
||||
public void testNormalLoading() {
|
||||
performWithStandardData(
|
||||
new TestCode() {
|
||||
public void perform(TestData data) {
|
||||
Session session = openSession();
|
||||
session.beginTransaction();
|
||||
CourseOffering section = ( CourseOffering ) session.get( CourseOffering.class, data.getSectionId() );
|
||||
assertEquals( 1, sfi().getStatistics().getEntityLoadCount() );
|
||||
assertEquals( 0, sfi().getStatistics().getEntityFetchCount() );
|
||||
assertFalse( Hibernate.isInitialized( section.getCourse() ) );
|
||||
assertFalse( Hibernate.isInitialized( section.getEnrollments() ) );
|
||||
assertFalse( Hibernate.isInitialized( section.getCourse().getCode().getDepartment() ) );
|
||||
assertTrue( Hibernate.isInitialized( section.getCourse() ) );
|
||||
assertEquals( 1, sfi().getStatistics().getEntityFetchCount() );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public void testNormalCriteria() {
|
||||
performWithStandardData(
|
||||
new TestCode() {
|
||||
public void perform(TestData data) {
|
||||
Session session = openSession();
|
||||
session.beginTransaction();
|
||||
CourseOffering section = ( CourseOffering ) session.createCriteria( CourseOffering.class ).uniqueResult();
|
||||
assertEquals( 1, sfi().getStatistics().getEntityLoadCount() );
|
||||
assertEquals( 0, sfi().getStatistics().getEntityFetchCount() );
|
||||
assertFalse( Hibernate.isInitialized( section.getCourse() ) );
|
||||
assertFalse( Hibernate.isInitialized( section.getEnrollments() ) );
|
||||
assertFalse( Hibernate.isInitialized( section.getCourse().getCode().getDepartment() ) );
|
||||
assertTrue( Hibernate.isInitialized( section.getCourse() ) );
|
||||
assertEquals( 1, sfi().getStatistics().getEntityFetchCount() );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public void testBasicFetchProfileOperation() {
|
||||
assertTrue( "fetch profile not parsed properly", sfi().containsFetchProfileDefition( "enrollment.details" ) );
|
||||
assertTrue( "fetch profile not parsed properly", sfi().containsFetchProfileDefition( "offering.details" ) );
|
||||
assertTrue( "fetch profile not parsed properly", sfi().containsFetchProfileDefition( "course.details" ) );
|
||||
Session s = openSession();
|
||||
SessionImplementor si = ( SessionImplementor ) s;
|
||||
s.enableFetchProfile( "enrollment.details" );
|
||||
assertTrue( si.getLoadQueryInfluencers().hasEnabledFetchProfiles() );
|
||||
s.disableFetchProfile( "enrollment.details" );
|
||||
assertFalse( si.getLoadQueryInfluencers().hasEnabledFetchProfiles() );
|
||||
try {
|
||||
s.enableFetchProfile( "never-gonna-get-it" );
|
||||
fail( "expecting failure on undefined fetch-profile" );
|
||||
}
|
||||
catch ( UnknownProfileException expected ) {
|
||||
}
|
||||
s.close();
|
||||
}
|
||||
|
||||
public void testLoadManyToOneFetchProfile() {
|
||||
performWithStandardData(
|
||||
new TestCode() {
|
||||
public void perform(TestData data) {
|
||||
Session session = openSession();
|
||||
session.beginTransaction();
|
||||
session.enableFetchProfile( "enrollment.details" );
|
||||
Enrollment enrollment = ( Enrollment ) session.get( Enrollment.class, data.getEnrollmentId() );
|
||||
assertEquals( 3, sfi().getStatistics().getEntityLoadCount() ); // enrollment + (section + student)
|
||||
assertEquals( 0, sfi().getStatistics().getEntityFetchCount() );
|
||||
assertTrue( Hibernate.isInitialized( enrollment.getOffering() ) );
|
||||
assertTrue( Hibernate.isInitialized( enrollment.getStudent() ) );
|
||||
assertEquals( 0, sfi().getStatistics().getEntityFetchCount() );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public void testCriteriaManyToOneFetchProfile() {
|
||||
performWithStandardData(
|
||||
new TestCode() {
|
||||
public void perform(TestData data) {
|
||||
Session session = openSession();
|
||||
session.beginTransaction();
|
||||
session.enableFetchProfile( "enrollment.details" );
|
||||
Enrollment enrollment = ( Enrollment ) session.createCriteria( Enrollment.class ).uniqueResult();
|
||||
assertEquals( 3, sfi().getStatistics().getEntityLoadCount() ); // enrollment + (section + student)
|
||||
assertEquals( 0, sfi().getStatistics().getEntityFetchCount() );
|
||||
assertTrue( Hibernate.isInitialized( enrollment.getOffering() ) );
|
||||
assertTrue( Hibernate.isInitialized( enrollment.getStudent() ) );
|
||||
assertEquals( 0, sfi().getStatistics().getEntityFetchCount() );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public void testLoadOneToManyFetchProfile() {
|
||||
performWithStandardData(
|
||||
new TestCode() {
|
||||
public void perform(TestData data) {
|
||||
Session session = openSession();
|
||||
session.beginTransaction();
|
||||
session.enableFetchProfile( "offering.details" );
|
||||
CourseOffering section = ( CourseOffering ) session.get( CourseOffering.class, data.getSectionId() );
|
||||
assertEquals( 3, sfi().getStatistics().getEntityLoadCount() ); // section + (enrollments + course)
|
||||
assertEquals( 0, sfi().getStatistics().getEntityFetchCount() );
|
||||
assertTrue( Hibernate.isInitialized( section.getEnrollments() ) );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public void testLoadDeepFetchProfile() {
|
||||
performWithStandardData(
|
||||
new TestCode() {
|
||||
public void perform(TestData data) {
|
||||
Session session = openSession();
|
||||
session.beginTransaction();
|
||||
// enable both enrollment and offering detail profiles;
|
||||
// then loading the section/offering should fetch the enrollment
|
||||
// which in turn should fetch student (+ offering).
|
||||
session.enableFetchProfile( "offering.details" );
|
||||
session.enableFetchProfile( "enrollment.details" );
|
||||
CourseOffering section = ( CourseOffering ) session.get( CourseOffering.class, data.getSectionId() );
|
||||
assertEquals( 4, sfi().getStatistics().getEntityLoadCount() ); // section + (course + enrollments + (student))
|
||||
assertEquals( 0, sfi().getStatistics().getEntityFetchCount() );
|
||||
assertTrue( Hibernate.isInitialized( section.getEnrollments() ) );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public void testLoadComponentDerefFetchProfile() {
|
||||
performWithStandardData(
|
||||
new TestCode() {
|
||||
public void perform(TestData data) {
|
||||
Session session = openSession();
|
||||
session.beginTransaction();
|
||||
session.enableFetchProfile( "course.details" );
|
||||
Course course = ( Course ) session.get( Course.class, data.getCourseId() );
|
||||
assertEquals( 2, sfi().getStatistics().getEntityLoadCount() ); // course + department
|
||||
assertEquals( 0, sfi().getStatistics().getEntityFetchCount() );
|
||||
assertTrue( Hibernate.isInitialized( course.getCode().getDepartment() ) );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* 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.test.fetchprofiles.basic;
|
||||
|
||||
/**
|
||||
* TODO : javadoc
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class Course {
|
||||
private Long id;
|
||||
private Code code;
|
||||
private String name;
|
||||
|
||||
public Course() {
|
||||
}
|
||||
|
||||
public Course(Code code, String name) {
|
||||
this.code = code;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Code getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(Code code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
public static class Code {
|
||||
private Department department;
|
||||
private int number;
|
||||
|
||||
public Code() {
|
||||
}
|
||||
|
||||
public Code(Department department, int number) {
|
||||
this.department = department;
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
public Department getDepartment() {
|
||||
return department;
|
||||
}
|
||||
|
||||
public void setDepartment(Department department) {
|
||||
this.department = department;
|
||||
}
|
||||
|
||||
public int getNumber() {
|
||||
return number;
|
||||
}
|
||||
|
||||
public void setNumber(int number) {
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if ( this == o ) {
|
||||
return true;
|
||||
}
|
||||
if ( !( o instanceof Code ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Code code = ( Code ) o;
|
||||
|
||||
if ( number != code.number ) {
|
||||
return false;
|
||||
}
|
||||
if ( !department.equals( code.department ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int result;
|
||||
result = department.hashCode();
|
||||
result = 31 * result + number;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* 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.test.fetchprofiles.basic;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
|
||||
/**
|
||||
* TODO : javadoc
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class CourseOffering {
|
||||
private Long id;
|
||||
private Course course;
|
||||
private int semester;
|
||||
private int year;
|
||||
private Set enrollments = new HashSet();
|
||||
|
||||
public CourseOffering() {
|
||||
}
|
||||
|
||||
public CourseOffering(Course course, int semester, int year) {
|
||||
this.course = course;
|
||||
this.semester = semester;
|
||||
this.year = year;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Course getCourse() {
|
||||
return course;
|
||||
}
|
||||
|
||||
public void setCourse(Course course) {
|
||||
this.course = course;
|
||||
}
|
||||
|
||||
public int getSemester() {
|
||||
return semester;
|
||||
}
|
||||
|
||||
public void setSemester(int semester) {
|
||||
this.semester = semester;
|
||||
}
|
||||
|
||||
public int getYear() {
|
||||
return year;
|
||||
}
|
||||
|
||||
public void setYear(int year) {
|
||||
this.year = year;
|
||||
}
|
||||
|
||||
public Set getEnrollments() {
|
||||
return enrollments;
|
||||
}
|
||||
|
||||
public void setEnrollments(Set enrollments) {
|
||||
this.enrollments = enrollments;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* 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.test.fetchprofiles.basic;
|
||||
|
||||
/**
|
||||
* TODO : javadoc
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class Department {
|
||||
private Long id;
|
||||
private String code;
|
||||
private String name;
|
||||
|
||||
public Department() {
|
||||
}
|
||||
|
||||
public Department(String code, String name) {
|
||||
this.code = code;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* 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.test.fetchprofiles.basic;
|
||||
|
||||
/**
|
||||
* TODO : javadoc
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class Enrollment {
|
||||
private Long id;
|
||||
private CourseOffering offering;
|
||||
private Student student;
|
||||
private int finalGrade;
|
||||
|
||||
public Enrollment() {
|
||||
}
|
||||
|
||||
public Enrollment(CourseOffering offering, Student student) {
|
||||
this.offering = offering;
|
||||
this.student = student;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public CourseOffering getOffering() {
|
||||
return offering;
|
||||
}
|
||||
|
||||
public void setOffering(CourseOffering offering) {
|
||||
this.offering = offering;
|
||||
}
|
||||
|
||||
public Student getStudent() {
|
||||
return student;
|
||||
}
|
||||
|
||||
public void setStudent(Student student) {
|
||||
this.student = student;
|
||||
}
|
||||
|
||||
public int getFinalGrade() {
|
||||
return finalGrade;
|
||||
}
|
||||
|
||||
public void setFinalGrade(int finalGrade) {
|
||||
this.finalGrade = finalGrade;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE hibernate-mapping
|
||||
SYSTEM
|
||||
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
|
||||
|
||||
<!--
|
||||
~ 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
|
||||
~
|
||||
-->
|
||||
|
||||
<hibernate-mapping package="org.hibernate.test.fetchprofiles.basic">
|
||||
|
||||
<class name="Department">
|
||||
<id name="id" type="long">
|
||||
<generator class="increment"/>
|
||||
</id>
|
||||
<property name="code" column="CODE" type="string"/>
|
||||
<property name="name" column="NAME" type="string"/>
|
||||
</class>
|
||||
|
||||
<class name="Student">
|
||||
<id name="id" type="long">
|
||||
<generator class="increment"/>
|
||||
</id>
|
||||
<property name="name" column="NAME" type="string"/>
|
||||
</class>
|
||||
|
||||
<class name="Course">
|
||||
<id name="id" type="long">
|
||||
<generator class="increment"/>
|
||||
</id>
|
||||
<property name="name" column="NAME" type="string"/>
|
||||
<component name="code" class="Course$Code">
|
||||
<many-to-one name="department" class="Department" column="DEPT_ID" cascade="save-update"/>
|
||||
<property name="number" type="int" column="CODE_NUMBER"/>
|
||||
</component>
|
||||
<fetch-profile name="course.details">
|
||||
<fetch association="code.department" style="join"/>
|
||||
</fetch-profile>
|
||||
</class>
|
||||
|
||||
<class name="CourseOffering" table="SECTION">
|
||||
<id name="id" type="long">
|
||||
<generator class="increment"/>
|
||||
</id>
|
||||
<many-to-one name="course" column="COURSE_ID" class="Course"/>
|
||||
<property name="semester" type="int" column="SEMESTER"/>
|
||||
<property name="year" type="int" column="YEAR"/>
|
||||
<set name="enrollments" lazy="true" cascade="all">
|
||||
<key column="SECTION_ID"/>
|
||||
<one-to-many class="Enrollment"/>
|
||||
</set>
|
||||
<fetch-profile name="offering.details">
|
||||
<fetch association="enrollments" style="join"/>
|
||||
<fetch association="course" style="join"/>
|
||||
</fetch-profile>
|
||||
<fetch-profile name="offering.details2">
|
||||
<fetch entity="CourseOffering" association="enrollments" style="join"/>
|
||||
</fetch-profile>
|
||||
</class>
|
||||
|
||||
<class name="Enrollment">
|
||||
<id name="id" type="long">
|
||||
<generator class="increment"/>
|
||||
</id>
|
||||
<many-to-one name="offering" column="SECTION_ID" class="CourseOffering" cascade="none"/>
|
||||
<many-to-one name="student" column="STUDENT_ID" class="Student" cascade="none"/>
|
||||
<property name="finalGrade" column="FINAL_GRADE" type="int"/>
|
||||
</class>
|
||||
|
||||
<fetch-profile name="enrollment.details">
|
||||
<fetch entity="Enrollment" association="student" style="join"/>
|
||||
<fetch entity="Enrollment" association="offering" style="join"/>
|
||||
</fetch-profile>
|
||||
|
||||
</hibernate-mapping>
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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.test.fetchprofiles.basic;
|
||||
|
||||
/**
|
||||
* TODO : javadoc
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class Student {
|
||||
private Long id;
|
||||
private String name;
|
||||
|
||||
public Student() {
|
||||
}
|
||||
|
||||
public Student(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
|
@ -22,6 +22,7 @@ hibernate.connection.isolation ${jdbc.isolation}
|
|||
|
||||
hibernate.connection.pool_size 5
|
||||
|
||||
hibernate.show_sql true
|
||||
hibernate.format_sql true
|
||||
|
||||
hibernate.max_fetch_depth 5
|
||||
|
|
Loading…
Reference in New Issue