HHH-13763 : Update all load-by-key handling to use SQL AST
* dropped `org.hibernate.loader.plan` * dropped `org.hibernate.loader.custom` (*) * dropped `org.hibernate.loader.collection` * dropped `org.hibernate.loader.entity` * dropped `org.hibernate.loader.hql` * cleaned-up `org.hibernate.loader`
This commit is contained in:
parent
10cdb47a97
commit
0a41ac8466
|
@ -18,7 +18,7 @@ import org.hibernate.dialect.H2Dialect;
|
||||||
import org.hibernate.dialect.Oracle8iDialect;
|
import org.hibernate.dialect.Oracle8iDialect;
|
||||||
import org.hibernate.dialect.PostgreSQL82Dialect;
|
import org.hibernate.dialect.PostgreSQL82Dialect;
|
||||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||||
import org.hibernate.loader.custom.NonUniqueDiscoveredSqlAliasException;
|
import org.hibernate.loader.NonUniqueDiscoveredSqlAliasException;
|
||||||
import org.hibernate.transform.DistinctRootEntityResultTransformer;
|
import org.hibernate.transform.DistinctRootEntityResultTransformer;
|
||||||
import org.hibernate.transform.RootEntityResultTransformer;
|
import org.hibernate.transform.RootEntityResultTransformer;
|
||||||
import org.hibernate.transform.Transformers;
|
import org.hibernate.transform.Transformers;
|
||||||
|
|
|
@ -288,7 +288,10 @@ public interface SessionFactoryBuilder {
|
||||||
* @return {@code this}, for method chaining
|
* @return {@code this}, for method chaining
|
||||||
*
|
*
|
||||||
* @see org.hibernate.cfg.AvailableSettings#BATCH_FETCH_STYLE
|
* @see org.hibernate.cfg.AvailableSettings#BATCH_FETCH_STYLE
|
||||||
|
*
|
||||||
|
* @deprecated (since 6.0) : an appropriate style is selected
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
SessionFactoryBuilder applyBatchFetchStyle(BatchFetchStyle style);
|
SessionFactoryBuilder applyBatchFetchStyle(BatchFetchStyle style);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1291,6 +1291,10 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
|
||||||
this.tempTableDdlTransactionHandling = handling;
|
this.tempTableDdlTransactionHandling = handling;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated (since 6.0) : No longer used internally
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public void applyBatchFetchStyle(BatchFetchStyle style) {
|
public void applyBatchFetchStyle(BatchFetchStyle style) {
|
||||||
this.batchFetchStyle = style;
|
this.batchFetchStyle = style;
|
||||||
}
|
}
|
||||||
|
|
|
@ -168,6 +168,10 @@ public interface SessionFactoryOptions {
|
||||||
|
|
||||||
TempTableDdlTransactionHandling getTempTableDdlTransactionHandling();
|
TempTableDdlTransactionHandling getTempTableDdlTransactionHandling();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated (since 6.0) : No longer used internally
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
BatchFetchStyle getBatchFetchStyle();
|
BatchFetchStyle getBatchFetchStyle();
|
||||||
|
|
||||||
boolean isDelayBatchFetchLoaderCreationsEnabled();
|
boolean isDelayBatchFetchLoaderCreationsEnabled();
|
||||||
|
|
|
@ -1689,7 +1689,10 @@ public interface AvailableSettings extends org.hibernate.jpa.AvailableSettings {
|
||||||
* {@link org.hibernate.loader.BatchFetchStyle} instance.
|
* {@link org.hibernate.loader.BatchFetchStyle} instance.
|
||||||
*
|
*
|
||||||
* {@code LEGACY} is the default value.
|
* {@code LEGACY} is the default value.
|
||||||
|
*
|
||||||
|
* @deprecated (since 6.0) : An appropriate batch-fetch style is selected automatically
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
String BATCH_FETCH_STYLE = "hibernate.batch_fetch_style";
|
String BATCH_FETCH_STYLE = "hibernate.batch_fetch_style";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -169,6 +169,10 @@ public final class Settings {
|
||||||
return sessionFactoryOptions.isInitializeLazyStateOutsideTransactionsEnabled();
|
return sessionFactoryOptions.isInitializeLazyStateOutsideTransactionsEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated (since 6.0) : No longer used internally
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public BatchFetchStyle getBatchFetchStyle() {
|
public BatchFetchStyle getBatchFetchStyle() {
|
||||||
return sessionFactoryOptions.getBatchFetchStyle();
|
return sessionFactoryOptions.getBatchFetchStyle();
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,6 @@ package org.hibernate.collection.internal;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -19,7 +17,6 @@ import org.hibernate.HibernateException;
|
||||||
import org.hibernate.engine.spi.SessionImplementor;
|
import org.hibernate.engine.spi.SessionImplementor;
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
import org.hibernate.internal.CoreMessageLogger;
|
import org.hibernate.internal.CoreMessageLogger;
|
||||||
import org.hibernate.loader.CollectionAliases;
|
|
||||||
import org.hibernate.persister.collection.CollectionPersister;
|
import org.hibernate.persister.collection.CollectionPersister;
|
||||||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||||
|
@ -179,19 +176,6 @@ public class PersistentArrayHolder extends AbstractPersistentCollection {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public Object readFrom(ResultSet rs, CollectionPersister persister, CollectionAliases descriptor, Object owner)
|
|
||||||
throws HibernateException, SQLException {
|
|
||||||
final Object element = persister.readElement( rs, owner, descriptor.getSuffixedElementAliases(), getSession() );
|
|
||||||
final int index = (Integer) persister.readIndex( rs, descriptor.getSuffixedIndexAliases(), getSession() );
|
|
||||||
for ( int i = tempList.size(); i<=index; i++) {
|
|
||||||
tempList.add( i, null );
|
|
||||||
}
|
|
||||||
tempList.set( index, element );
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object readFrom(
|
public Object readFrom(
|
||||||
RowProcessingState rowProcessingState,
|
RowProcessingState rowProcessingState,
|
||||||
|
|
|
@ -7,8 +7,6 @@
|
||||||
package org.hibernate.collection.internal;
|
package org.hibernate.collection.internal;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -21,7 +19,6 @@ import java.util.Map;
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.HibernateException;
|
||||||
import org.hibernate.engine.spi.SessionImplementor;
|
import org.hibernate.engine.spi.SessionImplementor;
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
import org.hibernate.loader.CollectionAliases;
|
|
||||||
import org.hibernate.persister.collection.CollectionPersister;
|
import org.hibernate.persister.collection.CollectionPersister;
|
||||||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||||
|
@ -124,19 +121,6 @@ public class PersistentBag extends AbstractPersistentCollection implements List
|
||||||
return bag.iterator();
|
return bag.iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public Object readFrom(ResultSet rs, CollectionPersister persister, CollectionAliases descriptor, Object owner)
|
|
||||||
throws HibernateException, SQLException {
|
|
||||||
// note that if we load this collection from a cartesian product
|
|
||||||
// the multiplicity would be broken ... so use an idbag instead
|
|
||||||
final Object element = persister.readElement( rs, owner, descriptor.getSuffixedElementAliases(), getSession() );
|
|
||||||
if ( element != null ) {
|
|
||||||
bag.add( element );
|
|
||||||
}
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object readFrom(
|
public Object readFrom(
|
||||||
RowProcessingState rowProcessingState,
|
RowProcessingState rowProcessingState,
|
||||||
|
|
|
@ -7,8 +7,6 @@
|
||||||
package org.hibernate.collection.internal;
|
package org.hibernate.collection.internal;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -20,7 +18,6 @@ import java.util.Map;
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.HibernateException;
|
||||||
import org.hibernate.engine.spi.SessionImplementor;
|
import org.hibernate.engine.spi.SessionImplementor;
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
import org.hibernate.loader.CollectionAliases;
|
|
||||||
import org.hibernate.persister.collection.CollectionPersister;
|
import org.hibernate.persister.collection.CollectionPersister;
|
||||||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||||
|
@ -354,25 +351,6 @@ public class PersistentIdentifierBag extends AbstractPersistentCollection implem
|
||||||
return old != null && elemType.isDirty( old, entry, getSession() );
|
return old != null && elemType.isDirty( old, entry, getSession() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object readFrom(
|
|
||||||
ResultSet rs,
|
|
||||||
CollectionPersister persister,
|
|
||||||
CollectionAliases descriptor,
|
|
||||||
Object owner) throws HibernateException, SQLException {
|
|
||||||
final Object element = persister.readElement( rs, owner, descriptor.getSuffixedElementAliases(), getSession() );
|
|
||||||
final Object old = identifiers.put(
|
|
||||||
values.size(),
|
|
||||||
persister.readIdentifier( rs, descriptor.getSuffixedIdentifierAlias(), getSession() )
|
|
||||||
);
|
|
||||||
|
|
||||||
if ( old == null ) {
|
|
||||||
//maintain correct duplication if loaded in a cartesian product
|
|
||||||
values.add( element );
|
|
||||||
}
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object readFrom(
|
public Object readFrom(
|
||||||
RowProcessingState rowProcessingState,
|
RowProcessingState rowProcessingState,
|
||||||
|
|
|
@ -7,8 +7,6 @@
|
||||||
package org.hibernate.collection.internal;
|
package org.hibernate.collection.internal;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
@ -18,7 +16,6 @@ import java.util.ListIterator;
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.HibernateException;
|
||||||
import org.hibernate.engine.spi.SessionImplementor;
|
import org.hibernate.engine.spi.SessionImplementor;
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
import org.hibernate.loader.CollectionAliases;
|
|
||||||
import org.hibernate.persister.collection.CollectionPersister;
|
import org.hibernate.persister.collection.CollectionPersister;
|
||||||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||||
|
@ -396,22 +393,6 @@ public class PersistentList extends AbstractPersistentCollection implements List
|
||||||
return list.toString();
|
return list.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public Object readFrom(ResultSet rs, CollectionPersister persister, CollectionAliases descriptor, Object owner)
|
|
||||||
throws HibernateException, SQLException {
|
|
||||||
final Object element = persister.readElement( rs, owner, descriptor.getSuffixedElementAliases(), getSession() ) ;
|
|
||||||
final int index = (Integer) persister.readIndex( rs, descriptor.getSuffixedIndexAliases(), getSession() );
|
|
||||||
|
|
||||||
//pad with nulls from the current last element up to the new index
|
|
||||||
for ( int i = list.size(); i<=index; i++) {
|
|
||||||
list.add( i, null );
|
|
||||||
}
|
|
||||||
|
|
||||||
list.set( index, element );
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object readFrom(
|
public Object readFrom(
|
||||||
RowProcessingState rowProcessingState,
|
RowProcessingState rowProcessingState,
|
||||||
|
|
|
@ -7,8 +7,6 @@
|
||||||
package org.hibernate.collection.internal;
|
package org.hibernate.collection.internal;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -20,7 +18,6 @@ import java.util.Set;
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.HibernateException;
|
||||||
import org.hibernate.engine.spi.SessionImplementor;
|
import org.hibernate.engine.spi.SessionImplementor;
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
import org.hibernate.loader.CollectionAliases;
|
|
||||||
import org.hibernate.persister.collection.CollectionPersister;
|
import org.hibernate.persister.collection.CollectionPersister;
|
||||||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||||
|
@ -279,23 +276,6 @@ public class PersistentMap extends AbstractPersistentCollection implements Map {
|
||||||
|
|
||||||
private transient List<Object[]> loadingEntries;
|
private transient List<Object[]> loadingEntries;
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object readFrom(
|
|
||||||
ResultSet rs,
|
|
||||||
CollectionPersister persister,
|
|
||||||
CollectionAliases descriptor,
|
|
||||||
Object owner) throws HibernateException, SQLException {
|
|
||||||
final Object element = persister.readElement( rs, owner, descriptor.getSuffixedElementAliases(), getSession() );
|
|
||||||
if ( element != null ) {
|
|
||||||
final Object index = persister.readIndex( rs, descriptor.getSuffixedIndexAliases(), getSession() );
|
|
||||||
if ( loadingEntries == null ) {
|
|
||||||
loadingEntries = new ArrayList<>();
|
|
||||||
}
|
|
||||||
loadingEntries.add( new Object[] { index, element } );
|
|
||||||
}
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object readFrom(
|
public Object readFrom(
|
||||||
RowProcessingState rowProcessingState,
|
RowProcessingState rowProcessingState,
|
||||||
|
|
|
@ -7,8 +7,6 @@
|
||||||
package org.hibernate.collection.internal;
|
package org.hibernate.collection.internal;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -19,7 +17,6 @@ import java.util.Set;
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.HibernateException;
|
||||||
import org.hibernate.engine.spi.SessionImplementor;
|
import org.hibernate.engine.spi.SessionImplementor;
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
import org.hibernate.loader.CollectionAliases;
|
|
||||||
import org.hibernate.persister.collection.CollectionPersister;
|
import org.hibernate.persister.collection.CollectionPersister;
|
||||||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||||
|
@ -336,20 +333,6 @@ public class PersistentSet extends AbstractPersistentCollection implements java.
|
||||||
return set.toString();
|
return set.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public Object readFrom(
|
|
||||||
ResultSet rs,
|
|
||||||
CollectionPersister persister,
|
|
||||||
CollectionAliases descriptor,
|
|
||||||
Object owner) throws HibernateException, SQLException {
|
|
||||||
final Object element = persister.readElement( rs, owner, descriptor.getSuffixedElementAliases(), getSession() );
|
|
||||||
if ( element != null ) {
|
|
||||||
tempList.add( element );
|
|
||||||
}
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object readFrom(
|
public Object readFrom(
|
||||||
RowProcessingState rowProcessingState,
|
RowProcessingState rowProcessingState,
|
||||||
|
|
|
@ -7,14 +7,11 @@
|
||||||
package org.hibernate.collection.spi;
|
package org.hibernate.collection.spi;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.HibernateException;
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
import org.hibernate.loader.CollectionAliases;
|
|
||||||
import org.hibernate.persister.collection.CollectionPersister;
|
import org.hibernate.persister.collection.CollectionPersister;
|
||||||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||||
|
@ -158,21 +155,10 @@ public interface PersistentCollection {
|
||||||
Iterator entries(CollectionPersister persister);
|
Iterator entries(CollectionPersister persister);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a row from the JDBC result set
|
* Read a row from the JDBC values
|
||||||
*
|
|
||||||
* @param rs The JDBC ResultSet
|
|
||||||
* @param role The collection role
|
|
||||||
* @param descriptor The aliases used for the columns making up the collection
|
|
||||||
* @param owner The collection owner
|
|
||||||
*
|
|
||||||
* @return The read object
|
|
||||||
*
|
*
|
||||||
* @throws HibernateException Generally indicates a problem resolving data read from the ResultSet
|
* @throws HibernateException Generally indicates a problem resolving data read from the ResultSet
|
||||||
* @throws SQLException Indicates a problem accessing the ResultSet
|
|
||||||
*/
|
*/
|
||||||
Object readFrom(ResultSet rs, CollectionPersister role, CollectionAliases descriptor, Object owner)
|
|
||||||
throws HibernateException, SQLException;
|
|
||||||
|
|
||||||
Object readFrom(
|
Object readFrom(
|
||||||
RowProcessingState rowProcessingState,
|
RowProcessingState rowProcessingState,
|
||||||
DomainResultAssembler elementAssembler,
|
DomainResultAssembler elementAssembler,
|
||||||
|
|
|
@ -1,126 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.engine.query.spi;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.sql.PreparedStatement;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
|
||||||
import org.hibernate.action.internal.BulkOperationCleanupAction;
|
|
||||||
import org.hibernate.engine.spi.QueryParameters;
|
|
||||||
import org.hibernate.engine.spi.RowSelection;
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|
||||||
import org.hibernate.event.spi.EventSource;
|
|
||||||
import org.hibernate.internal.CoreLogging;
|
|
||||||
import org.hibernate.internal.CoreMessageLogger;
|
|
||||||
import org.hibernate.loader.custom.CustomQuery;
|
|
||||||
import org.hibernate.param.ParameterBinder;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines a query execution plan for a native-SQL query.
|
|
||||||
*
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public class NativeSQLQueryPlan implements Serializable {
|
|
||||||
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( NativeSQLQueryPlan.class );
|
|
||||||
|
|
||||||
private final String sourceQuery;
|
|
||||||
private final CustomQuery customQuery;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a NativeSQLQueryPlan.
|
|
||||||
*
|
|
||||||
* @param sourceQuery The original native query to create a plan for
|
|
||||||
* @param customQuery The query executed via this plan
|
|
||||||
*/
|
|
||||||
public NativeSQLQueryPlan(String sourceQuery, CustomQuery customQuery) {
|
|
||||||
this.sourceQuery = sourceQuery;
|
|
||||||
this.customQuery = customQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getSourceQuery() {
|
|
||||||
return sourceQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CustomQuery getCustomQuery() {
|
|
||||||
return customQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void coordinateSharedCacheCleanup(SharedSessionContractImplementor session) {
|
|
||||||
final BulkOperationCleanupAction action = new BulkOperationCleanupAction( session, getCustomQuery().getQuerySpaces() );
|
|
||||||
|
|
||||||
if ( session.isEventSource() ) {
|
|
||||||
( (EventSource) session ).getActionQueue().addAction( action );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
action.getAfterTransactionCompletionProcess().doAfterTransactionCompletion( true, session );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs the execute query
|
|
||||||
*
|
|
||||||
* @param queryParameters The query parameters
|
|
||||||
* @param session The session
|
|
||||||
*
|
|
||||||
* @return The number of affected rows as returned by the JDBC driver
|
|
||||||
*
|
|
||||||
* @throws HibernateException Indicates a problem performing the query execution
|
|
||||||
*/
|
|
||||||
public int performExecuteUpdate(
|
|
||||||
QueryParameters queryParameters,
|
|
||||||
SharedSessionContractImplementor session) throws HibernateException {
|
|
||||||
|
|
||||||
coordinateSharedCacheCleanup( session );
|
|
||||||
|
|
||||||
if ( queryParameters.isCallable() ) {
|
|
||||||
throw new IllegalArgumentException("callable not yet supported for native queries");
|
|
||||||
}
|
|
||||||
|
|
||||||
int result = 0;
|
|
||||||
PreparedStatement ps;
|
|
||||||
RowSelection selection = queryParameters.getRowSelection();
|
|
||||||
try {
|
|
||||||
queryParameters.processFilters( this.customQuery.getSQL(), session );
|
|
||||||
final String sql = session.getJdbcServices().getDialect()
|
|
||||||
.addSqlHintOrComment(
|
|
||||||
queryParameters.getFilteredSQL(),
|
|
||||||
queryParameters,
|
|
||||||
session.getFactory().getSessionFactoryOptions().isCommentsEnabled()
|
|
||||||
);
|
|
||||||
|
|
||||||
ps = session.getJdbcCoordinator().getStatementPreparer().prepareStatement( sql, false );
|
|
||||||
|
|
||||||
try {
|
|
||||||
int col = 1;
|
|
||||||
for ( ParameterBinder binder : this.customQuery.getParameterValueBinders() ) {
|
|
||||||
col += binder.bind( ps, queryParameters, session, col );
|
|
||||||
}
|
|
||||||
if ( selection != null && selection.getTimeout() != null ) {
|
|
||||||
ps.setQueryTimeout( selection.getTimeout() );
|
|
||||||
}
|
|
||||||
result = session.getJdbcCoordinator().getResultSetReturn().executeUpdate( ps );
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
if ( ps != null ) {
|
|
||||||
session.getJdbcCoordinator().getLogicalConnection().getResourceRegistry().release( ps );
|
|
||||||
session.getJdbcCoordinator().afterStatementExecution();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (SQLException sqle) {
|
|
||||||
throw session.getFactory().getSQLExceptionHelper().convert(
|
|
||||||
sqle,
|
|
||||||
"could not execute native bulk manipulation query",
|
|
||||||
this.sourceQuery
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -52,7 +52,6 @@ import org.hibernate.engine.query.spi.sql.NativeSQLQuerySpecification;
|
||||||
import org.hibernate.graph.spi.RootGraphImplementor;
|
import org.hibernate.graph.spi.RootGraphImplementor;
|
||||||
import org.hibernate.jdbc.ReturningWork;
|
import org.hibernate.jdbc.ReturningWork;
|
||||||
import org.hibernate.jdbc.Work;
|
import org.hibernate.jdbc.Work;
|
||||||
import org.hibernate.loader.custom.CustomQuery;
|
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
import org.hibernate.procedure.ProcedureCall;
|
import org.hibernate.procedure.ProcedureCall;
|
||||||
import org.hibernate.query.spi.QueryImplementor;
|
import org.hibernate.query.spi.QueryImplementor;
|
||||||
|
@ -192,16 +191,6 @@ public class SessionDelegatorBaseImpl implements SessionImplementor {
|
||||||
return delegate.instantiate( entityName, id );
|
return delegate.instantiate( entityName, id );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public List listCustomQuery(CustomQuery customQuery, QueryParameters queryParameters) throws HibernateException {
|
|
||||||
return delegate.listCustomQuery( customQuery, queryParameters );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ScrollableResultsImplementor scrollCustomQuery(CustomQuery customQuery, QueryParameters queryParameters) throws HibernateException {
|
|
||||||
return delegate.scrollCustomQuery( customQuery, queryParameters );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List list(NativeSQLQuerySpecification spec, QueryParameters queryParameters) throws HibernateException {
|
public List list(NativeSQLQuerySpecification spec, QueryParameters queryParameters) throws HibernateException {
|
||||||
return delegate.list( spec, queryParameters );
|
return delegate.list( spec, queryParameters );
|
||||||
|
|
|
@ -27,7 +27,6 @@ import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
|
||||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||||
import org.hibernate.engine.query.spi.sql.NativeSQLQuerySpecification;
|
import org.hibernate.engine.query.spi.sql.NativeSQLQuerySpecification;
|
||||||
import org.hibernate.internal.util.config.ConfigurationHelper;
|
import org.hibernate.internal.util.config.ConfigurationHelper;
|
||||||
import org.hibernate.loader.custom.CustomQuery;
|
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
import org.hibernate.query.spi.QueryProducerImplementor;
|
import org.hibernate.query.spi.QueryProducerImplementor;
|
||||||
import org.hibernate.query.spi.ScrollableResultsImplementor;
|
import org.hibernate.query.spi.ScrollableResultsImplementor;
|
||||||
|
@ -303,18 +302,6 @@ public interface SharedSessionContractImplementor
|
||||||
*/
|
*/
|
||||||
Object instantiate(String entityName, Serializable id) throws HibernateException;
|
Object instantiate(String entityName, Serializable id) throws HibernateException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute an SQL Query
|
|
||||||
*/
|
|
||||||
List listCustomQuery(CustomQuery customQuery, QueryParameters queryParameters)
|
|
||||||
throws HibernateException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute an SQL Query
|
|
||||||
*/
|
|
||||||
ScrollableResultsImplementor scrollCustomQuery(CustomQuery customQuery, QueryParameters queryParameters)
|
|
||||||
throws HibernateException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute a native SQL query, and return the results as a fully built list.
|
* Execute a native SQL query, and return the results as a fully built list.
|
||||||
*
|
*
|
||||||
|
|
|
@ -48,7 +48,6 @@ import org.hibernate.LockOptions;
|
||||||
import org.hibernate.MappingException;
|
import org.hibernate.MappingException;
|
||||||
import org.hibernate.MultiIdentifierLoadAccess;
|
import org.hibernate.MultiIdentifierLoadAccess;
|
||||||
import org.hibernate.NaturalIdLoadAccess;
|
import org.hibernate.NaturalIdLoadAccess;
|
||||||
import org.hibernate.NotYetImplementedFor6Exception;
|
|
||||||
import org.hibernate.ObjectDeletedException;
|
import org.hibernate.ObjectDeletedException;
|
||||||
import org.hibernate.ObjectNotFoundException;
|
import org.hibernate.ObjectNotFoundException;
|
||||||
import org.hibernate.ReplicationMode;
|
import org.hibernate.ReplicationMode;
|
||||||
|
@ -73,7 +72,6 @@ import org.hibernate.engine.spi.EntityEntry;
|
||||||
import org.hibernate.engine.spi.EntityKey;
|
import org.hibernate.engine.spi.EntityKey;
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||||
import org.hibernate.engine.spi.PersistenceContext;
|
import org.hibernate.engine.spi.PersistenceContext;
|
||||||
import org.hibernate.engine.spi.QueryParameters;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.engine.spi.SessionImplementor;
|
import org.hibernate.engine.spi.SessionImplementor;
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
|
@ -127,7 +125,6 @@ import org.hibernate.jpa.internal.util.ConfigurationHelper;
|
||||||
import org.hibernate.jpa.internal.util.FlushModeTypeHelper;
|
import org.hibernate.jpa.internal.util.FlushModeTypeHelper;
|
||||||
import org.hibernate.jpa.internal.util.LockModeTypeHelper;
|
import org.hibernate.jpa.internal.util.LockModeTypeHelper;
|
||||||
import org.hibernate.jpa.internal.util.LockOptionsHelper;
|
import org.hibernate.jpa.internal.util.LockOptionsHelper;
|
||||||
import org.hibernate.loader.custom.CustomQuery;
|
|
||||||
import org.hibernate.metamodel.spi.MetamodelImplementor;
|
import org.hibernate.metamodel.spi.MetamodelImplementor;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
import org.hibernate.persister.entity.MultiLoadOptions;
|
import org.hibernate.persister.entity.MultiLoadOptions;
|
||||||
|
@ -138,7 +135,6 @@ import org.hibernate.proxy.HibernateProxy;
|
||||||
import org.hibernate.proxy.LazyInitializer;
|
import org.hibernate.proxy.LazyInitializer;
|
||||||
import org.hibernate.query.Query;
|
import org.hibernate.query.Query;
|
||||||
import org.hibernate.query.UnknownSqlResultSetMappingException;
|
import org.hibernate.query.UnknownSqlResultSetMappingException;
|
||||||
import org.hibernate.query.spi.ScrollableResultsImplementor;
|
|
||||||
import org.hibernate.resource.transaction.TransactionRequiredForJoinException;
|
import org.hibernate.resource.transaction.TransactionRequiredForJoinException;
|
||||||
import org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorImpl;
|
import org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorImpl;
|
||||||
import org.hibernate.resource.transaction.spi.TransactionCoordinator;
|
import org.hibernate.resource.transaction.spi.TransactionCoordinator;
|
||||||
|
@ -1584,61 +1580,6 @@ public final class SessionImpl
|
||||||
return super.createStoredProcedureCall( procedureName, resultClasses );
|
return super.createStoredProcedureCall( procedureName, resultClasses );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public ScrollableResultsImplementor scrollCustomQuery(CustomQuery customQuery, QueryParameters queryParameters) {
|
|
||||||
throw new NotYetImplementedFor6Exception( getClass() );
|
|
||||||
|
|
||||||
// checkOpenOrWaitingForAutoClose();
|
|
||||||
//// checkTransactionSynchStatus();
|
|
||||||
//
|
|
||||||
// if ( log.isTraceEnabled() ) {
|
|
||||||
// log.tracev( "Scroll SQL query: {0}", customQuery.getSQL() );
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// CustomLoader loader = getFactory().getQueryPlanCache().getNativeQueryInterpreter().createCustomLoader( customQuery, getFactory() );
|
|
||||||
//
|
|
||||||
// autoFlushIfRequired( loader.getQuerySpaces() );
|
|
||||||
//
|
|
||||||
// dontFlushFromFind++; //stops flush being called multiple times if this method is recursively called
|
|
||||||
// try {
|
|
||||||
// return loader.scroll( queryParameters, this );
|
|
||||||
// }
|
|
||||||
// finally {
|
|
||||||
// delayedAfterCompletion();
|
|
||||||
// dontFlushFromFind--;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
// basically just an adapted copy of find(CriteriaImpl)
|
|
||||||
@Override
|
|
||||||
public List listCustomQuery(CustomQuery customQuery, QueryParameters queryParameters) {
|
|
||||||
throw new NotYetImplementedFor6Exception( getClass() );
|
|
||||||
|
|
||||||
// checkOpenOrWaitingForAutoClose();
|
|
||||||
//// checkTransactionSynchStatus();
|
|
||||||
//
|
|
||||||
// if ( log.isTraceEnabled() ) {
|
|
||||||
// log.tracev( "SQL query: {0}", customQuery.getSQL() );
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// CustomLoader loader = getFactory().getQueryPlanCache().getNativeQueryInterpreter().createCustomLoader( customQuery, getFactory() );
|
|
||||||
//
|
|
||||||
// autoFlushIfRequired( loader.getQuerySpaces() );
|
|
||||||
//
|
|
||||||
// dontFlushFromFind++;
|
|
||||||
// boolean success = false;
|
|
||||||
// try {
|
|
||||||
// List results = loader.list( this, queryParameters );
|
|
||||||
// success = true;
|
|
||||||
// return results;
|
|
||||||
// }
|
|
||||||
// finally {
|
|
||||||
// dontFlushFromFind--;
|
|
||||||
// delayedAfterCompletion();
|
|
||||||
// afterOperation( success );
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SessionFactoryImplementor getSessionFactory() {
|
public SessionFactoryImplementor getSessionFactory() {
|
||||||
// checkTransactionSynchStatus();
|
// checkTransactionSynchStatus();
|
||||||
|
|
|
@ -8,7 +8,6 @@ package org.hibernate.internal;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.util.List;
|
|
||||||
import javax.transaction.SystemException;
|
import javax.transaction.SystemException;
|
||||||
|
|
||||||
import org.hibernate.CacheMode;
|
import org.hibernate.CacheMode;
|
||||||
|
@ -27,16 +26,12 @@ import org.hibernate.engine.internal.Versioning;
|
||||||
import org.hibernate.engine.spi.EntityKey;
|
import org.hibernate.engine.spi.EntityKey;
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||||
import org.hibernate.engine.spi.PersistenceContext;
|
import org.hibernate.engine.spi.PersistenceContext;
|
||||||
import org.hibernate.engine.spi.QueryParameters;
|
|
||||||
import org.hibernate.engine.transaction.internal.jta.JtaStatusHelper;
|
import org.hibernate.engine.transaction.internal.jta.JtaStatusHelper;
|
||||||
import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform;
|
import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform;
|
||||||
import org.hibernate.id.IdentifierGeneratorHelper;
|
import org.hibernate.id.IdentifierGeneratorHelper;
|
||||||
import org.hibernate.loader.custom.CustomLoader;
|
|
||||||
import org.hibernate.loader.custom.CustomQuery;
|
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
import org.hibernate.pretty.MessageHelper;
|
import org.hibernate.pretty.MessageHelper;
|
||||||
import org.hibernate.proxy.HibernateProxy;
|
import org.hibernate.proxy.HibernateProxy;
|
||||||
import org.hibernate.query.spi.ScrollableResultsImplementor;
|
|
||||||
import org.hibernate.tuple.entity.EntityMetamodel;
|
import org.hibernate.tuple.entity.EntityMetamodel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -550,40 +545,6 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public List listCustomQuery(CustomQuery customQuery, QueryParameters queryParameters)
|
|
||||||
throws HibernateException {
|
|
||||||
checkOpen();
|
|
||||||
CustomLoader loader = new CustomLoader( customQuery, getFactory() );
|
|
||||||
|
|
||||||
boolean success = false;
|
|
||||||
List results;
|
|
||||||
try {
|
|
||||||
results = loader.list( this, queryParameters );
|
|
||||||
success = true;
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
afterOperation( success );
|
|
||||||
}
|
|
||||||
temporaryPersistenceContext.clear();
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ScrollableResultsImplementor scrollCustomQuery(CustomQuery customQuery, QueryParameters queryParameters)
|
|
||||||
throws HibernateException {
|
|
||||||
checkOpen();
|
|
||||||
CustomLoader loader = new CustomLoader( customQuery, getFactory() );
|
|
||||||
return loader.scroll( queryParameters, this );
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Override
|
|
||||||
// public ScrollableResultsImplementor scroll(String query, QueryParameters queryParameters) throws HibernateException {
|
|
||||||
// checkOpen();
|
|
||||||
// HQLQueryPlan plan = getQueryPlan( query, false );
|
|
||||||
// return plan.performScroll( queryParameters, this );
|
|
||||||
// }
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterScrollOperation() {
|
public void afterScrollOperation() {
|
||||||
temporaryPersistenceContext.clear();
|
temporaryPersistenceContext.clear();
|
||||||
|
|
|
@ -1,200 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.hibernate.FetchMode;
|
|
||||||
import org.hibernate.LockOptions;
|
|
||||||
import org.hibernate.MappingException;
|
|
||||||
import org.hibernate.engine.profile.Fetch;
|
|
||||||
import org.hibernate.engine.profile.FetchProfile;
|
|
||||||
import org.hibernate.engine.spi.CascadeStyle;
|
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.persister.entity.Loadable;
|
|
||||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
|
||||||
import org.hibernate.sql.JoinFragment;
|
|
||||||
import org.hibernate.sql.Select;
|
|
||||||
import org.hibernate.type.AssociationType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Abstract walker for walkers which begin at an entity (criteria
|
|
||||||
* queries and entity loaders).
|
|
||||||
*
|
|
||||||
* @author Gavin King
|
|
||||||
*/
|
|
||||||
public abstract class AbstractEntityJoinWalker extends JoinWalker {
|
|
||||||
private final OuterJoinLoadable persister;
|
|
||||||
private final String alias;
|
|
||||||
|
|
||||||
public AbstractEntityJoinWalker(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) {
|
|
||||||
this( persister, factory, loadQueryInfluencers, null );
|
|
||||||
}
|
|
||||||
|
|
||||||
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 LockOptions lockOptions) throws MappingException {
|
|
||||||
initAll( whereString, orderByString, lockOptions, AssociationInitCallback.NO_CALLBACK );
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final void initAll(
|
|
||||||
final String whereString,
|
|
||||||
final String orderByString,
|
|
||||||
final LockOptions lockOptions,
|
|
||||||
final AssociationInitCallback callback) throws MappingException {
|
|
||||||
walkEntityTree( persister, getAlias() );
|
|
||||||
List allAssociations = new ArrayList( associations );
|
|
||||||
allAssociations.add( OuterJoinableAssociation.createRoot( persister.getEntityType(), alias, getFactory() ) );
|
|
||||||
initPersisters( allAssociations, lockOptions, callback );
|
|
||||||
initStatementString( whereString, orderByString, lockOptions );
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final void initProjection(
|
|
||||||
final String projectionString,
|
|
||||||
final String whereString,
|
|
||||||
final String orderByString,
|
|
||||||
final String groupByString,
|
|
||||||
final LockOptions lockOptions) throws MappingException {
|
|
||||||
walkEntityTree( persister, getAlias() );
|
|
||||||
persisters = new Loadable[0];
|
|
||||||
initStatementString(projectionString, whereString, orderByString, groupByString, lockOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initStatementString(
|
|
||||||
final String condition,
|
|
||||||
final String orderBy,
|
|
||||||
final LockOptions lockOptions) throws MappingException {
|
|
||||||
initStatementString(null, condition, orderBy, "", lockOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initStatementString(
|
|
||||||
final String projection,
|
|
||||||
final String condition,
|
|
||||||
final String orderBy,
|
|
||||||
final String groupBy,
|
|
||||||
final LockOptions lockOptions) throws MappingException {
|
|
||||||
|
|
||||||
final int joins = countEntityPersisters( associations );
|
|
||||||
suffixes = BasicLoader.generateSuffixes( joins + 1 );
|
|
||||||
|
|
||||||
JoinFragment ojf = mergeOuterJoins( associations );
|
|
||||||
|
|
||||||
Select select = new Select( getDialect() )
|
|
||||||
.setLockOptions( lockOptions )
|
|
||||||
.setSelectClause(
|
|
||||||
projection == null ?
|
|
||||||
persister.selectFragment( alias, suffixes[joins] ) + selectString( associations ) :
|
|
||||||
projection
|
|
||||||
)
|
|
||||||
.setFromClause(
|
|
||||||
getDialect().appendLockHint( lockOptions, persister.fromTableFragment( alias ) ) +
|
|
||||||
persister.fromJoinFragment( alias, true, true )
|
|
||||||
)
|
|
||||||
.setWhereClause( condition )
|
|
||||||
.setOuterJoins(
|
|
||||||
ojf.toFromFragmentString(),
|
|
||||||
ojf.toWhereFragmentString() + getWhereFragment()
|
|
||||||
)
|
|
||||||
.setOrderByClause( orderBy( associations, orderBy ) )
|
|
||||||
.setGroupByClause( groupBy );
|
|
||||||
|
|
||||||
if ( getFactory().getSessionFactoryOptions().isCommentsEnabled() ) {
|
|
||||||
select.setComment( getComment() );
|
|
||||||
}
|
|
||||||
sql = select.toStatementString();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String getWhereFragment() throws MappingException {
|
|
||||||
// here we do not bother with the discriminator.
|
|
||||||
return persister.whereJoinFragment(alias, true, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The superclass deliberately excludes collections
|
|
||||||
*/
|
|
||||||
protected boolean isJoinedFetchEnabled(AssociationType type, FetchMode config, CascadeStyle cascadeStyle) {
|
|
||||||
return isJoinedFetchEnabledInMapping( config, type );
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final boolean isJoinFetchEnabledByProfile(OuterJoinLoadable persister, PropertyPath path, int propertyNumber) {
|
|
||||||
if ( !getLoadQueryInfluencers().hasEnabledFetchProfiles() ) {
|
|
||||||
// perf optimization
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ugh, this stuff has to be made easier...
|
|
||||||
final String fullPath = path.getFullPath();
|
|
||||||
String rootPropertyName = persister.getSubclassPropertyName( propertyNumber );
|
|
||||||
int pos = fullPath.lastIndexOf( rootPropertyName );
|
|
||||||
String relativePropertyPath = pos >= 0
|
|
||||||
? fullPath.substring( pos )
|
|
||||||
: rootPropertyName;
|
|
||||||
String fetchRole = persister.getEntityName() + '.' + relativePropertyPath;
|
|
||||||
|
|
||||||
for ( String profileName : getLoadQueryInfluencers().getEnabledFetchProfileNames() ) {
|
|
||||||
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();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isDuplicateAssociation(final String foreignKeyTable, final String[] foreignKeyColumns) {
|
|
||||||
//disable a join back to this same association
|
|
||||||
final boolean isSameJoin =
|
|
||||||
persister.getTableName().equals( foreignKeyTable ) &&
|
|
||||||
Arrays.equals( foreignKeyColumns, persister.getKeyColumnNames() );
|
|
||||||
return isSameJoin ||
|
|
||||||
super.isDuplicateAssociation(foreignKeyTable, foreignKeyColumns);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public final Loadable getPersister() {
|
|
||||||
return persister;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final String getAlias() {
|
|
||||||
return alias;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For entities, orderings added by, for example, Criteria#addOrder need to come before the associations' @OrderBy
|
|
||||||
* values. However, other sub-classes of JoinWalker (BasicCollectionJoinWalker, OneToManyJoinWalker, etc.)
|
|
||||||
* still need the other way around. So, override here instead. See HHH-7116.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected String orderBy(final List associations, final String orderBy) {
|
|
||||||
return mergeOrderings( orderBy, orderBy( associations ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString() {
|
|
||||||
return getClass().getName() + '(' + getPersister().getEntityName() + ')';
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,109 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.loader.internal.AliasConstantsHelper;
|
|
||||||
import org.hibernate.persister.collection.CollectionPersister;
|
|
||||||
import org.hibernate.persister.entity.Loadable;
|
|
||||||
import org.hibernate.type.BagType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Uses the default mapping from property to result set column
|
|
||||||
* alias defined by the entities' persisters. Used when Hibernate
|
|
||||||
* is generating result set column aliases.
|
|
||||||
*
|
|
||||||
* @author Gavin King
|
|
||||||
*/
|
|
||||||
public abstract class BasicLoader extends Loader {
|
|
||||||
|
|
||||||
protected static final String[] NO_SUFFIX = {""};
|
|
||||||
|
|
||||||
private EntityAliases[] descriptors;
|
|
||||||
private CollectionAliases[] collectionDescriptors;
|
|
||||||
|
|
||||||
public BasicLoader(SessionFactoryImplementor factory) {
|
|
||||||
super(factory);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final EntityAliases[] getEntityAliases() {
|
|
||||||
return descriptors;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final CollectionAliases[] getCollectionAliases() {
|
|
||||||
return collectionDescriptors;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract String[] getSuffixes();
|
|
||||||
protected abstract String[] getCollectionSuffixes();
|
|
||||||
|
|
||||||
protected void postInstantiate() {
|
|
||||||
Loadable[] persisters = getEntityPersisters();
|
|
||||||
String[] suffixes = getSuffixes();
|
|
||||||
descriptors = new EntityAliases[persisters.length];
|
|
||||||
for ( int i=0; i<descriptors.length; i++ ) {
|
|
||||||
descriptors[i] = new DefaultEntityAliases( persisters[i], suffixes[i] );
|
|
||||||
}
|
|
||||||
|
|
||||||
CollectionPersister[] collectionPersisters = getCollectionPersisters();
|
|
||||||
List bagRoles = null;
|
|
||||||
if ( collectionPersisters != null ) {
|
|
||||||
String[] collectionSuffixes = getCollectionSuffixes();
|
|
||||||
collectionDescriptors = new CollectionAliases[collectionPersisters.length];
|
|
||||||
for ( int i = 0; i < collectionPersisters.length; i++ ) {
|
|
||||||
if ( isBag( collectionPersisters[i] ) ) {
|
|
||||||
if ( bagRoles == null ) {
|
|
||||||
bagRoles = new ArrayList();
|
|
||||||
}
|
|
||||||
bagRoles.add( collectionPersisters[i].getRole() );
|
|
||||||
}
|
|
||||||
collectionDescriptors[i] = new GeneratedCollectionAliases(
|
|
||||||
collectionPersisters[i],
|
|
||||||
collectionSuffixes[i]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
collectionDescriptors = null;
|
|
||||||
}
|
|
||||||
if ( bagRoles != null && bagRoles.size() > 1 ) {
|
|
||||||
throw new MultipleBagFetchException( bagRoles );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isBag(CollectionPersister collectionPersister) {
|
|
||||||
return collectionPersister.getCollectionType().getClass().isAssignableFrom( BagType.class );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility method that generates 0_, 1_ suffixes. Subclasses don't
|
|
||||||
* necessarily need to use this algorithm, but it is intended that
|
|
||||||
* they will in most cases.
|
|
||||||
*
|
|
||||||
* @param length The number of suffixes to generate
|
|
||||||
*
|
|
||||||
* @return The array of generated suffixes (with length=length).
|
|
||||||
*/
|
|
||||||
public static String[] generateSuffixes(int length) {
|
|
||||||
return generateSuffixes( 0, length );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String[] generateSuffixes(int seed, int length) {
|
|
||||||
if ( length == 0 ) {
|
|
||||||
return NO_SUFFIX;
|
|
||||||
}
|
|
||||||
|
|
||||||
String[] suffixes = new String[length];
|
|
||||||
for ( int i = 0; i < length; i++ ) {
|
|
||||||
suffixes[i] = AliasConstantsHelper.get( i + seed );
|
|
||||||
}
|
|
||||||
return suffixes;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,8 +1,8 @@
|
||||||
/*
|
/*
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
*
|
*
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
*/
|
*/
|
||||||
package org.hibernate.loader;
|
package org.hibernate.loader;
|
||||||
|
|
||||||
|
@ -15,7 +15,10 @@ import org.jboss.logging.Logger;
|
||||||
* ({@link org.hibernate.cfg.AvailableSettings#BATCH_FETCH_STYLE}) setting
|
* ({@link org.hibernate.cfg.AvailableSettings#BATCH_FETCH_STYLE}) setting
|
||||||
*
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
|
*
|
||||||
|
* @deprecated (since 6.0) : see {@link BatchLoadSizingStrategy} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public enum BatchFetchStyle {
|
public enum BatchFetchStyle {
|
||||||
/**
|
/**
|
||||||
* The legacy algorithm where we keep a set of pre-built batch sizes based on
|
* The legacy algorithm where we keep a set of pre-built batch sizes based on
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Type definition of CollectionAliases.
|
|
||||||
*
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public interface CollectionAliases {
|
|
||||||
/**
|
|
||||||
* Returns the suffixed result-set column-aliases for columns making
|
|
||||||
* up the key for this collection (i.e., its FK to its owner).
|
|
||||||
*
|
|
||||||
* @return The key result-set column aliases.
|
|
||||||
*/
|
|
||||||
public String[] getSuffixedKeyAliases();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the suffixed result-set column-aliases for the collumns
|
|
||||||
* making up the collection's index (map or list).
|
|
||||||
*
|
|
||||||
* @return The index result-set column aliases.
|
|
||||||
*/
|
|
||||||
public String[] getSuffixedIndexAliases();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the suffixed result-set column-aliases for the columns
|
|
||||||
* making up the collection's elements.
|
|
||||||
*
|
|
||||||
* @return The element result-set column aliases.
|
|
||||||
*/
|
|
||||||
public String[] getSuffixedElementAliases();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the suffixed result-set column-aliases for the column
|
|
||||||
* defining the collection's identifier (if any).
|
|
||||||
*
|
|
||||||
* @return The identifier result-set column aliases.
|
|
||||||
*/
|
|
||||||
public String getSuffixedIdentifierAlias();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the suffix used to unique the column aliases for this
|
|
||||||
* particular alias set.
|
|
||||||
*
|
|
||||||
* @return The uniqued column alias suffix.
|
|
||||||
*/
|
|
||||||
public String getSuffix();
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.hibernate.persister.entity.Loadable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* EntityAliases that chooses the column names over the alias names. This strategy is used
|
|
||||||
* when the result-set mapping did not give specific aliases to use in extracting from the
|
|
||||||
* result set. We use the column names from the underlying persister.
|
|
||||||
*
|
|
||||||
* @author max
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public class ColumnEntityAliases extends DefaultEntityAliases {
|
|
||||||
|
|
||||||
public ColumnEntityAliases(
|
|
||||||
Map returnProperties,
|
|
||||||
Loadable persister,
|
|
||||||
String suffix) {
|
|
||||||
super( returnProperties, persister, suffix );
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String[] getIdentifierAliases(Loadable persister, String suffix) {
|
|
||||||
return persister.getIdentifierColumnNames();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String getDiscriminatorAlias(Loadable persister, String suffix) {
|
|
||||||
return persister.getDiscriminatorColumnName();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String[] getPropertyAliases(Loadable persister, int j) {
|
|
||||||
return persister.getPropertyColumnNames(j);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,187 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.hibernate.dialect.Dialect;
|
|
||||||
import org.hibernate.internal.util.StringHelper;
|
|
||||||
import org.hibernate.persister.entity.Loadable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* EntityAliases which handles the logic of selecting user provided aliases (via return-property),
|
|
||||||
* before using the default aliases.
|
|
||||||
*
|
|
||||||
* @author max
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class DefaultEntityAliases implements EntityAliases {
|
|
||||||
|
|
||||||
private static final String[][] EMPTY_ARRAY_OF_ARRAY_OF_STRINGS = new String[0][];
|
|
||||||
|
|
||||||
private final String[] suffixedKeyColumns;
|
|
||||||
private final String[] suffixedVersionColumn;
|
|
||||||
private final String[][] suffixedPropertyColumns;
|
|
||||||
private final String suffixedDiscriminatorColumn;
|
|
||||||
private final String suffix;
|
|
||||||
private final String rowIdAlias;
|
|
||||||
private final Map userProvidedAliases;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate and cache select-clause aliases
|
|
||||||
*
|
|
||||||
* @param userProvidedAliases The explicit aliases provided in a result-set mapping.
|
|
||||||
* @param persister The persister for which we are generating select aliases
|
|
||||||
* @param suffix The calculated suffix.
|
|
||||||
*/
|
|
||||||
public DefaultEntityAliases(
|
|
||||||
Map userProvidedAliases,
|
|
||||||
Loadable persister,
|
|
||||||
String suffix) {
|
|
||||||
this( userProvidedAliases, persister, suffix, false );
|
|
||||||
}
|
|
||||||
|
|
||||||
public DefaultEntityAliases(Loadable persister, String suffix) {
|
|
||||||
this( Collections.EMPTY_MAP, persister, suffix, true );
|
|
||||||
}
|
|
||||||
|
|
||||||
private DefaultEntityAliases(
|
|
||||||
Map userProvidedAliases,
|
|
||||||
Loadable persister,
|
|
||||||
String suffix,
|
|
||||||
boolean interns) {
|
|
||||||
if ( interns ) {
|
|
||||||
this.suffix = suffix.intern();
|
|
||||||
this.rowIdAlias = (Loadable.ROWID_ALIAS + suffix).intern(); // TODO: not visible to the user!
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.suffix = suffix;
|
|
||||||
this.rowIdAlias = (Loadable.ROWID_ALIAS + suffix);
|
|
||||||
}
|
|
||||||
this.userProvidedAliases = userProvidedAliases;
|
|
||||||
suffixedKeyColumns = determineKeyAlias( persister, suffix );
|
|
||||||
suffixedPropertyColumns = determinePropertyAliases( persister );
|
|
||||||
suffixedDiscriminatorColumn = determineDiscriminatorAlias( persister, suffix );
|
|
||||||
suffixedVersionColumn = determineVersionAlias( persister );
|
|
||||||
}
|
|
||||||
|
|
||||||
private String[] determineKeyAlias(Loadable persister, String suffix) {
|
|
||||||
final String[] aliases;
|
|
||||||
final String[] keyColumnsCandidates = getUserProvidedAliases( persister.getIdentifierPropertyName(), null );
|
|
||||||
if ( keyColumnsCandidates == null ) {
|
|
||||||
aliases = getUserProvidedAliases(
|
|
||||||
"id",
|
|
||||||
getIdentifierAliases(persister, suffix)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
aliases = keyColumnsCandidates;
|
|
||||||
}
|
|
||||||
return StringHelper.unquote( aliases, persister.getFactory().getDialect() );
|
|
||||||
}
|
|
||||||
|
|
||||||
private String[][] determinePropertyAliases(Loadable persister) {
|
|
||||||
return getSuffixedPropertyAliases( persister );
|
|
||||||
}
|
|
||||||
|
|
||||||
private String determineDiscriminatorAlias(Loadable persister, String suffix) {
|
|
||||||
String alias = getUserProvidedAlias( "class", getDiscriminatorAlias( persister, suffix ) );
|
|
||||||
return StringHelper.unquote( alias, persister.getFactory().getDialect() );
|
|
||||||
}
|
|
||||||
|
|
||||||
private String[] determineVersionAlias(Loadable persister) {
|
|
||||||
return persister.isVersioned()
|
|
||||||
? suffixedPropertyColumns[ persister.getVersionProperty() ]
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String getDiscriminatorAlias(Loadable persister, String suffix) {
|
|
||||||
return persister.getDiscriminatorAlias(suffix);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String[] getIdentifierAliases(Loadable persister, String suffix) {
|
|
||||||
return persister.getIdentifierAliases(suffix);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String[] getPropertyAliases(Loadable persister, int j) {
|
|
||||||
return persister.getPropertyAliases(suffix, j);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String[] getUserProvidedAliases(String propertyPath, String[] defaultAliases) {
|
|
||||||
String[] result = (String[]) userProvidedAliases.get(propertyPath);
|
|
||||||
if (result==null) {
|
|
||||||
return defaultAliases;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getUserProvidedAlias(String propertyPath, String defaultAlias) {
|
|
||||||
String[] columns = (String[]) userProvidedAliases.get( propertyPath );
|
|
||||||
if ( columns == null ) {
|
|
||||||
return defaultAlias;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return columns[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String[][] getSuffixedPropertyAliases(Loadable persister) {
|
|
||||||
final String[] propertyNames = persister.getPropertyNames();
|
|
||||||
final int size = propertyNames.length;
|
|
||||||
final String[][] suffixedPropertyAliases;
|
|
||||||
if ( size > 0 ) {
|
|
||||||
suffixedPropertyAliases = new String[size][];
|
|
||||||
final Dialect dialect = persister.getFactory().getDialect();
|
|
||||||
for ( int j = 0; j < size; j++ ) {
|
|
||||||
suffixedPropertyAliases[j] = getUserProvidedAliases(
|
|
||||||
propertyNames[j],
|
|
||||||
getPropertyAliases( persister, j )
|
|
||||||
);
|
|
||||||
suffixedPropertyAliases[j] = StringHelper.unquote( suffixedPropertyAliases[j], dialect );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
suffixedPropertyAliases = EMPTY_ARRAY_OF_ARRAY_OF_STRINGS;
|
|
||||||
}
|
|
||||||
return suffixedPropertyAliases;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String[] getSuffixedVersionAliases() {
|
|
||||||
return suffixedVersionColumn;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String[][] getSuffixedPropertyAliases() {
|
|
||||||
return suffixedPropertyColumns;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getSuffixedDiscriminatorAlias() {
|
|
||||||
return suffixedDiscriminatorColumn;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String[] getSuffixedKeyAliases() {
|
|
||||||
return suffixedKeyColumns;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getRowIdAlias() {
|
|
||||||
return rowIdAlias;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getSuffix() {
|
|
||||||
return suffix;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader;
|
|
||||||
import org.hibernate.persister.entity.Loadable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Metadata describing the SQL result set column aliases
|
|
||||||
* for a particular entity.
|
|
||||||
*
|
|
||||||
* @author Gavin King
|
|
||||||
*/
|
|
||||||
public interface EntityAliases {
|
|
||||||
/**
|
|
||||||
* The result set column aliases for the primary key columns
|
|
||||||
*/
|
|
||||||
public String[] getSuffixedKeyAliases();
|
|
||||||
/**
|
|
||||||
* The result set column aliases for the discriminator columns
|
|
||||||
*/
|
|
||||||
public String getSuffixedDiscriminatorAlias();
|
|
||||||
/**
|
|
||||||
* The result set column aliases for the version columns
|
|
||||||
*/
|
|
||||||
public String[] getSuffixedVersionAliases();
|
|
||||||
/**
|
|
||||||
* The result set column aliases for the property columns
|
|
||||||
*/
|
|
||||||
public String[][] getSuffixedPropertyAliases();
|
|
||||||
/**
|
|
||||||
* The result set column aliases for the property columns of a subclass
|
|
||||||
*/
|
|
||||||
public String[][] getSuffixedPropertyAliases(Loadable persister);
|
|
||||||
/**
|
|
||||||
* The result set column alias for the Oracle row id
|
|
||||||
*/
|
|
||||||
public String getRowIdAlias();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the suffix used to generate the aliases.
|
|
||||||
* @return the suffix used to generate the aliases.
|
|
||||||
*/
|
|
||||||
public String getSuffix();
|
|
||||||
}
|
|
|
@ -1,141 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.hibernate.persister.collection.CollectionPersister;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CollectionAliases which handles the logic of selecting user provided aliases (via return-property),
|
|
||||||
* before using the default aliases.
|
|
||||||
*
|
|
||||||
* @author Steve Ebersole
|
|
||||||
* @author Max Rydahl Andersen
|
|
||||||
*/
|
|
||||||
public class GeneratedCollectionAliases implements CollectionAliases {
|
|
||||||
private final String suffix;
|
|
||||||
private final String[] keyAliases;
|
|
||||||
private final String[] indexAliases;
|
|
||||||
private final String[] elementAliases;
|
|
||||||
private final String identifierAlias;
|
|
||||||
private Map userProvidedAliases;
|
|
||||||
|
|
||||||
public GeneratedCollectionAliases(Map userProvidedAliases, CollectionPersister persister, String suffix) {
|
|
||||||
this.suffix = suffix;
|
|
||||||
this.userProvidedAliases = userProvidedAliases;
|
|
||||||
|
|
||||||
this.keyAliases = getUserProvidedAliases(
|
|
||||||
"key",
|
|
||||||
persister.getKeyColumnAliases( suffix )
|
|
||||||
);
|
|
||||||
|
|
||||||
this.indexAliases = getUserProvidedAliases(
|
|
||||||
"index",
|
|
||||||
persister.getIndexColumnAliases( suffix )
|
|
||||||
);
|
|
||||||
|
|
||||||
this.elementAliases = getUserProvidedAliases(
|
|
||||||
"element",
|
|
||||||
persister.getElementColumnAliases( suffix )
|
|
||||||
);
|
|
||||||
|
|
||||||
this.identifierAlias = getUserProvidedAlias(
|
|
||||||
"id",
|
|
||||||
persister.getIdentifierColumnAlias( suffix )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public GeneratedCollectionAliases(CollectionPersister persister, String string) {
|
|
||||||
this( Collections.EMPTY_MAP, persister, string );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the suffixed result-set column-aliases for columns making up the key for this collection (i.e., its FK to
|
|
||||||
* its owner).
|
|
||||||
*
|
|
||||||
* @return The key result-set column aliases.
|
|
||||||
*/
|
|
||||||
public String[] getSuffixedKeyAliases() {
|
|
||||||
return keyAliases;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the suffixed result-set column-aliases for the collumns making up the collection's index (map or list).
|
|
||||||
*
|
|
||||||
* @return The index result-set column aliases.
|
|
||||||
*/
|
|
||||||
public String[] getSuffixedIndexAliases() {
|
|
||||||
return indexAliases;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the suffixed result-set column-aliases for the columns making up the collection's elements.
|
|
||||||
*
|
|
||||||
* @return The element result-set column aliases.
|
|
||||||
*/
|
|
||||||
public String[] getSuffixedElementAliases() {
|
|
||||||
return elementAliases;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the suffixed result-set column-aliases for the column defining the collection's identifier (if any).
|
|
||||||
*
|
|
||||||
* @return The identifier result-set column aliases.
|
|
||||||
*/
|
|
||||||
public String getSuffixedIdentifierAlias() {
|
|
||||||
return identifierAlias;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the suffix used to unique the column aliases for this particular alias set.
|
|
||||||
*
|
|
||||||
* @return The uniqued column alias suffix.
|
|
||||||
*/
|
|
||||||
public String getSuffix() {
|
|
||||||
return suffix;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return super.toString() + " [suffix=" + suffix +
|
|
||||||
", suffixedKeyAliases=[" + join( keyAliases ) +
|
|
||||||
"], suffixedIndexAliases=[" + join( indexAliases ) +
|
|
||||||
"], suffixedElementAliases=[" + join( elementAliases ) +
|
|
||||||
"], suffixedIdentifierAlias=[" + identifierAlias + "]]";
|
|
||||||
}
|
|
||||||
|
|
||||||
private String join(String[] aliases) {
|
|
||||||
if ( aliases == null ) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return String.join( ", ", aliases );
|
|
||||||
}
|
|
||||||
|
|
||||||
private String[] getUserProvidedAliases(String propertyPath, String[] defaultAliases) {
|
|
||||||
String[] result = (String[]) userProvidedAliases.get( propertyPath );
|
|
||||||
if ( result == null ) {
|
|
||||||
return defaultAliases;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getUserProvidedAlias(String propertyPath, String defaultAlias) {
|
|
||||||
String[] columns = (String[]) userProvidedAliases.get( propertyPath );
|
|
||||||
if ( columns == null ) {
|
|
||||||
return defaultAlias;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return columns[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,10 +1,10 @@
|
||||||
/*
|
/*
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
*
|
*
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
*/
|
*/
|
||||||
package org.hibernate.loader.custom;
|
package org.hibernate.loader;
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.HibernateException;
|
||||||
|
|
|
@ -1,115 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader;
|
|
||||||
|
|
||||||
import org.hibernate.LockMode;
|
|
||||||
import org.hibernate.LockOptions;
|
|
||||||
import org.hibernate.dialect.Dialect;
|
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.persister.collection.CollectionPersister;
|
|
||||||
import org.hibernate.persister.entity.Loadable;
|
|
||||||
import org.hibernate.type.EntityType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements logic for walking a tree of associated classes.
|
|
||||||
*
|
|
||||||
* Generates an SQL select string containing all properties of those classes.
|
|
||||||
* Tables are joined using an ANSI-style left outer join.
|
|
||||||
*
|
|
||||||
* @author Gavin King
|
|
||||||
*/
|
|
||||||
public abstract class OuterJoinLoader extends BasicLoader {
|
|
||||||
protected Loadable[] persisters;
|
|
||||||
protected CollectionPersister[] collectionPersisters;
|
|
||||||
protected int[] collectionOwners;
|
|
||||||
protected String[] aliases;
|
|
||||||
private LockOptions lockOptions;
|
|
||||||
protected LockMode[] lockModeArray;
|
|
||||||
protected int[] owners;
|
|
||||||
protected EntityType[] ownerAssociationTypes;
|
|
||||||
protected String sql;
|
|
||||||
protected String[] suffixes;
|
|
||||||
protected String[] collectionSuffixes;
|
|
||||||
|
|
||||||
private LoadQueryInfluencers loadQueryInfluencers;
|
|
||||||
|
|
||||||
protected final Dialect getDialect() {
|
|
||||||
return getFactory().getDialect();
|
|
||||||
}
|
|
||||||
|
|
||||||
public OuterJoinLoader(
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) {
|
|
||||||
super( factory );
|
|
||||||
this.loadQueryInfluencers = loadQueryInfluencers;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String[] getSuffixes() {
|
|
||||||
return suffixes;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String[] getCollectionSuffixes() {
|
|
||||||
return collectionSuffixes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final String getSQLString() {
|
|
||||||
return sql;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final Loadable[] getEntityPersisters() {
|
|
||||||
return persisters;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int[] getOwners() {
|
|
||||||
return owners;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected EntityType[] getOwnerAssociationTypes() {
|
|
||||||
return ownerAssociationTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected LockMode[] getLockModes(LockOptions lockOptions) {
|
|
||||||
return lockModeArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected LockOptions getLockOptions() {
|
|
||||||
return lockOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LoadQueryInfluencers getLoadQueryInfluencers() {
|
|
||||||
return loadQueryInfluencers;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final String[] getAliases() {
|
|
||||||
return aliases;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final CollectionPersister[] getCollectionPersisters() {
|
|
||||||
return collectionPersisters;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final int[] getCollectionOwners() {
|
|
||||||
return collectionOwners;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void initFromWalker(JoinWalker walker) {
|
|
||||||
persisters = walker.getPersisters();
|
|
||||||
collectionPersisters = walker.getCollectionPersisters();
|
|
||||||
ownerAssociationTypes = walker.getOwnerAssociationTypes();
|
|
||||||
lockOptions = walker.getLockModeOptions();
|
|
||||||
lockModeArray = walker.getLockModeArray();
|
|
||||||
suffixes = walker.getSuffixes();
|
|
||||||
collectionSuffixes = walker.getCollectionSuffixes();
|
|
||||||
owners = walker.getOwners();
|
|
||||||
collectionOwners = walker.getCollectionOwners();
|
|
||||||
sql = walker.getSQLString();
|
|
||||||
aliases = walker.getAliases();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,214 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.hibernate.MappingException;
|
|
||||||
import org.hibernate.engine.internal.JoinHelper;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.persister.collection.QueryableCollection;
|
|
||||||
import org.hibernate.persister.entity.Joinable;
|
|
||||||
import org.hibernate.sql.JoinFragment;
|
|
||||||
import org.hibernate.sql.JoinType;
|
|
||||||
import org.hibernate.type.AssociationType;
|
|
||||||
import org.hibernate.type.EntityType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Part of the Hibernate SQL rendering internals. This class represents
|
|
||||||
* a joinable association.
|
|
||||||
*
|
|
||||||
* @author Gavin King
|
|
||||||
*/
|
|
||||||
public final class OuterJoinableAssociation {
|
|
||||||
private final PropertyPath propertyPath;
|
|
||||||
private final AssociationType joinableType;
|
|
||||||
private final Joinable joinable;
|
|
||||||
private final String lhsAlias; // belong to other persister
|
|
||||||
private final String[] lhsColumns; // belong to other persister
|
|
||||||
private final String rhsAlias;
|
|
||||||
private final String[] rhsColumns;
|
|
||||||
private final JoinType joinType;
|
|
||||||
private final String on;
|
|
||||||
private final Map enabledFilters;
|
|
||||||
private final boolean hasRestriction;
|
|
||||||
|
|
||||||
public static OuterJoinableAssociation createRoot(
|
|
||||||
AssociationType joinableType,
|
|
||||||
String alias,
|
|
||||||
SessionFactoryImplementor factory) {
|
|
||||||
return new OuterJoinableAssociation(
|
|
||||||
new PropertyPath(),
|
|
||||||
joinableType,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
alias,
|
|
||||||
JoinType.LEFT_OUTER_JOIN,
|
|
||||||
null,
|
|
||||||
false,
|
|
||||||
factory,
|
|
||||||
Collections.EMPTY_MAP
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public OuterJoinableAssociation(
|
|
||||||
PropertyPath propertyPath,
|
|
||||||
AssociationType joinableType,
|
|
||||||
String lhsAlias,
|
|
||||||
String[] lhsColumns,
|
|
||||||
String rhsAlias,
|
|
||||||
JoinType joinType,
|
|
||||||
String withClause,
|
|
||||||
boolean hasRestriction,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
Map enabledFilters) throws MappingException {
|
|
||||||
this.propertyPath = propertyPath;
|
|
||||||
this.joinableType = joinableType;
|
|
||||||
this.lhsAlias = lhsAlias;
|
|
||||||
this.lhsColumns = lhsColumns;
|
|
||||||
this.rhsAlias = rhsAlias;
|
|
||||||
this.joinType = joinType;
|
|
||||||
this.joinable = joinableType.getAssociatedJoinable( factory );
|
|
||||||
this.rhsColumns = JoinHelper.getRHSColumnNames( joinableType, factory );
|
|
||||||
this.on = joinableType.getOnCondition( rhsAlias, factory, enabledFilters )
|
|
||||||
+ ( withClause == null || withClause.trim().length() == 0 ? "" : " and ( " + withClause + " )" );
|
|
||||||
this.hasRestriction = hasRestriction;
|
|
||||||
this.enabledFilters = enabledFilters; // needed later for many-to-many/filter application
|
|
||||||
}
|
|
||||||
|
|
||||||
public PropertyPath getPropertyPath() {
|
|
||||||
return propertyPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public JoinType getJoinType() {
|
|
||||||
return joinType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getLhsAlias() {
|
|
||||||
return lhsAlias;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getRHSAlias() {
|
|
||||||
return rhsAlias;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getRhsAlias() {
|
|
||||||
return rhsAlias;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isOneToOne() {
|
|
||||||
if ( joinableType.isEntityType() ) {
|
|
||||||
EntityType etype = (EntityType) joinableType;
|
|
||||||
return etype.isOneToOne() /*&& etype.isReferenceToPrimaryKey()*/;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public AssociationType getJoinableType() {
|
|
||||||
return joinableType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getRHSUniqueKeyName() {
|
|
||||||
return joinableType.getRHSUniqueKeyPropertyName();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isCollection() {
|
|
||||||
return joinableType.isCollectionType();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Joinable getJoinable() {
|
|
||||||
return joinable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasRestriction() {
|
|
||||||
return hasRestriction;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getOwner(final List associations) {
|
|
||||||
if ( isOneToOne() || isCollection() ) {
|
|
||||||
return getPosition( lhsAlias, associations );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the position of the join with the given alias in the
|
|
||||||
* list of joins
|
|
||||||
*/
|
|
||||||
private static int getPosition(String lhsAlias, List associations) {
|
|
||||||
int result = 0;
|
|
||||||
for ( Object association : associations ) {
|
|
||||||
final OuterJoinableAssociation oj = (OuterJoinableAssociation) association;
|
|
||||||
if ( oj.getJoinable().consumesEntityAlias() /*|| oj.getJoinable().consumesCollectionAlias() */ ) {
|
|
||||||
if ( oj.rhsAlias.equals( lhsAlias ) ) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
result++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addJoins(JoinFragment outerjoin) throws MappingException {
|
|
||||||
outerjoin.addJoin(
|
|
||||||
joinable.getTableName(),
|
|
||||||
rhsAlias,
|
|
||||||
lhsColumns,
|
|
||||||
rhsColumns,
|
|
||||||
joinType,
|
|
||||||
on
|
|
||||||
);
|
|
||||||
outerjoin.addJoins(
|
|
||||||
joinable.fromJoinFragment( rhsAlias, false, true ),
|
|
||||||
joinable.whereJoinFragment( rhsAlias, false, true )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void validateJoin(String path) throws MappingException {
|
|
||||||
if ( rhsColumns == null || lhsColumns == null
|
|
||||||
|| lhsColumns.length != rhsColumns.length || lhsColumns.length == 0 ) {
|
|
||||||
throw new MappingException( "invalid join columns for association: " + path );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isManyToManyWith(OuterJoinableAssociation other) {
|
|
||||||
if ( joinable.isCollection() ) {
|
|
||||||
QueryableCollection persister = (QueryableCollection) joinable;
|
|
||||||
if ( persister.isManyToMany() ) {
|
|
||||||
return persister.getElementType() == other.getJoinableType();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addManyToManyJoin(JoinFragment outerjoin, QueryableCollection collection) throws MappingException {
|
|
||||||
String manyToManyFilter = collection.getManyToManyFilterFragment( rhsAlias, enabledFilters );
|
|
||||||
String condition = "".equals( manyToManyFilter )
|
|
||||||
? on
|
|
||||||
: "".equals( on )
|
|
||||||
? manyToManyFilter
|
|
||||||
: on + " and " + manyToManyFilter;
|
|
||||||
outerjoin.addJoin(
|
|
||||||
joinable.getTableName(),
|
|
||||||
rhsAlias,
|
|
||||||
lhsColumns,
|
|
||||||
rhsColumns,
|
|
||||||
joinType,
|
|
||||||
condition
|
|
||||||
);
|
|
||||||
outerjoin.addJoins(
|
|
||||||
joinable.fromJoinFragment( rhsAlias, false, true ),
|
|
||||||
joinable.whereJoinFragment( rhsAlias, false, true )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -43,8 +43,8 @@ import org.jboss.logging.Logger;
|
||||||
*
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class BatchKeyCollectionLoader implements CollectionLoader {
|
public class CollectionLoaderBatchKey implements CollectionLoader {
|
||||||
private static final Logger log = Logger.getLogger( BatchKeyCollectionLoader.class );
|
private static final Logger log = Logger.getLogger( CollectionLoaderBatchKey.class );
|
||||||
|
|
||||||
private final PluralAttributeMapping attributeMapping;
|
private final PluralAttributeMapping attributeMapping;
|
||||||
private final int batchSize;
|
private final int batchSize;
|
||||||
|
@ -54,7 +54,7 @@ public class BatchKeyCollectionLoader implements CollectionLoader {
|
||||||
private SelectStatement batchSizeSqlAst;
|
private SelectStatement batchSizeSqlAst;
|
||||||
private List<JdbcParameter> batchSizeJdbcParameters;
|
private List<JdbcParameter> batchSizeJdbcParameters;
|
||||||
|
|
||||||
public BatchKeyCollectionLoader(
|
public CollectionLoaderBatchKey(
|
||||||
PluralAttributeMapping attributeMapping,
|
PluralAttributeMapping attributeMapping,
|
||||||
int batchSize,
|
int batchSize,
|
||||||
LoadQueryInfluencers influencers,
|
LoadQueryInfluencers influencers,
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.loader.ast.internal;
|
||||||
|
|
||||||
|
import org.hibernate.collection.spi.PersistentCollection;
|
||||||
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
|
import org.hibernate.loader.ast.spi.CollectionLoader;
|
||||||
|
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||||
|
import org.hibernate.persister.collection.CollectionPersister;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
public class CollectionLoaderNamedQuery implements CollectionLoader {
|
||||||
|
private final String loaderQueryName;
|
||||||
|
private final CollectionPersister persister;
|
||||||
|
private final PluralAttributeMapping attributeMapping;
|
||||||
|
|
||||||
|
public CollectionLoaderNamedQuery(
|
||||||
|
String loaderQueryName,
|
||||||
|
CollectionPersister persister,
|
||||||
|
PluralAttributeMapping attributeMapping) {
|
||||||
|
this.loaderQueryName = loaderQueryName;
|
||||||
|
this.persister = persister;
|
||||||
|
this.attributeMapping = attributeMapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PluralAttributeMapping getLoadable() {
|
||||||
|
return attributeMapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PersistentCollection load(Object key, SharedSessionContractImplementor session) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,7 +40,7 @@ import org.hibernate.sql.results.internal.RowTransformerPassThruImpl;
|
||||||
*
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class SingleKeyCollectionLoader implements CollectionLoader {
|
public class CollectionLoaderSingleKey implements CollectionLoader {
|
||||||
private final PluralAttributeMapping attributeMapping;
|
private final PluralAttributeMapping attributeMapping;
|
||||||
|
|
||||||
private final int keyJdbcCount;
|
private final int keyJdbcCount;
|
||||||
|
@ -48,7 +48,7 @@ public class SingleKeyCollectionLoader implements CollectionLoader {
|
||||||
private final SelectStatement sqlAst;
|
private final SelectStatement sqlAst;
|
||||||
private final List<JdbcParameter> jdbcParameters;
|
private final List<JdbcParameter> jdbcParameters;
|
||||||
|
|
||||||
public SingleKeyCollectionLoader(
|
public CollectionLoaderSingleKey(
|
||||||
PluralAttributeMapping attributeMapping,
|
PluralAttributeMapping attributeMapping,
|
||||||
LoadQueryInfluencers influencers,
|
LoadQueryInfluencers influencers,
|
||||||
SessionFactoryImplementor sessionFactory) {
|
SessionFactoryImplementor sessionFactory) {
|
|
@ -31,13 +31,13 @@ import org.hibernate.sql.results.internal.RowTransformerPassThruImpl;
|
||||||
*
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class SubSelectFetchCollectionLoader implements CollectionLoader {
|
public class CollectionLoaderSubSelectFetch implements CollectionLoader {
|
||||||
private final PluralAttributeMapping attributeMapping;
|
private final PluralAttributeMapping attributeMapping;
|
||||||
private final SubselectFetch subselect;
|
private final SubselectFetch subselect;
|
||||||
|
|
||||||
private final SelectStatement sqlAst;
|
private final SelectStatement sqlAst;
|
||||||
|
|
||||||
public SubSelectFetchCollectionLoader(
|
public CollectionLoaderSubSelectFetch(
|
||||||
PluralAttributeMapping attributeMapping,
|
PluralAttributeMapping attributeMapping,
|
||||||
DomainResult cachedDomainResult,
|
DomainResult cachedDomainResult,
|
||||||
SubselectFetch subselect,
|
SubselectFetch subselect,
|
|
@ -107,7 +107,7 @@ public class LoaderSelectBuilder {
|
||||||
/**
|
/**
|
||||||
* Create a SQL AST select-statement used for subselect-based CollectionLoader
|
* Create a SQL AST select-statement used for subselect-based CollectionLoader
|
||||||
*
|
*
|
||||||
* @see SubSelectFetchCollectionLoader
|
* @see CollectionLoaderSubSelectFetch
|
||||||
*
|
*
|
||||||
* @param attributeMapping The plural-attribute being loaded
|
* @param attributeMapping The plural-attribute being loaded
|
||||||
* @param subselect The subselect details to apply
|
* @param subselect The subselect details to apply
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.loader.ast.internal;
|
||||||
|
|
||||||
|
import org.hibernate.LockOptions;
|
||||||
|
import org.hibernate.NotYetImplementedFor6Exception;
|
||||||
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
|
import org.hibernate.loader.ast.spi.SingleUniqueKeyEntityLoader;
|
||||||
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
|
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
public class SingleUniqueKeyEntityLoaderStandard implements SingleUniqueKeyEntityLoader {
|
||||||
|
private final EntityMappingType entityDescriptor;
|
||||||
|
private final SingularAttributeMapping uniqueKeyAttribute;
|
||||||
|
|
||||||
|
public SingleUniqueKeyEntityLoaderStandard(
|
||||||
|
EntityMappingType entityDescriptor,
|
||||||
|
SingularAttributeMapping uniqueKeyAttribute) {
|
||||||
|
this.entityDescriptor = entityDescriptor;
|
||||||
|
this.uniqueKeyAttribute = uniqueKeyAttribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EntityMappingType getLoadable() {
|
||||||
|
return entityDescriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object load(
|
||||||
|
Object ukValue,
|
||||||
|
LockOptions lockOptions,
|
||||||
|
SharedSessionContractImplementor session) {
|
||||||
|
throw new NotYetImplementedFor6Exception( getClass() );
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,163 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.collection;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.hibernate.FetchMode;
|
|
||||||
import org.hibernate.Filter;
|
|
||||||
import org.hibernate.LockMode;
|
|
||||||
import org.hibernate.MappingException;
|
|
||||||
import org.hibernate.engine.spi.CascadeStyle;
|
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.internal.util.StringHelper;
|
|
||||||
import org.hibernate.loader.BasicLoader;
|
|
||||||
import org.hibernate.loader.OuterJoinableAssociation;
|
|
||||||
import org.hibernate.loader.PropertyPath;
|
|
||||||
import org.hibernate.persister.collection.QueryableCollection;
|
|
||||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
|
||||||
import org.hibernate.sql.JoinFragment;
|
|
||||||
import org.hibernate.sql.JoinType;
|
|
||||||
import org.hibernate.sql.Select;
|
|
||||||
import org.hibernate.type.AssociationType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Walker for collections of values and many-to-many associations
|
|
||||||
*
|
|
||||||
* @see BasicCollectionLoader
|
|
||||||
* @author Gavin King
|
|
||||||
*/
|
|
||||||
public class BasicCollectionJoinWalker extends CollectionJoinWalker {
|
|
||||||
|
|
||||||
private final QueryableCollection collectionPersister;
|
|
||||||
|
|
||||||
public BasicCollectionJoinWalker(
|
|
||||||
QueryableCollection collectionPersister,
|
|
||||||
int batchSize,
|
|
||||||
String subquery,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
|
||||||
|
|
||||||
super( factory, loadQueryInfluencers );
|
|
||||||
|
|
||||||
this.collectionPersister = collectionPersister;
|
|
||||||
|
|
||||||
String alias = generateRootAlias( collectionPersister.getRole() );
|
|
||||||
|
|
||||||
walkCollectionTree(collectionPersister, alias);
|
|
||||||
|
|
||||||
List allAssociations = new ArrayList( associations );
|
|
||||||
allAssociations.add( OuterJoinableAssociation.createRoot( collectionPersister.getCollectionType(), alias, getFactory() ) );
|
|
||||||
initPersisters( allAssociations, LockMode.NONE );
|
|
||||||
initStatementString( alias, batchSize, subquery );
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initStatementString(
|
|
||||||
final String alias,
|
|
||||||
final int batchSize,
|
|
||||||
final String subquery) throws MappingException {
|
|
||||||
|
|
||||||
final int joins = countEntityPersisters( associations );
|
|
||||||
final int collectionJoins = countCollectionPersisters( associations ) + 1;
|
|
||||||
|
|
||||||
suffixes = BasicLoader.generateSuffixes( joins );
|
|
||||||
collectionSuffixes = BasicLoader.generateSuffixes( joins, collectionJoins );
|
|
||||||
|
|
||||||
StringBuilder whereString = whereString(
|
|
||||||
alias,
|
|
||||||
collectionPersister.getKeyColumnNames(),
|
|
||||||
subquery,
|
|
||||||
batchSize
|
|
||||||
);
|
|
||||||
|
|
||||||
StringBuilder sb = null;
|
|
||||||
final Map<String, Filter> enabledFilters = getLoadQueryInfluencers().getEnabledFilters();
|
|
||||||
String filter = collectionPersister.filterFragment( alias, enabledFilters );
|
|
||||||
if ( collectionPersister.isManyToMany() ) {
|
|
||||||
// from the collection of associations, locate OJA for the
|
|
||||||
// ManyToOne corresponding to this persister to fully
|
|
||||||
// define the many-to-many; we need that OJA so that we can
|
|
||||||
// use its alias here
|
|
||||||
// TODO : is there a better way here?
|
|
||||||
Iterator itr = associations.iterator();
|
|
||||||
sb = new StringBuilder( 20 );
|
|
||||||
AssociationType associationType = ( AssociationType ) collectionPersister.getElementType();
|
|
||||||
while ( itr.hasNext() ) {
|
|
||||||
OuterJoinableAssociation oja = ( OuterJoinableAssociation ) itr.next();
|
|
||||||
if ( oja.getJoinableType() == associationType ) {
|
|
||||||
// we found it
|
|
||||||
filter += collectionPersister.getManyToManyFilterFragment(
|
|
||||||
oja.getRHSAlias(),
|
|
||||||
enabledFilters
|
|
||||||
);
|
|
||||||
sb.append( collectionPersister.getManyToManyOrderByString( oja.getRHSAlias() ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
whereString.insert( 0, StringHelper.moveAndToBeginning( filter ) );
|
|
||||||
|
|
||||||
JoinFragment ojf = mergeOuterJoins(associations);
|
|
||||||
Select select = new Select( getDialect() )
|
|
||||||
.setSelectClause(
|
|
||||||
collectionPersister.selectFragment(alias, collectionSuffixes[0] ) +
|
|
||||||
selectString( associations )
|
|
||||||
)
|
|
||||||
.setFromClause( collectionPersister.getTableName(), alias )
|
|
||||||
.setWhereClause( whereString.toString() )
|
|
||||||
.setOuterJoins(
|
|
||||||
ojf.toFromFragmentString(),
|
|
||||||
ojf.toWhereFragmentString()
|
|
||||||
);
|
|
||||||
|
|
||||||
select.setOrderByClause( orderBy( associations, mergeOrderings( collectionPersister.getSQLOrderByString( alias ), sb == null ? "" : sb.toString() ) ) );
|
|
||||||
|
|
||||||
if ( getFactory().getSettings().isCommentsEnabled() ) {
|
|
||||||
select.setComment( "load collection " + collectionPersister.getRole() );
|
|
||||||
}
|
|
||||||
|
|
||||||
sql = select.toStatementString();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected JoinType getJoinType(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
PropertyPath path,
|
|
||||||
int propertyNumber,
|
|
||||||
AssociationType associationType,
|
|
||||||
FetchMode metadataFetchMode,
|
|
||||||
CascadeStyle metadataCascadeStyle,
|
|
||||||
String lhsTable,
|
|
||||||
String[] lhsColumns,
|
|
||||||
boolean nullable,
|
|
||||||
int currentDepth) throws MappingException {
|
|
||||||
JoinType joinType = super.getJoinType(
|
|
||||||
persister,
|
|
||||||
path,
|
|
||||||
propertyNumber,
|
|
||||||
associationType,
|
|
||||||
metadataFetchMode,
|
|
||||||
metadataCascadeStyle,
|
|
||||||
lhsTable,
|
|
||||||
lhsColumns,
|
|
||||||
nullable,
|
|
||||||
currentDepth
|
|
||||||
);
|
|
||||||
//we can use an inner join for the many-to-many
|
|
||||||
if ( joinType==JoinType.LEFT_OUTER_JOIN && path.isRoot() ) {
|
|
||||||
joinType=JoinType.INNER_JOIN;
|
|
||||||
}
|
|
||||||
return joinType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString() {
|
|
||||||
return getClass().getName() + '(' + collectionPersister.getRole() + ')';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.collection;
|
|
||||||
|
|
||||||
import org.hibernate.MappingException;
|
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.internal.CoreMessageLogger;
|
|
||||||
import org.hibernate.loader.JoinWalker;
|
|
||||||
import org.hibernate.persister.collection.QueryableCollection;
|
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads a collection of values or a many-to-many association.
|
|
||||||
* <br>
|
|
||||||
* The collection persister must implement <tt>QueryableCOllection<tt>. For
|
|
||||||
* other collections, create a customized subclass of <tt>Loader</tt>.
|
|
||||||
*
|
|
||||||
* @see OneToManyLoader
|
|
||||||
* @author Gavin King
|
|
||||||
*/
|
|
||||||
public class BasicCollectionLoader extends CollectionLoader {
|
|
||||||
|
|
||||||
private static final CoreMessageLogger LOG = Logger.getMessageLogger( CoreMessageLogger.class, BasicCollectionLoader.class.getName() );
|
|
||||||
|
|
||||||
public BasicCollectionLoader(
|
|
||||||
QueryableCollection collectionPersister,
|
|
||||||
SessionFactoryImplementor session,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
|
||||||
this( collectionPersister, 1, session, loadQueryInfluencers );
|
|
||||||
}
|
|
||||||
|
|
||||||
public BasicCollectionLoader(
|
|
||||||
QueryableCollection collectionPersister,
|
|
||||||
int batchSize,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
|
||||||
this( collectionPersister, batchSize, null, factory, loadQueryInfluencers );
|
|
||||||
}
|
|
||||||
|
|
||||||
protected BasicCollectionLoader(
|
|
||||||
QueryableCollection collectionPersister,
|
|
||||||
int batchSize,
|
|
||||||
String subquery,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
|
||||||
super( collectionPersister, factory, loadQueryInfluencers );
|
|
||||||
|
|
||||||
JoinWalker walker = new BasicCollectionJoinWalker(
|
|
||||||
collectionPersister,
|
|
||||||
batchSize,
|
|
||||||
subquery,
|
|
||||||
factory,
|
|
||||||
loadQueryInfluencers
|
|
||||||
);
|
|
||||||
initFromWalker( walker );
|
|
||||||
|
|
||||||
postInstantiate();
|
|
||||||
|
|
||||||
if ( LOG.isDebugEnabled() ) {
|
|
||||||
LOG.debugf( "Static select for collection %s: %s", collectionPersister.getRole(), getSQLString() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.collection;
|
|
||||||
|
|
||||||
import org.hibernate.persister.collection.CollectionPersister;
|
|
||||||
import org.hibernate.persister.collection.QueryableCollection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The base contract for loaders capable of performing batch-fetch loading of collections using multiple foreign key
|
|
||||||
* values in the SQL <tt>WHERE</tt> clause.
|
|
||||||
*
|
|
||||||
* @author Gavin King
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*
|
|
||||||
* @see BatchingCollectionInitializerBuilder
|
|
||||||
* @see BasicCollectionLoader
|
|
||||||
* @see OneToManyLoader
|
|
||||||
*/
|
|
||||||
public abstract class BatchingCollectionInitializer implements CollectionInitializer {
|
|
||||||
private final QueryableCollection collectionPersister;
|
|
||||||
|
|
||||||
public BatchingCollectionInitializer(QueryableCollection collectionPersister) {
|
|
||||||
this.collectionPersister = collectionPersister;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CollectionPersister getCollectionPersister() {
|
|
||||||
return collectionPersister;
|
|
||||||
}
|
|
||||||
|
|
||||||
public QueryableCollection collectionPersister() {
|
|
||||||
return collectionPersister;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,105 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.collection;
|
|
||||||
|
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.persister.collection.QueryableCollection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Contract for building {@link CollectionInitializer} instances capable of performing batch-fetch loading.
|
|
||||||
*
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*
|
|
||||||
* @see org.hibernate.loader.BatchFetchStyle
|
|
||||||
*/
|
|
||||||
public abstract class BatchingCollectionInitializerBuilder {
|
|
||||||
public static BatchingCollectionInitializerBuilder getBuilder(SessionFactoryImplementor factory) {
|
|
||||||
switch ( factory.getSettings().getBatchFetchStyle() ) {
|
|
||||||
case PADDED: {
|
|
||||||
return PaddedBatchingCollectionInitializerBuilder.INSTANCE;
|
|
||||||
}
|
|
||||||
case DYNAMIC: {
|
|
||||||
return DynamicBatchingCollectionInitializerBuilder.INSTANCE;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return org.hibernate.loader.collection.plan.LegacyBatchingCollectionInitializerBuilder.INSTANCE;
|
|
||||||
//return LegacyBatchingCollectionInitializerBuilder.INSTANCE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds a batch-fetch capable CollectionInitializer for basic and many-to-many collections (collections with
|
|
||||||
* a dedicated collection table).
|
|
||||||
*
|
|
||||||
* @param persister THe collection persister
|
|
||||||
* @param maxBatchSize The maximum number of keys to batch-fetch together
|
|
||||||
* @param factory The SessionFactory
|
|
||||||
* @param influencers Any influencers that should affect the built query
|
|
||||||
*
|
|
||||||
* @return The batch-fetch capable collection initializer
|
|
||||||
*/
|
|
||||||
public CollectionInitializer createBatchingCollectionInitializer(
|
|
||||||
QueryableCollection persister,
|
|
||||||
int maxBatchSize,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers influencers) {
|
|
||||||
if ( maxBatchSize <= 1 ) {
|
|
||||||
// no batching
|
|
||||||
return buildNonBatchingLoader( persister, factory, influencers );
|
|
||||||
}
|
|
||||||
|
|
||||||
return createRealBatchingCollectionInitializer( persister, maxBatchSize, factory, influencers );
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract CollectionInitializer createRealBatchingCollectionInitializer(
|
|
||||||
QueryableCollection persister,
|
|
||||||
int maxBatchSize,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers influencers);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds a batch-fetch capable CollectionInitializer for one-to-many collections (collections without
|
|
||||||
* a dedicated collection table).
|
|
||||||
*
|
|
||||||
* @param persister THe collection persister
|
|
||||||
* @param maxBatchSize The maximum number of keys to batch-fetch together
|
|
||||||
* @param factory The SessionFactory
|
|
||||||
* @param influencers Any influencers that should affect the built query
|
|
||||||
*
|
|
||||||
* @return The batch-fetch capable collection initializer
|
|
||||||
*/
|
|
||||||
public CollectionInitializer createBatchingOneToManyInitializer(
|
|
||||||
QueryableCollection persister,
|
|
||||||
int maxBatchSize,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers influencers) {
|
|
||||||
if ( maxBatchSize <= 1 ) {
|
|
||||||
// no batching
|
|
||||||
return buildNonBatchingLoader( persister, factory, influencers );
|
|
||||||
}
|
|
||||||
|
|
||||||
return createRealBatchingOneToManyInitializer( persister, maxBatchSize, factory, influencers );
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract CollectionInitializer createRealBatchingOneToManyInitializer(
|
|
||||||
QueryableCollection persister,
|
|
||||||
int maxBatchSize,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers influencers);
|
|
||||||
|
|
||||||
protected CollectionInitializer buildNonBatchingLoader(
|
|
||||||
QueryableCollection persister,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers influencers) {
|
|
||||||
return persister.isOneToMany() ?
|
|
||||||
new OneToManyLoader( persister, factory, influencers ) :
|
|
||||||
new BasicCollectionLoader( persister, factory, influencers );
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.collection;
|
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An interface for collection loaders
|
|
||||||
* @see BasicCollectionLoader
|
|
||||||
* @see OneToManyLoader
|
|
||||||
* @author Gavin King
|
|
||||||
*/
|
|
||||||
public interface CollectionInitializer {
|
|
||||||
/**
|
|
||||||
* Initialize the given collection
|
|
||||||
*/
|
|
||||||
void initialize(Object id, SharedSessionContractImplementor session) throws HibernateException;
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.collection;
|
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.internal.util.StringHelper;
|
|
||||||
import org.hibernate.loader.JoinWalker;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Superclass of walkers for collection initializers
|
|
||||||
*
|
|
||||||
* @see CollectionLoader
|
|
||||||
* @see OneToManyJoinWalker
|
|
||||||
* @see BasicCollectionJoinWalker
|
|
||||||
* @author Gavin King
|
|
||||||
*/
|
|
||||||
public abstract class CollectionJoinWalker extends JoinWalker {
|
|
||||||
public CollectionJoinWalker(SessionFactoryImplementor factory, LoadQueryInfluencers loadQueryInfluencers) {
|
|
||||||
super( factory, loadQueryInfluencers );
|
|
||||||
}
|
|
||||||
|
|
||||||
protected StringBuilder whereString(String alias, String[] columnNames, String subselect, int batchSize) {
|
|
||||||
if (subselect==null) {
|
|
||||||
return whereString(alias, columnNames, batchSize);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
StringBuilder buf = new StringBuilder();
|
|
||||||
if (columnNames.length>1) {
|
|
||||||
buf.append('(');
|
|
||||||
}
|
|
||||||
buf.append( String.join(", ", StringHelper.qualify(alias, columnNames) ) );
|
|
||||||
if (columnNames.length>1) {
|
|
||||||
buf.append(')');
|
|
||||||
}
|
|
||||||
buf.append(" in ")
|
|
||||||
.append('(')
|
|
||||||
.append(subselect)
|
|
||||||
.append(')');
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.collection;
|
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|
||||||
import org.hibernate.loader.OuterJoinLoader;
|
|
||||||
import org.hibernate.persister.collection.QueryableCollection;
|
|
||||||
import org.hibernate.type.Type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Superclass for loaders that initialize collections
|
|
||||||
*
|
|
||||||
* @see OneToManyLoader
|
|
||||||
* @see BasicCollectionLoader
|
|
||||||
* @author Gavin King
|
|
||||||
*/
|
|
||||||
public class CollectionLoader extends OuterJoinLoader implements CollectionInitializer {
|
|
||||||
|
|
||||||
private final QueryableCollection collectionPersister;
|
|
||||||
|
|
||||||
public CollectionLoader(
|
|
||||||
QueryableCollection collectionPersister,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) {
|
|
||||||
super( factory, loadQueryInfluencers );
|
|
||||||
this.collectionPersister = collectionPersister;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected QueryableCollection collectionPersister() {
|
|
||||||
return collectionPersister;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isSubselectLoadingEnabled() {
|
|
||||||
return hasSubselectLoadableCollections();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initialize(Object id, SharedSessionContractImplementor session) throws HibernateException {
|
|
||||||
loadCollection( session, id, getKeyType() );
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Type getKeyType() {
|
|
||||||
return collectionPersister.getKeyType();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return getClass().getName() + '(' + collectionPersister.getRole() + ')';
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,257 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.collection;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.sql.Statement;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
|
||||||
import org.hibernate.dialect.pagination.LimitHelper;
|
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|
||||||
import org.hibernate.engine.spi.PersistenceContext;
|
|
||||||
import org.hibernate.engine.spi.QueryParameters;
|
|
||||||
import org.hibernate.engine.spi.RowSelection;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|
||||||
import org.hibernate.internal.util.StringHelper;
|
|
||||||
import org.hibernate.internal.util.collections.ArrayHelper;
|
|
||||||
import org.hibernate.loader.JoinWalker;
|
|
||||||
import org.hibernate.loader.Loader;
|
|
||||||
import org.hibernate.loader.ast.spi.AfterLoadAction;
|
|
||||||
import org.hibernate.persister.collection.QueryableCollection;
|
|
||||||
import org.hibernate.pretty.MessageHelper;
|
|
||||||
import org.hibernate.type.Type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A BatchingCollectionInitializerBuilder that builds CollectionInitializer instances capable of dynamically building
|
|
||||||
* its batch-fetch SQL based on the actual number of collections keys waiting to be fetched.
|
|
||||||
*
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public class DynamicBatchingCollectionInitializerBuilder extends BatchingCollectionInitializerBuilder {
|
|
||||||
public static final DynamicBatchingCollectionInitializerBuilder INSTANCE = new DynamicBatchingCollectionInitializerBuilder();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected CollectionInitializer createRealBatchingCollectionInitializer(
|
|
||||||
QueryableCollection persister,
|
|
||||||
int maxBatchSize,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers influencers) {
|
|
||||||
return new DynamicBatchingCollectionInitializer( persister, maxBatchSize, factory, influencers );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected CollectionInitializer createRealBatchingOneToManyInitializer(
|
|
||||||
QueryableCollection persister,
|
|
||||||
int maxBatchSize,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers influencers) {
|
|
||||||
return new DynamicBatchingCollectionInitializer( persister, maxBatchSize, factory, influencers );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class DynamicBatchingCollectionInitializer extends BatchingCollectionInitializer {
|
|
||||||
private final int maxBatchSize;
|
|
||||||
private final Loader singleKeyLoader;
|
|
||||||
private final DynamicBatchingCollectionLoader batchLoader;
|
|
||||||
|
|
||||||
public DynamicBatchingCollectionInitializer(
|
|
||||||
QueryableCollection collectionPersister,
|
|
||||||
int maxBatchSize,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers influencers) {
|
|
||||||
super( collectionPersister );
|
|
||||||
this.maxBatchSize = maxBatchSize;
|
|
||||||
|
|
||||||
if ( collectionPersister.isOneToMany() ) {
|
|
||||||
this.singleKeyLoader = new OneToManyLoader( collectionPersister, 1, factory, influencers );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.singleKeyLoader = new BasicCollectionLoader( collectionPersister, 1, factory, influencers );
|
|
||||||
}
|
|
||||||
|
|
||||||
this.batchLoader = new DynamicBatchingCollectionLoader( collectionPersister, factory, influencers );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initialize(Object id, SharedSessionContractImplementor session) throws HibernateException {
|
|
||||||
// first, figure out how many batchable ids we have...
|
|
||||||
final Object[] batch = session.getPersistenceContextInternal()
|
|
||||||
.getBatchFetchQueue()
|
|
||||||
.getCollectionBatch( collectionPersister(), id, maxBatchSize );
|
|
||||||
final int numberOfIds = ArrayHelper.countNonNull( batch );
|
|
||||||
if ( numberOfIds <= 1 ) {
|
|
||||||
singleKeyLoader.loadCollection( session, id, collectionPersister().getKeyType() );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Serializable[] idsToLoad = new Serializable[numberOfIds];
|
|
||||||
System.arraycopy( batch, 0, idsToLoad, 0, numberOfIds );
|
|
||||||
|
|
||||||
batchLoader.doBatchedCollectionLoad( session, idsToLoad, collectionPersister().getKeyType() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class DynamicBatchingCollectionLoader extends CollectionLoader {
|
|
||||||
// todo : this represents another case where the current Loader contract is unhelpful
|
|
||||||
// the other recent case was stored procedure support. Really any place where the SQL
|
|
||||||
// generation is dynamic but the "loading plan" remains constant. The long term plan
|
|
||||||
// is to split Loader into (a) PreparedStatement generation/execution and (b) ResultSet
|
|
||||||
// processing.
|
|
||||||
//
|
|
||||||
// Same holds true for org.hibernate.loader.entity.DynamicBatchingEntityLoaderBuilder.DynamicBatchingEntityLoader
|
|
||||||
//
|
|
||||||
// for now I will essentially semi-re-implement the collection loader contract here to be able to alter
|
|
||||||
// the SQL (specifically to be able to dynamically build the WHERE-clause IN-condition) later, when
|
|
||||||
// we actually know the ids to batch fetch
|
|
||||||
|
|
||||||
private final String sqlTemplate;
|
|
||||||
private final String alias;
|
|
||||||
|
|
||||||
public DynamicBatchingCollectionLoader(
|
|
||||||
QueryableCollection collectionPersister,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers influencers) {
|
|
||||||
super( collectionPersister, factory, influencers );
|
|
||||||
|
|
||||||
JoinWalker walker = buildJoinWalker( collectionPersister, factory, influencers );
|
|
||||||
initFromWalker( walker );
|
|
||||||
this.sqlTemplate = walker.getSQLString();
|
|
||||||
this.alias = StringHelper.generateAlias( collectionPersister.getRole(), 0 );
|
|
||||||
postInstantiate();
|
|
||||||
|
|
||||||
if ( LOG.isDebugEnabled() ) {
|
|
||||||
LOG.debugf(
|
|
||||||
"SQL-template for dynamic collection [%s] batch-fetching : %s",
|
|
||||||
collectionPersister.getRole(),
|
|
||||||
sqlTemplate
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private JoinWalker buildJoinWalker(
|
|
||||||
QueryableCollection collectionPersister,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers influencers) {
|
|
||||||
|
|
||||||
if ( collectionPersister.isOneToMany() ) {
|
|
||||||
return new OneToManyJoinWalker( collectionPersister, -1, null, factory, influencers ) {
|
|
||||||
@Override
|
|
||||||
protected StringBuilder whereString(String alias, String[] columnNames, String subselect, int batchSize) {
|
|
||||||
if ( subselect != null ) {
|
|
||||||
return super.whereString( alias, columnNames, subselect, batchSize );
|
|
||||||
}
|
|
||||||
|
|
||||||
return StringHelper.buildBatchFetchRestrictionFragment( alias, columnNames, getFactory().getDialect() );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return new BasicCollectionJoinWalker( collectionPersister, -1, null, factory, influencers ) {
|
|
||||||
@Override
|
|
||||||
protected StringBuilder whereString(String alias, String[] columnNames, String subselect, int batchSize) {
|
|
||||||
if ( subselect != null ) {
|
|
||||||
return super.whereString( alias, columnNames, subselect, batchSize );
|
|
||||||
}
|
|
||||||
|
|
||||||
return StringHelper.buildBatchFetchRestrictionFragment( alias, columnNames, getFactory().getDialect() );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void doBatchedCollectionLoad(
|
|
||||||
final SharedSessionContractImplementor session,
|
|
||||||
final Serializable[] ids,
|
|
||||||
final Type type) throws HibernateException {
|
|
||||||
|
|
||||||
if ( LOG.isDebugEnabled() ) {
|
|
||||||
LOG.debugf(
|
|
||||||
"Batch loading collection: %s",
|
|
||||||
MessageHelper.collectionInfoString( getCollectionPersisters()[0], ids, getFactory() )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final Type[] idTypes = new Type[ids.length];
|
|
||||||
Arrays.fill( idTypes, type );
|
|
||||||
final QueryParameters queryParameters = new QueryParameters( idTypes, ids, ids );
|
|
||||||
|
|
||||||
final String sql = StringHelper.expandBatchIdPlaceholder(
|
|
||||||
sqlTemplate,
|
|
||||||
ids,
|
|
||||||
alias,
|
|
||||||
collectionPersister().getKeyColumnNames(),
|
|
||||||
session.getJdbcServices().getJdbcEnvironment().getDialect()
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
|
||||||
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
|
|
||||||
boolean defaultReadOnlyOrig = persistenceContext.isDefaultReadOnly();
|
|
||||||
if ( queryParameters.isReadOnlyInitialized() ) {
|
|
||||||
// The read-only/modifiable mode for the query was explicitly set.
|
|
||||||
// Temporarily set the default read-only/modifiable setting to the query's setting.
|
|
||||||
persistenceContext.setDefaultReadOnly( queryParameters.isReadOnly() );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// The read-only/modifiable setting for the query was not initialized.
|
|
||||||
// Use the default read-only/modifiable from the persistence context instead.
|
|
||||||
queryParameters.setReadOnly( persistenceContext.isDefaultReadOnly() );
|
|
||||||
}
|
|
||||||
persistenceContext.beforeLoad();
|
|
||||||
try {
|
|
||||||
try {
|
|
||||||
doTheLoad( sql, queryParameters, session );
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
persistenceContext.afterLoad();
|
|
||||||
}
|
|
||||||
persistenceContext.initializeNonLazyCollections();
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
// Restore the original default
|
|
||||||
persistenceContext.setDefaultReadOnly( defaultReadOnlyOrig );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch ( SQLException e ) {
|
|
||||||
throw session.getJdbcServices().getSqlExceptionHelper().convert(
|
|
||||||
e,
|
|
||||||
"could not initialize a collection batch: " +
|
|
||||||
MessageHelper.collectionInfoString( collectionPersister(), ids, getFactory() ),
|
|
||||||
sql
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG.debug( "Done batch load" );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void doTheLoad(String sql, QueryParameters queryParameters, SharedSessionContractImplementor session) throws SQLException {
|
|
||||||
final RowSelection selection = queryParameters.getRowSelection();
|
|
||||||
final int maxRows = LimitHelper.hasMaxRows( selection ) ?
|
|
||||||
selection.getMaxRows() :
|
|
||||||
Integer.MAX_VALUE;
|
|
||||||
|
|
||||||
final List<AfterLoadAction> afterLoadActions = Collections.emptyList();
|
|
||||||
final SqlStatementWrapper wrapper = executeQueryStatement( sql, queryParameters, false, afterLoadActions, session );
|
|
||||||
final ResultSet rs = wrapper.getResultSet();
|
|
||||||
final Statement st = wrapper.getStatement();
|
|
||||||
try {
|
|
||||||
processResultSet( rs, queryParameters, session, true, null, maxRows, afterLoadActions );
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
session.getJdbcCoordinator().getLogicalConnection().getResourceRegistry().release( st );
|
|
||||||
session.getJdbcCoordinator().afterStatementExecution();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,86 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.collection;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
|
||||||
import org.hibernate.MappingException;
|
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|
||||||
import org.hibernate.internal.util.collections.ArrayHelper;
|
|
||||||
import org.hibernate.loader.Loader;
|
|
||||||
import org.hibernate.persister.collection.QueryableCollection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public class LegacyBatchingCollectionInitializerBuilder extends BatchingCollectionInitializerBuilder {
|
|
||||||
public static final LegacyBatchingCollectionInitializerBuilder INSTANCE = new LegacyBatchingCollectionInitializerBuilder();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected CollectionInitializer createRealBatchingCollectionInitializer(
|
|
||||||
QueryableCollection persister,
|
|
||||||
int maxBatchSize,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
|
||||||
int[] batchSizes = ArrayHelper.getBatchSizes( maxBatchSize );
|
|
||||||
Loader[] loaders = new Loader[ batchSizes.length ];
|
|
||||||
for ( int i = 0; i < batchSizes.length; i++ ) {
|
|
||||||
loaders[i] = new BasicCollectionLoader( persister, batchSizes[i], factory, loadQueryInfluencers );
|
|
||||||
}
|
|
||||||
return new LegacyBatchingCollectionInitializer( persister, batchSizes, loaders );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected CollectionInitializer createRealBatchingOneToManyInitializer(
|
|
||||||
QueryableCollection persister,
|
|
||||||
int maxBatchSize,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
|
||||||
final int[] batchSizes = ArrayHelper.getBatchSizes( maxBatchSize );
|
|
||||||
final Loader[] loaders = new Loader[ batchSizes.length ];
|
|
||||||
for ( int i = 0; i < batchSizes.length; i++ ) {
|
|
||||||
loaders[i] = new OneToManyLoader( persister, batchSizes[i], factory, loadQueryInfluencers );
|
|
||||||
}
|
|
||||||
return new LegacyBatchingCollectionInitializer( persister, batchSizes, loaders );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static class LegacyBatchingCollectionInitializer extends BatchingCollectionInitializer {
|
|
||||||
private final int[] batchSizes;
|
|
||||||
private final Loader[] loaders;
|
|
||||||
|
|
||||||
public LegacyBatchingCollectionInitializer(
|
|
||||||
QueryableCollection persister,
|
|
||||||
int[] batchSizes,
|
|
||||||
Loader[] loaders) {
|
|
||||||
super( persister );
|
|
||||||
this.batchSizes = batchSizes;
|
|
||||||
this.loaders = loaders;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initialize(Object id, SharedSessionContractImplementor session) throws HibernateException {
|
|
||||||
final Object[] batch = session.getPersistenceContextInternal().getBatchFetchQueue()
|
|
||||||
.getCollectionBatch( collectionPersister(), id, batchSizes[0] );
|
|
||||||
|
|
||||||
for ( int i=0; i<batchSizes.length-1; i++) {
|
|
||||||
final int smallBatchSize = batchSizes[i];
|
|
||||||
if ( batch[smallBatchSize-1]!=null ) {
|
|
||||||
Serializable[] smallBatch = new Serializable[smallBatchSize];
|
|
||||||
System.arraycopy(batch, 0, smallBatch, 0, smallBatchSize);
|
|
||||||
loaders[i].loadCollectionBatch( session, smallBatch, collectionPersister().getKeyType() );
|
|
||||||
return; //EARLY EXIT!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loaders[batchSizes.length-1].loadCollection( session, id, collectionPersister().getKeyType() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,133 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.collection;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.hibernate.LockMode;
|
|
||||||
import org.hibernate.MappingException;
|
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.internal.util.StringHelper;
|
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Walker for one-to-many associations
|
|
||||||
*
|
|
||||||
* @author Gavin King
|
|
||||||
* @see OneToManyLoader
|
|
||||||
*/
|
|
||||||
public class OneToManyJoinWalker extends CollectionJoinWalker {
|
|
||||||
|
|
||||||
private final QueryableCollection oneToManyPersister;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isDuplicateAssociation(
|
|
||||||
final String foreignKeyTable,
|
|
||||||
final String[] foreignKeyColumns) {
|
|
||||||
//disable a join back to this same association
|
|
||||||
final boolean isSameJoin = oneToManyPersister.getTableName().equals( foreignKeyTable ) &&
|
|
||||||
Arrays.equals( foreignKeyColumns, oneToManyPersister.getKeyColumnNames() );
|
|
||||||
return isSameJoin ||
|
|
||||||
super.isDuplicateAssociation( foreignKeyTable, foreignKeyColumns );
|
|
||||||
}
|
|
||||||
|
|
||||||
public OneToManyJoinWalker(
|
|
||||||
QueryableCollection oneToManyPersister,
|
|
||||||
int batchSize,
|
|
||||||
String subquery,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
|
||||||
super( factory, loadQueryInfluencers );
|
|
||||||
|
|
||||||
this.oneToManyPersister = oneToManyPersister;
|
|
||||||
|
|
||||||
final OuterJoinLoadable elementPersister = (OuterJoinLoadable) oneToManyPersister.getElementPersister();
|
|
||||||
final String alias = generateRootAlias( oneToManyPersister.getRole() );
|
|
||||||
|
|
||||||
walkEntityTree( elementPersister, alias );
|
|
||||||
|
|
||||||
List allAssociations = new ArrayList( associations );
|
|
||||||
allAssociations.add(
|
|
||||||
OuterJoinableAssociation.createRoot(
|
|
||||||
oneToManyPersister.getCollectionType(),
|
|
||||||
alias,
|
|
||||||
getFactory()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
initPersisters( allAssociations, LockMode.NONE );
|
|
||||||
initStatementString( elementPersister, alias, batchSize, subquery );
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initStatementString(
|
|
||||||
final OuterJoinLoadable elementPersister,
|
|
||||||
final String alias,
|
|
||||||
final int batchSize,
|
|
||||||
final String subquery)
|
|
||||||
throws MappingException {
|
|
||||||
|
|
||||||
final int joins = countEntityPersisters( associations );
|
|
||||||
suffixes = BasicLoader.generateSuffixes( joins + 1 );
|
|
||||||
|
|
||||||
final int collectionJoins = countCollectionPersisters( associations ) + 1;
|
|
||||||
collectionSuffixes = BasicLoader.generateSuffixes( joins + 1, collectionJoins );
|
|
||||||
|
|
||||||
StringBuilder whereString = whereString(
|
|
||||||
alias,
|
|
||||||
oneToManyPersister.getKeyColumnNames(),
|
|
||||||
subquery,
|
|
||||||
batchSize
|
|
||||||
);
|
|
||||||
String filter = oneToManyPersister.filterFragment( alias, getLoadQueryInfluencers().getEnabledFilters() );
|
|
||||||
whereString.insert( 0, StringHelper.moveAndToBeginning( filter ) );
|
|
||||||
|
|
||||||
JoinFragment ojf = mergeOuterJoins( associations );
|
|
||||||
Select select = new Select( getDialect() )
|
|
||||||
.setSelectClause(
|
|
||||||
oneToManyPersister.selectFragment(
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
alias,
|
|
||||||
suffixes[joins],
|
|
||||||
collectionSuffixes[0],
|
|
||||||
true
|
|
||||||
) +
|
|
||||||
selectString( associations )
|
|
||||||
)
|
|
||||||
.setFromClause(
|
|
||||||
elementPersister.fromTableFragment( alias ) +
|
|
||||||
elementPersister.fromJoinFragment( alias, true, true )
|
|
||||||
)
|
|
||||||
.setWhereClause( whereString.toString() )
|
|
||||||
.setOuterJoins(
|
|
||||||
ojf.toFromFragmentString(),
|
|
||||||
ojf.toWhereFragmentString() +
|
|
||||||
elementPersister.whereJoinFragment( alias, true, true )
|
|
||||||
);
|
|
||||||
|
|
||||||
select.setOrderByClause( orderBy( associations, oneToManyPersister.getSQLOrderByString( alias ) ) );
|
|
||||||
|
|
||||||
if ( getFactory().getSessionFactoryOptions().isCommentsEnabled() ) {
|
|
||||||
select.setComment( "load one-to-many " + oneToManyPersister.getRole() );
|
|
||||||
}
|
|
||||||
|
|
||||||
sql = select.toStatementString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return getClass().getName() + '(' + oneToManyPersister.getRole() + ')';
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,68 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.collection;
|
|
||||||
|
|
||||||
import org.hibernate.MappingException;
|
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.internal.CoreMessageLogger;
|
|
||||||
import org.hibernate.loader.JoinWalker;
|
|
||||||
import org.hibernate.persister.collection.QueryableCollection;
|
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads one-to-many associations<br>
|
|
||||||
* <br>
|
|
||||||
* The collection persister must implement <tt>QueryableCOllection<tt>. For
|
|
||||||
* other collections, create a customized subclass of <tt>Loader</tt>.
|
|
||||||
*
|
|
||||||
* @see BasicCollectionLoader
|
|
||||||
* @author Gavin King
|
|
||||||
*/
|
|
||||||
public class OneToManyLoader extends CollectionLoader {
|
|
||||||
|
|
||||||
private static final CoreMessageLogger LOG = Logger.getMessageLogger( CoreMessageLogger.class, OneToManyLoader.class.getName() );
|
|
||||||
|
|
||||||
public OneToManyLoader(
|
|
||||||
QueryableCollection oneToManyPersister,
|
|
||||||
SessionFactoryImplementor session,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
|
||||||
this( oneToManyPersister, 1, session, loadQueryInfluencers );
|
|
||||||
}
|
|
||||||
|
|
||||||
public OneToManyLoader(
|
|
||||||
QueryableCollection oneToManyPersister,
|
|
||||||
int batchSize,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
|
||||||
this( oneToManyPersister, batchSize, null, factory, loadQueryInfluencers );
|
|
||||||
}
|
|
||||||
|
|
||||||
public OneToManyLoader(
|
|
||||||
QueryableCollection oneToManyPersister,
|
|
||||||
int batchSize,
|
|
||||||
String subquery,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
|
||||||
super( oneToManyPersister, factory, loadQueryInfluencers );
|
|
||||||
|
|
||||||
JoinWalker walker = new OneToManyJoinWalker(
|
|
||||||
oneToManyPersister,
|
|
||||||
batchSize,
|
|
||||||
subquery,
|
|
||||||
factory,
|
|
||||||
loadQueryInfluencers
|
|
||||||
);
|
|
||||||
initFromWalker( walker );
|
|
||||||
|
|
||||||
postInstantiate();
|
|
||||||
if ( LOG.isDebugEnabled() ) {
|
|
||||||
LOG.debugf( "Static select for one-to-many %s: %s", oneToManyPersister.getRole(), getSQLString() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,102 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.collection;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
|
||||||
import org.hibernate.MappingException;
|
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|
||||||
import org.hibernate.internal.util.collections.ArrayHelper;
|
|
||||||
import org.hibernate.loader.Loader;
|
|
||||||
import org.hibernate.persister.collection.QueryableCollection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A batch-fetch capable CollectionInitializer that performs batch-fetching using the padded style. See
|
|
||||||
* {@link org.hibernate.loader.BatchFetchStyle} for a discussion of the different styles.
|
|
||||||
*
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*
|
|
||||||
* @see org.hibernate.loader.BatchFetchStyle#PADDED
|
|
||||||
*/
|
|
||||||
public class PaddedBatchingCollectionInitializerBuilder extends BatchingCollectionInitializerBuilder {
|
|
||||||
public static final PaddedBatchingCollectionInitializerBuilder INSTANCE = new PaddedBatchingCollectionInitializerBuilder();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CollectionInitializer createRealBatchingCollectionInitializer(
|
|
||||||
QueryableCollection persister,
|
|
||||||
int maxBatchSize,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
|
||||||
int[] batchSizes = ArrayHelper.getBatchSizes( maxBatchSize );
|
|
||||||
Loader[] loaders = new Loader[ batchSizes.length ];
|
|
||||||
for ( int i = 0; i < batchSizes.length; i++ ) {
|
|
||||||
loaders[i] = new BasicCollectionLoader( persister, batchSizes[i], factory, loadQueryInfluencers );
|
|
||||||
}
|
|
||||||
return new PaddedBatchingCollectionInitializer( persister, batchSizes, loaders );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CollectionInitializer createRealBatchingOneToManyInitializer(
|
|
||||||
QueryableCollection persister,
|
|
||||||
int maxBatchSize,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
|
||||||
final int[] batchSizes = ArrayHelper.getBatchSizes( maxBatchSize );
|
|
||||||
final Loader[] loaders = new Loader[ batchSizes.length ];
|
|
||||||
for ( int i = 0; i < batchSizes.length; i++ ) {
|
|
||||||
loaders[i] = new OneToManyLoader( persister, batchSizes[i], factory, loadQueryInfluencers );
|
|
||||||
}
|
|
||||||
return new PaddedBatchingCollectionInitializer( persister, batchSizes, loaders );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static class PaddedBatchingCollectionInitializer extends BatchingCollectionInitializer {
|
|
||||||
private final int[] batchSizes;
|
|
||||||
private final Loader[] loaders;
|
|
||||||
|
|
||||||
public PaddedBatchingCollectionInitializer(QueryableCollection persister, int[] batchSizes, Loader[] loaders) {
|
|
||||||
super( persister );
|
|
||||||
|
|
||||||
this.batchSizes = batchSizes;
|
|
||||||
this.loaders = loaders;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initialize(Object id, SharedSessionContractImplementor session) throws HibernateException {
|
|
||||||
final Object[] batch = session.getPersistenceContextInternal()
|
|
||||||
.getBatchFetchQueue()
|
|
||||||
.getCollectionBatch( collectionPersister(), id, batchSizes[0] );
|
|
||||||
final int numberOfIds = ArrayHelper.countNonNull( batch );
|
|
||||||
if ( numberOfIds <= 1 ) {
|
|
||||||
loaders[batchSizes.length-1].loadCollection( session, id, collectionPersister().getKeyType() );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uses the first batch-size bigger than the number of actual ids in the batch
|
|
||||||
int indexToUse = batchSizes.length-1;
|
|
||||||
for ( int i = 0; i < batchSizes.length-1; i++ ) {
|
|
||||||
if ( batchSizes[i] >= numberOfIds ) {
|
|
||||||
indexToUse = i;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final Serializable[] idsToLoad = new Serializable[ batchSizes[indexToUse] ];
|
|
||||||
System.arraycopy( batch, 0, idsToLoad, 0, numberOfIds );
|
|
||||||
for ( int i = numberOfIds; i < batchSizes[indexToUse]; i++ ) {
|
|
||||||
idsToLoad[i] = (Serializable) id;
|
|
||||||
}
|
|
||||||
|
|
||||||
loaders[indexToUse].loadCollectionBatch( session, idsToLoad, collectionPersister().getKeyType() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,79 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.collection;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
|
||||||
import org.hibernate.MappingException;
|
|
||||||
import org.hibernate.engine.spi.EntityKey;
|
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|
||||||
import org.hibernate.engine.spi.QueryParameters;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|
||||||
import org.hibernate.engine.spi.TypedValue;
|
|
||||||
import org.hibernate.persister.collection.QueryableCollection;
|
|
||||||
import org.hibernate.type.Type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements subselect fetching for a collection
|
|
||||||
* @author Gavin King
|
|
||||||
*/
|
|
||||||
public class SubselectCollectionLoader extends BasicCollectionLoader {
|
|
||||||
|
|
||||||
private final Serializable[] keys;
|
|
||||||
private final Type[] types;
|
|
||||||
private final Object[] values;
|
|
||||||
private final Map<String, TypedValue> namedParameters;
|
|
||||||
private final Map<String, int[]> namedParameterLocMap;
|
|
||||||
|
|
||||||
public SubselectCollectionLoader(
|
|
||||||
QueryableCollection persister,
|
|
||||||
String subquery,
|
|
||||||
Collection entityKeys,
|
|
||||||
QueryParameters queryParameters,
|
|
||||||
Map<String, int[]> namedParameterLocMap,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
|
||||||
super( persister, 1, subquery, factory, loadQueryInfluencers );
|
|
||||||
|
|
||||||
keys = new Serializable[ entityKeys.size() ];
|
|
||||||
Iterator iter = entityKeys.iterator();
|
|
||||||
int i=0;
|
|
||||||
while ( iter.hasNext() ) {
|
|
||||||
keys[i++] = ( (EntityKey) iter.next() ).getIdentifier();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.namedParameters = queryParameters.getNamedParameters();
|
|
||||||
this.types = queryParameters.getFilteredPositionalParameterTypes();
|
|
||||||
this.values = queryParameters.getFilteredPositionalParameterValues();
|
|
||||||
this.namedParameterLocMap = namedParameterLocMap;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initialize(Object id, SharedSessionContractImplementor session)
|
|
||||||
throws HibernateException {
|
|
||||||
loadCollectionSubselect(
|
|
||||||
session,
|
|
||||||
keys,
|
|
||||||
values,
|
|
||||||
types,
|
|
||||||
namedParameters,
|
|
||||||
getKeyType()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int[] getNamedParameterLocs(String name) {
|
|
||||||
return namedParameterLocMap.get( name );
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,76 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.collection;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
|
||||||
import org.hibernate.MappingException;
|
|
||||||
import org.hibernate.engine.spi.EntityKey;
|
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|
||||||
import org.hibernate.engine.spi.QueryParameters;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|
||||||
import org.hibernate.engine.spi.TypedValue;
|
|
||||||
import org.hibernate.persister.collection.QueryableCollection;
|
|
||||||
import org.hibernate.type.Type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements subselect fetching for a one to many association
|
|
||||||
* @author Gavin King
|
|
||||||
*/
|
|
||||||
public class SubselectOneToManyLoader extends OneToManyLoader {
|
|
||||||
|
|
||||||
private final Serializable[] keys;
|
|
||||||
private final Type[] types;
|
|
||||||
private final Object[] values;
|
|
||||||
private final Map<String, TypedValue> namedParameters;
|
|
||||||
private final Map<String, int[]> namedParameterLocMap;
|
|
||||||
|
|
||||||
public SubselectOneToManyLoader(
|
|
||||||
QueryableCollection persister,
|
|
||||||
String subquery,
|
|
||||||
Collection entityKeys,
|
|
||||||
QueryParameters queryParameters,
|
|
||||||
Map<String, int[]> namedParameterLocMap,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
|
||||||
super( persister, 1, subquery, factory, loadQueryInfluencers );
|
|
||||||
|
|
||||||
keys = new Serializable[ entityKeys.size() ];
|
|
||||||
Iterator iter = entityKeys.iterator();
|
|
||||||
int i=0;
|
|
||||||
while ( iter.hasNext() ) {
|
|
||||||
keys[i++] = ( (EntityKey) iter.next() ).getIdentifier();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.namedParameters = queryParameters.getNamedParameters();
|
|
||||||
this.types = queryParameters.getFilteredPositionalParameterTypes();
|
|
||||||
this.values = queryParameters.getFilteredPositionalParameterValues();
|
|
||||||
this.namedParameterLocMap = namedParameterLocMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initialize(Object id, SharedSessionContractImplementor session) throws HibernateException {
|
|
||||||
loadCollectionSubselect(
|
|
||||||
session,
|
|
||||||
keys,
|
|
||||||
values,
|
|
||||||
types,
|
|
||||||
namedParameters,
|
|
||||||
getKeyType()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public int[] getNamedParameterLocs(String name) {
|
|
||||||
return namedParameterLocMap.get( name );
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
<!--
|
|
||||||
~ Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
~
|
|
||||||
~ License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
~ See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<html>
|
|
||||||
<head></head>
|
|
||||||
<body>
|
|
||||||
<p>
|
|
||||||
This package defines collection initializers
|
|
||||||
</p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,30 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.collection.plan;
|
|
||||||
|
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.loader.collection.BatchingCollectionInitializerBuilder;
|
|
||||||
import org.hibernate.loader.collection.CollectionInitializer;
|
|
||||||
import org.hibernate.persister.collection.QueryableCollection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for LoadPlan-based BatchingCollectionInitializerBuilder implementations. Mainly we handle the common
|
|
||||||
* "no batching" case here to use the LoadPlan-based CollectionLoader
|
|
||||||
*
|
|
||||||
* @author Gail Badner
|
|
||||||
*/
|
|
||||||
public abstract class AbstractBatchingCollectionInitializerBuilder extends BatchingCollectionInitializerBuilder {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected CollectionInitializer buildNonBatchingLoader(
|
|
||||||
QueryableCollection persister,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers influencers) {
|
|
||||||
return CollectionLoader.forCollection( persister ).withInfluencers( influencers ).byKey();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,126 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.collection.plan;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
import org.hibernate.AssertionFailure;
|
|
||||||
import org.hibernate.HibernateException;
|
|
||||||
import org.hibernate.LockOptions;
|
|
||||||
import org.hibernate.engine.spi.QueryParameters;
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|
||||||
import org.hibernate.internal.CoreLogging;
|
|
||||||
import org.hibernate.internal.CoreMessageLogger;
|
|
||||||
import org.hibernate.loader.collection.CollectionInitializer;
|
|
||||||
import org.hibernate.loader.plan.build.internal.FetchStyleLoadPlanBuildingAssociationVisitationStrategy;
|
|
||||||
import org.hibernate.loader.plan.build.spi.MetamodelDrivenLoadPlanBuilder;
|
|
||||||
import org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader;
|
|
||||||
import org.hibernate.loader.plan.exec.internal.BatchingLoadQueryDetailsFactory;
|
|
||||||
import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters;
|
|
||||||
import org.hibernate.loader.plan.exec.spi.LoadQueryDetails;
|
|
||||||
import org.hibernate.loader.plan.spi.LoadPlan;
|
|
||||||
import org.hibernate.persister.collection.QueryableCollection;
|
|
||||||
import org.hibernate.pretty.MessageHelper;
|
|
||||||
import org.hibernate.type.Type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An abstract {@link CollectionInitializer} implementation based on using LoadPlans
|
|
||||||
*
|
|
||||||
* @author Gail Badner
|
|
||||||
*/
|
|
||||||
public abstract class AbstractLoadPlanBasedCollectionInitializer
|
|
||||||
extends AbstractLoadPlanBasedLoader implements CollectionInitializer {
|
|
||||||
private static final CoreMessageLogger log = CoreLogging.messageLogger( AbstractLoadPlanBasedCollectionInitializer.class );
|
|
||||||
|
|
||||||
private final QueryableCollection collectionPersister;
|
|
||||||
private final LoadQueryDetails staticLoadQuery;
|
|
||||||
private final LockOptions lockOptions;
|
|
||||||
|
|
||||||
public AbstractLoadPlanBasedCollectionInitializer(
|
|
||||||
QueryableCollection collectionPersister,
|
|
||||||
QueryBuildingParameters buildingParameters) {
|
|
||||||
super( collectionPersister.getFactory() );
|
|
||||||
this.collectionPersister = collectionPersister;
|
|
||||||
this.lockOptions = buildingParameters.getLockMode() != null
|
|
||||||
? new LockOptions( buildingParameters.getLockMode() )
|
|
||||||
: buildingParameters.getLockOptions();
|
|
||||||
|
|
||||||
final FetchStyleLoadPlanBuildingAssociationVisitationStrategy strategy =
|
|
||||||
new FetchStyleLoadPlanBuildingAssociationVisitationStrategy(
|
|
||||||
collectionPersister.getFactory(),
|
|
||||||
buildingParameters.getQueryInfluencers(),
|
|
||||||
this.lockOptions.getLockMode()
|
|
||||||
);
|
|
||||||
|
|
||||||
final LoadPlan plan = MetamodelDrivenLoadPlanBuilder.buildRootCollectionLoadPlan( strategy, collectionPersister );
|
|
||||||
this.staticLoadQuery = BatchingLoadQueryDetailsFactory.INSTANCE.makeCollectionLoadQueryDetails(
|
|
||||||
collectionPersister,
|
|
||||||
plan,
|
|
||||||
buildingParameters
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initialize(Object id, SharedSessionContractImplementor session)
|
|
||||||
throws HibernateException {
|
|
||||||
if ( log.isDebugEnabled() ) {
|
|
||||||
log.debugf( "Loading collection: %s",
|
|
||||||
MessageHelper.collectionInfoString( collectionPersister, id, getFactory() ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
final Serializable[] ids = new Serializable[] {(Serializable) id};
|
|
||||||
try {
|
|
||||||
final QueryParameters qp = new QueryParameters();
|
|
||||||
qp.setPositionalParameterTypes( new Type[]{ collectionPersister.getKeyType() } );
|
|
||||||
qp.setPositionalParameterValues( ids );
|
|
||||||
qp.setCollectionKeys( ids );
|
|
||||||
|
|
||||||
qp.setLockOptions( lockOptions );
|
|
||||||
|
|
||||||
executeLoad(
|
|
||||||
session,
|
|
||||||
qp,
|
|
||||||
staticLoadQuery,
|
|
||||||
true,
|
|
||||||
null
|
|
||||||
|
|
||||||
);
|
|
||||||
}
|
|
||||||
catch ( SQLException sqle ) {
|
|
||||||
throw session.getJdbcServices().getSqlExceptionHelper().convert(
|
|
||||||
sqle,
|
|
||||||
"could not initialize a collection: " +
|
|
||||||
MessageHelper.collectionInfoString( collectionPersister, id, getFactory() ),
|
|
||||||
staticLoadQuery.getSqlStatement()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
log.debug( "Done loading collection" );
|
|
||||||
}
|
|
||||||
|
|
||||||
protected QueryableCollection collectionPersister() {
|
|
||||||
return collectionPersister;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected LoadQueryDetails getStaticLoadQuery() {
|
|
||||||
return staticLoadQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int[] getNamedParameterLocs(String name) {
|
|
||||||
throw new AssertionFailure("no named parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void autoDiscoverTypes(ResultSet rs) {
|
|
||||||
throw new AssertionFailure("Auto discover types not supported in this loader");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.collection.plan;
|
|
||||||
|
|
||||||
import org.hibernate.loader.collection.CollectionInitializer;
|
|
||||||
import org.hibernate.persister.collection.CollectionPersister;
|
|
||||||
import org.hibernate.persister.collection.QueryableCollection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The base contract for loaders capable of performing batch-fetch loading of collections using multiple foreign key
|
|
||||||
* values in the SQL <tt>WHERE</tt> clause.
|
|
||||||
*
|
|
||||||
* @author Gavin King
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*
|
|
||||||
* @see org.hibernate.loader.collection.BatchingCollectionInitializerBuilder
|
|
||||||
* @see org.hibernate.loader.collection.BasicCollectionLoader
|
|
||||||
* @see org.hibernate.loader.collection.OneToManyLoader
|
|
||||||
*/
|
|
||||||
public abstract class BatchingCollectionInitializer implements CollectionInitializer {
|
|
||||||
private final QueryableCollection collectionPersister;
|
|
||||||
|
|
||||||
public BatchingCollectionInitializer(QueryableCollection collectionPersister) {
|
|
||||||
this.collectionPersister = collectionPersister;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CollectionPersister getCollectionPersister() {
|
|
||||||
return collectionPersister;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,98 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.collection.plan;
|
|
||||||
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
|
|
||||||
import org.hibernate.LockMode;
|
|
||||||
import org.hibernate.LockOptions;
|
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|
||||||
import org.hibernate.internal.CoreLogging;
|
|
||||||
import org.hibernate.loader.plan.exec.query.internal.QueryBuildingParametersImpl;
|
|
||||||
import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters;
|
|
||||||
import org.hibernate.persister.collection.QueryableCollection;
|
|
||||||
import org.hibernate.type.Type;
|
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Superclass for loaders that initialize collections
|
|
||||||
*
|
|
||||||
* @see org.hibernate.loader.collection.OneToManyLoader
|
|
||||||
* @see org.hibernate.loader.collection.BasicCollectionLoader
|
|
||||||
* @author Gavin King
|
|
||||||
* @author Gail Badner
|
|
||||||
*/
|
|
||||||
public class CollectionLoader extends AbstractLoadPlanBasedCollectionInitializer {
|
|
||||||
private static final Logger log = CoreLogging.logger( CollectionLoader.class );
|
|
||||||
|
|
||||||
public static Builder forCollection(QueryableCollection collectionPersister) {
|
|
||||||
return new Builder( collectionPersister );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int[] getNamedParameterLocs(String name) {
|
|
||||||
return new int[0]; //To change body of implemented methods use File | Settings | File Templates.
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void autoDiscoverTypes(ResultSet rs) {
|
|
||||||
//To change body of implemented methods use File | Settings | File Templates.
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static class Builder {
|
|
||||||
private final QueryableCollection collectionPersister;
|
|
||||||
private int batchSize = 1;
|
|
||||||
private LoadQueryInfluencers influencers = LoadQueryInfluencers.NONE;
|
|
||||||
|
|
||||||
private Builder(QueryableCollection collectionPersister) {
|
|
||||||
this.collectionPersister = collectionPersister;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder withBatchSize(int batchSize) {
|
|
||||||
this.batchSize = batchSize;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder withInfluencers(LoadQueryInfluencers influencers) {
|
|
||||||
this.influencers = influencers;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CollectionLoader byKey() {
|
|
||||||
// capture current values in a new instance of QueryBuildingParametersImpl
|
|
||||||
final QueryBuildingParameters currentBuildingParameters = new QueryBuildingParametersImpl(
|
|
||||||
influencers,
|
|
||||||
batchSize,
|
|
||||||
LockMode.NONE,
|
|
||||||
null
|
|
||||||
);
|
|
||||||
return new CollectionLoader( collectionPersister, currentBuildingParameters ) ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public CollectionLoader(
|
|
||||||
QueryableCollection collectionPersister,
|
|
||||||
QueryBuildingParameters buildingParameters) {
|
|
||||||
super( collectionPersister, buildingParameters );
|
|
||||||
if ( log.isDebugEnabled() ) {
|
|
||||||
log.debugf(
|
|
||||||
"Static select for collection %s: %s",
|
|
||||||
collectionPersister.getRole(),
|
|
||||||
getStaticLoadQuery().getSqlStatement()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Type getKeyType() {
|
|
||||||
return collectionPersister().getKeyType();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString() {
|
|
||||||
return getClass().getName() + '(' + collectionPersister().getRole() + ')';
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,95 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.collection.plan;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
|
||||||
import org.hibernate.MappingException;
|
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|
||||||
import org.hibernate.internal.util.collections.ArrayHelper;
|
|
||||||
import org.hibernate.loader.Loader;
|
|
||||||
import org.hibernate.loader.collection.BasicCollectionLoader;
|
|
||||||
import org.hibernate.loader.collection.CollectionInitializer;
|
|
||||||
import org.hibernate.loader.collection.OneToManyLoader;
|
|
||||||
import org.hibernate.persister.collection.CollectionPersister;
|
|
||||||
import org.hibernate.persister.collection.QueryableCollection;
|
|
||||||
import org.hibernate.type.Type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* LoadPlan-based implementation of the the legacy batch collection initializer.
|
|
||||||
*
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public class LegacyBatchingCollectionInitializerBuilder extends AbstractBatchingCollectionInitializerBuilder {
|
|
||||||
public static final LegacyBatchingCollectionInitializerBuilder INSTANCE = new LegacyBatchingCollectionInitializerBuilder();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CollectionInitializer createRealBatchingCollectionInitializer(
|
|
||||||
QueryableCollection persister,
|
|
||||||
int maxBatchSize,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
|
||||||
int[] batchSizes = ArrayHelper.getBatchSizes( maxBatchSize );
|
|
||||||
Loader[] loaders = new Loader[ batchSizes.length ];
|
|
||||||
for ( int i = 0; i < batchSizes.length; i++ ) {
|
|
||||||
loaders[i] = new BasicCollectionLoader( persister, batchSizes[i], factory, loadQueryInfluencers );
|
|
||||||
}
|
|
||||||
return new LegacyBatchingCollectionInitializer( persister, batchSizes, loaders );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CollectionInitializer createRealBatchingOneToManyInitializer(
|
|
||||||
QueryableCollection persister,
|
|
||||||
int maxBatchSize,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
|
||||||
final int[] batchSizes = ArrayHelper.getBatchSizes( maxBatchSize );
|
|
||||||
final Loader[] loaders = new Loader[ batchSizes.length ];
|
|
||||||
for ( int i = 0; i < batchSizes.length; i++ ) {
|
|
||||||
loaders[i] = new OneToManyLoader( persister, batchSizes[i], factory, loadQueryInfluencers );
|
|
||||||
}
|
|
||||||
return new LegacyBatchingCollectionInitializer( persister, batchSizes, loaders );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static class LegacyBatchingCollectionInitializer extends BatchingCollectionInitializer {
|
|
||||||
private final int[] batchSizes;
|
|
||||||
private final Loader[] loaders;
|
|
||||||
|
|
||||||
public LegacyBatchingCollectionInitializer(
|
|
||||||
QueryableCollection persister,
|
|
||||||
int[] batchSizes,
|
|
||||||
Loader[] loaders) {
|
|
||||||
super( persister );
|
|
||||||
this.batchSizes = batchSizes;
|
|
||||||
this.loaders = loaders;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initialize(Object id, SharedSessionContractImplementor session) throws HibernateException {
|
|
||||||
final CollectionPersister collectionPersister = getCollectionPersister();
|
|
||||||
Object[] batch = session.getPersistenceContext().getBatchFetchQueue()
|
|
||||||
.getCollectionBatch( collectionPersister, id, batchSizes[0] );
|
|
||||||
|
|
||||||
final Type keyType = collectionPersister.getKeyType();
|
|
||||||
for ( int i = 0; i < batchSizes.length - 1; i++ ) {
|
|
||||||
final int smallBatchSize = batchSizes[i];
|
|
||||||
if ( batch[smallBatchSize-1] != null ) {
|
|
||||||
Serializable[] smallBatch = new Serializable[smallBatchSize];
|
|
||||||
System.arraycopy(batch, 0, smallBatch, 0, smallBatchSize);
|
|
||||||
loaders[i].loadCollectionBatch( session, smallBatch, keyType );
|
|
||||||
return; //EARLY EXIT!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loaders[batchSizes.length-1].loadCollection( session, id, keyType );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.custom;
|
|
||||||
|
|
||||||
import org.hibernate.LockMode;
|
|
||||||
import org.hibernate.loader.CollectionAliases;
|
|
||||||
import org.hibernate.loader.EntityAliases;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifically a fetch return that refers to a collection association.
|
|
||||||
*
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public class CollectionFetchReturn extends FetchReturn {
|
|
||||||
private final CollectionAliases collectionAliases;
|
|
||||||
private final EntityAliases elementEntityAliases;
|
|
||||||
|
|
||||||
public CollectionFetchReturn(
|
|
||||||
String alias,
|
|
||||||
NonScalarReturn owner,
|
|
||||||
String ownerProperty,
|
|
||||||
CollectionAliases collectionAliases,
|
|
||||||
EntityAliases elementEntityAliases,
|
|
||||||
LockMode lockMode) {
|
|
||||||
super( owner, ownerProperty, alias, lockMode );
|
|
||||||
this.collectionAliases = collectionAliases;
|
|
||||||
this.elementEntityAliases = elementEntityAliases;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CollectionAliases getCollectionAliases() {
|
|
||||||
return collectionAliases;
|
|
||||||
}
|
|
||||||
|
|
||||||
public EntityAliases getElementEntityAliases() {
|
|
||||||
return elementEntityAliases;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.custom;
|
|
||||||
|
|
||||||
import org.hibernate.LockMode;
|
|
||||||
import org.hibernate.loader.CollectionAliases;
|
|
||||||
import org.hibernate.loader.EntityAliases;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a return which names a collection role; it
|
|
||||||
* is used in defining a custom query for loading an entity's
|
|
||||||
* collection in non-fetching scenarios (i.e., loading the collection
|
|
||||||
* itself as the "root" of the result).
|
|
||||||
*
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public class CollectionReturn extends NonScalarReturn {
|
|
||||||
private final String ownerEntityName;
|
|
||||||
private final String ownerProperty;
|
|
||||||
private final CollectionAliases collectionAliases;
|
|
||||||
private final EntityAliases elementEntityAliases;
|
|
||||||
|
|
||||||
public CollectionReturn(
|
|
||||||
String alias,
|
|
||||||
String ownerEntityName,
|
|
||||||
String ownerProperty,
|
|
||||||
CollectionAliases collectionAliases,
|
|
||||||
EntityAliases elementEntityAliases,
|
|
||||||
LockMode lockMode) {
|
|
||||||
super( alias, lockMode );
|
|
||||||
this.ownerEntityName = ownerEntityName;
|
|
||||||
this.ownerProperty = ownerProperty;
|
|
||||||
this.collectionAliases = collectionAliases;
|
|
||||||
this.elementEntityAliases = elementEntityAliases;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the class owning the collection.
|
|
||||||
*
|
|
||||||
* @return The class owning the collection.
|
|
||||||
*/
|
|
||||||
public String getOwnerEntityName() {
|
|
||||||
return ownerEntityName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the name of the property representing the collection from the {@link #getOwnerEntityName}.
|
|
||||||
*
|
|
||||||
* @return The name of the property representing the collection on the owner class.
|
|
||||||
*/
|
|
||||||
public String getOwnerProperty() {
|
|
||||||
return ownerProperty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CollectionAliases getCollectionAliases() {
|
|
||||||
return collectionAliases;
|
|
||||||
}
|
|
||||||
|
|
||||||
public EntityAliases getElementEntityAliases() {
|
|
||||||
return elementEntityAliases;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,136 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.custom;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.hibernate.loader.CollectionAliases;
|
|
||||||
import org.hibernate.persister.collection.SQLLoadableCollection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CollectionAliases that uses columnnames instead of generated aliases.
|
|
||||||
* Aliases can still be overwritten via <return-property>
|
|
||||||
*
|
|
||||||
* @author Max Rydahl Andersen
|
|
||||||
*/
|
|
||||||
public class ColumnCollectionAliases implements CollectionAliases {
|
|
||||||
private final String[] keyAliases;
|
|
||||||
private final String[] indexAliases;
|
|
||||||
private final String[] elementAliases;
|
|
||||||
private final String identifierAlias;
|
|
||||||
private Map userProvidedAliases;
|
|
||||||
|
|
||||||
|
|
||||||
public ColumnCollectionAliases(Map userProvidedAliases, SQLLoadableCollection persister) {
|
|
||||||
this.userProvidedAliases = userProvidedAliases;
|
|
||||||
|
|
||||||
this.keyAliases = getUserProvidedAliases(
|
|
||||||
"key",
|
|
||||||
persister.getKeyColumnNames()
|
|
||||||
);
|
|
||||||
|
|
||||||
this.indexAliases = getUserProvidedAliases(
|
|
||||||
"index",
|
|
||||||
persister.getIndexColumnNames()
|
|
||||||
);
|
|
||||||
|
|
||||||
this.elementAliases = getUserProvidedAliases(
|
|
||||||
"element",
|
|
||||||
persister.getElementColumnNames()
|
|
||||||
);
|
|
||||||
|
|
||||||
this.identifierAlias = getUserProvidedAlias(
|
|
||||||
"id",
|
|
||||||
persister.getIdentifierColumnName()
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the suffixed result-set column-aliases for columns making up the key for this collection (i.e., its FK to
|
|
||||||
* its owner).
|
|
||||||
*
|
|
||||||
* @return The key result-set column aliases.
|
|
||||||
*/
|
|
||||||
public String[] getSuffixedKeyAliases() {
|
|
||||||
return keyAliases;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the suffixed result-set column-aliases for the collumns making up the collection's index (map or list).
|
|
||||||
*
|
|
||||||
* @return The index result-set column aliases.
|
|
||||||
*/
|
|
||||||
public String[] getSuffixedIndexAliases() {
|
|
||||||
return indexAliases;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the suffixed result-set column-aliases for the columns making up the collection's elements.
|
|
||||||
*
|
|
||||||
* @return The element result-set column aliases.
|
|
||||||
*/
|
|
||||||
public String[] getSuffixedElementAliases() {
|
|
||||||
return elementAliases;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the suffixed result-set column-aliases for the column defining the collection's identifier (if any).
|
|
||||||
*
|
|
||||||
* @return The identifier result-set column aliases.
|
|
||||||
*/
|
|
||||||
public String getSuffixedIdentifierAlias() {
|
|
||||||
return identifierAlias;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the suffix used to unique the column aliases for this particular alias set.
|
|
||||||
*
|
|
||||||
* @return The uniqued column alias suffix.
|
|
||||||
*/
|
|
||||||
public String getSuffix() {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return super.toString() + " [ suffixedKeyAliases=[" + join( keyAliases ) +
|
|
||||||
"], suffixedIndexAliases=[" + join( indexAliases ) +
|
|
||||||
"], suffixedElementAliases=[" + join( elementAliases ) +
|
|
||||||
"], suffixedIdentifierAlias=[" + identifierAlias + "]]";
|
|
||||||
}
|
|
||||||
|
|
||||||
private String join(String[] aliases) {
|
|
||||||
if ( aliases == null ) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return String.join( ", ", aliases );
|
|
||||||
}
|
|
||||||
|
|
||||||
private String[] getUserProvidedAliases(String propertyPath, String[] defaultAliases) {
|
|
||||||
String[] result = (String[]) userProvidedAliases.get( propertyPath );
|
|
||||||
if ( result == null ) {
|
|
||||||
return defaultAliases;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getUserProvidedAlias(String propertyPath, String defaultAlias) {
|
|
||||||
String[] columns = (String[]) userProvidedAliases.get( propertyPath );
|
|
||||||
if ( columns == null ) {
|
|
||||||
return defaultAlias;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return columns[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,100 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.custom;
|
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|
||||||
import org.hibernate.internal.util.type.PrimitiveWrapperHelper;
|
|
||||||
import org.hibernate.type.Type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a {@link javax.persistence.ConstructorResult} within the custom query.
|
|
||||||
*
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public class ConstructorResultColumnProcessor implements ResultColumnProcessor {
|
|
||||||
private final Class targetClass;
|
|
||||||
private final ScalarResultColumnProcessor[] scalarProcessors;
|
|
||||||
|
|
||||||
private Constructor constructor;
|
|
||||||
|
|
||||||
public ConstructorResultColumnProcessor(Class targetClass, ScalarResultColumnProcessor[] scalarProcessors) {
|
|
||||||
this.targetClass = targetClass;
|
|
||||||
this.scalarProcessors = scalarProcessors;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void performDiscovery(JdbcResultMetadata metadata, List<Type> types, List<String> aliases) throws SQLException {
|
|
||||||
final List<Type> localTypes = new ArrayList<>();
|
|
||||||
for ( ScalarResultColumnProcessor scalar : scalarProcessors ) {
|
|
||||||
scalar.performDiscovery( metadata, localTypes, aliases );
|
|
||||||
}
|
|
||||||
|
|
||||||
types.addAll( localTypes );
|
|
||||||
|
|
||||||
constructor = resolveConstructor( targetClass, localTypes );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object extract(Object[] data, ResultSet resultSet, SharedSessionContractImplementor session)
|
|
||||||
throws SQLException, HibernateException {
|
|
||||||
if ( constructor == null ) {
|
|
||||||
throw new IllegalStateException( "Constructor to call was null" );
|
|
||||||
}
|
|
||||||
|
|
||||||
final Object[] args = new Object[ scalarProcessors.length ];
|
|
||||||
for ( int i = 0; i < scalarProcessors.length; i++ ) {
|
|
||||||
args[i] = scalarProcessors[i].extract( data, resultSet, session );
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
return constructor.newInstance( args );
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
throw new HibernateException(
|
|
||||||
String.format( "Unable to call %s constructor", constructor.getDeclaringClass() ),
|
|
||||||
e
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Constructor resolveConstructor(Class targetClass, List<Type> types) {
|
|
||||||
for ( Constructor constructor : targetClass.getConstructors() ) {
|
|
||||||
final Class[] argumentTypes = constructor.getParameterTypes();
|
|
||||||
if ( argumentTypes.length != types.size() ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean allMatched = true;
|
|
||||||
for ( int i = 0; i < argumentTypes.length; i++ ) {
|
|
||||||
if ( ! areAssignmentCompatible( argumentTypes[i], types.get( i ).getReturnedClass() ) ) {
|
|
||||||
allMatched = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( !allMatched ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return constructor;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new IllegalArgumentException( "Could not locate appropriate constructor on class : " + targetClass.getName() );
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private static boolean areAssignmentCompatible(Class argumentType, Class typeReturnedClass) {
|
|
||||||
return argumentType.isAssignableFrom( typeReturnedClass )
|
|
||||||
|| PrimitiveWrapperHelper.arePrimitiveWrapperEquivalents( argumentType, typeReturnedClass );
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.custom;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A return representing a {@link javax.persistence.ConstructorResult}
|
|
||||||
*
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public class ConstructorReturn implements Return {
|
|
||||||
private final Class targetClass;
|
|
||||||
private final ScalarReturn[] scalars;
|
|
||||||
|
|
||||||
public ConstructorReturn(Class targetClass, ScalarReturn[] scalars) {
|
|
||||||
this.targetClass = targetClass;
|
|
||||||
this.scalars = scalars;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Class getTargetClass() {
|
|
||||||
return targetClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ScalarReturn[] getScalars() {
|
|
||||||
return scalars;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,548 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.custom;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.sql.PreparedStatement;
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
|
||||||
import org.hibernate.LockMode;
|
|
||||||
import org.hibernate.LockOptions;
|
|
||||||
import org.hibernate.NotYetImplementedFor6Exception;
|
|
||||||
import org.hibernate.QueryException;
|
|
||||||
import org.hibernate.Session;
|
|
||||||
import org.hibernate.cache.spi.QueryKey;
|
|
||||||
import org.hibernate.cache.spi.QueryResultsCache;
|
|
||||||
import org.hibernate.dialect.Dialect;
|
|
||||||
import org.hibernate.engine.spi.QueryParameters;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|
||||||
import org.hibernate.internal.util.collections.ArrayHelper;
|
|
||||||
import org.hibernate.loader.CollectionAliases;
|
|
||||||
import org.hibernate.loader.EntityAliases;
|
|
||||||
import org.hibernate.loader.Loader;
|
|
||||||
import org.hibernate.loader.ast.spi.AfterLoadAction;
|
|
||||||
import org.hibernate.param.ParameterBinder;
|
|
||||||
import org.hibernate.persister.collection.CollectionPersister;
|
|
||||||
import org.hibernate.persister.collection.QueryableCollection;
|
|
||||||
import org.hibernate.persister.entity.Loadable;
|
|
||||||
import org.hibernate.persister.entity.Queryable;
|
|
||||||
import org.hibernate.query.spi.ScrollableResultsImplementor;
|
|
||||||
import org.hibernate.transform.ResultTransformer;
|
|
||||||
import org.hibernate.type.Type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extension point for loaders which use a SQL result set with "unexpected" column aliases.
|
|
||||||
*
|
|
||||||
* @author Gavin King
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public class CustomLoader extends Loader {
|
|
||||||
|
|
||||||
// Currently *not* cachable if autodiscover types is in effect (e.g. "select * ...")
|
|
||||||
|
|
||||||
private final String sql;
|
|
||||||
private final Set<Serializable> querySpaces = new HashSet<>();
|
|
||||||
|
|
||||||
private final List<ParameterBinder> paramValueBinders;
|
|
||||||
|
|
||||||
private final Queryable[] entityPersisters;
|
|
||||||
private final int[] entiytOwners;
|
|
||||||
private final EntityAliases[] entityAliases;
|
|
||||||
|
|
||||||
private final QueryableCollection[] collectionPersisters;
|
|
||||||
private final int[] collectionOwners;
|
|
||||||
private final CollectionAliases[] collectionAliases;
|
|
||||||
|
|
||||||
private final LockMode[] lockModes;
|
|
||||||
|
|
||||||
private boolean[] includeInResultRow;
|
|
||||||
|
|
||||||
// private final String[] sqlAliases;
|
|
||||||
// private final String[] sqlAliasSuffixes;
|
|
||||||
private final ResultRowProcessor rowProcessor;
|
|
||||||
|
|
||||||
// this is only needed (afaict) for processing results from the query cache;
|
|
||||||
// however, this cannot possibly work in the case of discovered types...
|
|
||||||
private Type[] resultTypes;
|
|
||||||
|
|
||||||
// this is only needed (afaict) for ResultTransformer processing...
|
|
||||||
private String[] transformerAliases;
|
|
||||||
|
|
||||||
public CustomLoader(CustomQuery customQuery, SessionFactoryImplementor factory) {
|
|
||||||
super( factory );
|
|
||||||
|
|
||||||
throw new NotYetImplementedFor6Exception( getClass() );
|
|
||||||
|
|
||||||
// this.sql = customQuery.getSQL();
|
|
||||||
// this.querySpaces.addAll( customQuery.getQuerySpaces() );
|
|
||||||
//
|
|
||||||
// this.paramValueBinders = customQuery.getParameterValueBinders();
|
|
||||||
//
|
|
||||||
// List<Queryable> entityPersisters = new ArrayList<>();
|
|
||||||
// List<Integer> entityOwners = new ArrayList<>();
|
|
||||||
// List<EntityAliases> entityAliases = new ArrayList<>();
|
|
||||||
//
|
|
||||||
// List<QueryableCollection> collectionPersisters = new ArrayList<>();
|
|
||||||
// List<Integer> collectionOwners = new ArrayList<>();
|
|
||||||
// List<CollectionAliases> collectionAliases = new ArrayList<>();
|
|
||||||
//
|
|
||||||
// List<LockMode> lockModes = new ArrayList<>();
|
|
||||||
// List<ResultColumnProcessor> resultColumnProcessors = new ArrayList<>();
|
|
||||||
// List<Return> nonScalarReturnList = new ArrayList<>();
|
|
||||||
// List<Type> resultTypes = new ArrayList<>();
|
|
||||||
// List<String> specifiedAliases = new ArrayList<>();
|
|
||||||
//
|
|
||||||
// int returnableCounter = 0;
|
|
||||||
// boolean hasScalars = false;
|
|
||||||
//
|
|
||||||
// List<Boolean> includeInResultRowList = new ArrayList<>();
|
|
||||||
//
|
|
||||||
// for ( Return rtn : customQuery.getCustomQueryReturns() ) {
|
|
||||||
// if ( rtn instanceof ScalarReturn ) {
|
|
||||||
// ScalarReturn scalarRtn = (ScalarReturn) rtn;
|
|
||||||
// resultTypes.add( scalarRtn.getType() );
|
|
||||||
// specifiedAliases.add( scalarRtn.getColumnAlias() );
|
|
||||||
// resultColumnProcessors.add(
|
|
||||||
// new ScalarResultColumnProcessor(
|
|
||||||
// StringHelper.unquote( scalarRtn.getColumnAlias(), factory.getJdbcServices().getDialect() ),
|
|
||||||
// scalarRtn.getType()
|
|
||||||
// )
|
|
||||||
// );
|
|
||||||
// includeInResultRowList.add( true );
|
|
||||||
// hasScalars = true;
|
|
||||||
// }
|
|
||||||
// else if ( ConstructorReturn.class.isInstance( rtn ) ) {
|
|
||||||
// final ConstructorReturn constructorReturn = (ConstructorReturn) rtn;
|
|
||||||
// resultTypes.add( null ); // this bit makes me nervous
|
|
||||||
// includeInResultRowList.add( true );
|
|
||||||
// hasScalars = true;
|
|
||||||
//
|
|
||||||
// ScalarResultColumnProcessor[] scalarProcessors = new ScalarResultColumnProcessor[constructorReturn.getScalars().length];
|
|
||||||
// int i = 0;
|
|
||||||
// for ( ScalarReturn scalarReturn : constructorReturn.getScalars() ) {
|
|
||||||
// scalarProcessors[i++] = new ScalarResultColumnProcessor(
|
|
||||||
// StringHelper.unquote( scalarReturn.getColumnAlias(), factory.getJdbcServices().getDialect() ),
|
|
||||||
// scalarReturn.getType()
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// resultColumnProcessors.add(
|
|
||||||
// new ConstructorResultColumnProcessor( constructorReturn.getTargetClass(), scalarProcessors )
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// else if ( rtn instanceof RootReturn ) {
|
|
||||||
// RootReturn rootRtn = (RootReturn) rtn;
|
|
||||||
// Queryable persister = (Queryable) factory.getMetamodel().entityPersister( rootRtn.getEntityName() );
|
|
||||||
// entityPersisters.add( persister );
|
|
||||||
// lockModes.add( ( rootRtn.getLockMode() ) );
|
|
||||||
// resultColumnProcessors.add( new NonScalarResultColumnProcessor( returnableCounter++ ) );
|
|
||||||
// nonScalarReturnList.add( rtn );
|
|
||||||
// entityOwners.add( -1 );
|
|
||||||
// resultTypes.add( persister.getType() );
|
|
||||||
// specifiedAliases.add( rootRtn.getAlias() );
|
|
||||||
// entityAliases.add( rootRtn.getEntityAliases() );
|
|
||||||
// ArrayHelper.addAll( querySpaces, persister.getQuerySpaces() );
|
|
||||||
// includeInResultRowList.add( true );
|
|
||||||
// }
|
|
||||||
// else if ( rtn instanceof CollectionReturn ) {
|
|
||||||
// CollectionReturn collRtn = (CollectionReturn) rtn;
|
|
||||||
// String role = collRtn.getOwnerEntityName() + "." + collRtn.getOwnerProperty();
|
|
||||||
// QueryableCollection persister = (QueryableCollection) factory.getMetamodel().collectionPersister( role );
|
|
||||||
// collectionPersisters.add( persister );
|
|
||||||
// lockModes.add( collRtn.getLockMode() );
|
|
||||||
// resultColumnProcessors.add( new NonScalarResultColumnProcessor( returnableCounter++ ) );
|
|
||||||
// nonScalarReturnList.add( rtn );
|
|
||||||
// collectionOwners.add( -1 );
|
|
||||||
// resultTypes.add( persister.getType() );
|
|
||||||
// specifiedAliases.add( collRtn.getAlias() );
|
|
||||||
// collectionAliases.add( collRtn.getCollectionAliases() );
|
|
||||||
// // determine if the collection elements are entities...
|
|
||||||
// Type elementType = persister.getElementType();
|
|
||||||
// if ( elementType.isEntityType() ) {
|
|
||||||
// Queryable elementPersister = (Queryable) ( (EntityType) elementType ).getAssociatedJoinable( factory );
|
|
||||||
// entityPersisters.add( elementPersister );
|
|
||||||
// entityOwners.add( -1 );
|
|
||||||
// entityAliases.add( collRtn.getElementEntityAliases() );
|
|
||||||
// ArrayHelper.addAll( querySpaces, elementPersister.getQuerySpaces() );
|
|
||||||
// }
|
|
||||||
// includeInResultRowList.add( true );
|
|
||||||
// }
|
|
||||||
// else if ( rtn instanceof EntityFetchReturn ) {
|
|
||||||
// EntityFetchReturn fetchRtn = (EntityFetchReturn) rtn;
|
|
||||||
// NonScalarReturn ownerDescriptor = fetchRtn.getOwner();
|
|
||||||
// int ownerIndex = nonScalarReturnList.indexOf( ownerDescriptor );
|
|
||||||
// entityOwners.add( ownerIndex );
|
|
||||||
// lockModes.add( fetchRtn.getLockMode() );
|
|
||||||
// Queryable ownerPersister = determineAppropriateOwnerPersister( ownerDescriptor );
|
|
||||||
// EntityType fetchedType = (EntityType) ownerPersister.getPropertyType( fetchRtn.getOwnerProperty() );
|
|
||||||
// String entityName = fetchedType.getAssociatedEntityName( getFactory() );
|
|
||||||
// Queryable persister = (Queryable) factory.getMetamodel().entityPersister( entityName );
|
|
||||||
// entityPersisters.add( persister );
|
|
||||||
// nonScalarReturnList.add( rtn );
|
|
||||||
// specifiedAliases.add( fetchRtn.getAlias() );
|
|
||||||
// entityAliases.add( fetchRtn.getEntityAliases() );
|
|
||||||
// ArrayHelper.addAll( querySpaces, persister.getQuerySpaces() );
|
|
||||||
// includeInResultRowList.add( false );
|
|
||||||
// }
|
|
||||||
// else if ( rtn instanceof CollectionFetchReturn ) {
|
|
||||||
// CollectionFetchReturn fetchRtn = (CollectionFetchReturn) rtn;
|
|
||||||
// NonScalarReturn ownerDescriptor = fetchRtn.getOwner();
|
|
||||||
// int ownerIndex = nonScalarReturnList.indexOf( ownerDescriptor );
|
|
||||||
// collectionOwners.add( ownerIndex );
|
|
||||||
// lockModes.add( fetchRtn.getLockMode() );
|
|
||||||
// Queryable ownerPersister = determineAppropriateOwnerPersister( ownerDescriptor );
|
|
||||||
// String role = ownerPersister.getEntityName() + '.' + fetchRtn.getOwnerProperty();
|
|
||||||
// QueryableCollection persister = (QueryableCollection) factory.getMetamodel().collectionPersister( role );
|
|
||||||
// collectionPersisters.add( persister );
|
|
||||||
// nonScalarReturnList.add( rtn );
|
|
||||||
// specifiedAliases.add( fetchRtn.getAlias() );
|
|
||||||
// collectionAliases.add( fetchRtn.getCollectionAliases() );
|
|
||||||
// // determine if the collection elements are entities...
|
|
||||||
// Type elementType = persister.getElementType();
|
|
||||||
// if ( elementType.isEntityType() ) {
|
|
||||||
// Queryable elementPersister = (Queryable) ( (EntityType) elementType ).getAssociatedJoinable( factory );
|
|
||||||
// entityPersisters.add( elementPersister );
|
|
||||||
// entityOwners.add( ownerIndex );
|
|
||||||
// entityAliases.add( fetchRtn.getElementEntityAliases() );
|
|
||||||
// ArrayHelper.addAll( querySpaces, elementPersister.getQuerySpaces() );
|
|
||||||
// }
|
|
||||||
// includeInResultRowList.add( false );
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// throw new HibernateException( "unexpected custom query return type : " + rtn.getClass().getName() );
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// this.entityPersisters = new Queryable[entityPersisters.size()];
|
|
||||||
// for ( int i = 0; i < entityPersisters.size(); i++ ) {
|
|
||||||
// this.entityPersisters[i] = entityPersisters.get( i );
|
|
||||||
// }
|
|
||||||
// this.entiytOwners = ArrayHelper.toIntArray( entityOwners );
|
|
||||||
// this.entityAliases = new EntityAliases[entityAliases.size()];
|
|
||||||
// for ( int i = 0; i < entityAliases.size(); i++ ) {
|
|
||||||
// this.entityAliases[i] = entityAliases.get( i );
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// this.collectionPersisters = new QueryableCollection[collectionPersisters.size()];
|
|
||||||
// for ( int i = 0; i < collectionPersisters.size(); i++ ) {
|
|
||||||
// this.collectionPersisters[i] = collectionPersisters.get( i );
|
|
||||||
// }
|
|
||||||
// this.collectionOwners = ArrayHelper.toIntArray( collectionOwners );
|
|
||||||
// this.collectionAliases = new CollectionAliases[collectionAliases.size()];
|
|
||||||
// for ( int i = 0; i < collectionAliases.size(); i++ ) {
|
|
||||||
// this.collectionAliases[i] = collectionAliases.get( i );
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// this.lockModes = new LockMode[lockModes.size()];
|
|
||||||
// for ( int i = 0; i < lockModes.size(); i++ ) {
|
|
||||||
// this.lockModes[i] = lockModes.get( i );
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// this.resultTypes = ArrayHelper.toTypeArray( resultTypes );
|
|
||||||
// this.transformerAliases = ArrayHelper.toStringArray( specifiedAliases );
|
|
||||||
//
|
|
||||||
// this.rowProcessor = new ResultRowProcessor(
|
|
||||||
// hasScalars,
|
|
||||||
// resultColumnProcessors.toArray( new ResultColumnProcessor[resultColumnProcessors.size()] )
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// this.includeInResultRow = ArrayHelper.toBooleanArray( includeInResultRowList );
|
|
||||||
}
|
|
||||||
|
|
||||||
// private Queryable determineAppropriateOwnerPersister(NonScalarReturn ownerDescriptor) {
|
|
||||||
// String entityName = null;
|
|
||||||
// if ( ownerDescriptor instanceof RootReturn ) {
|
|
||||||
// entityName = ( (RootReturn) ownerDescriptor ).getEntityName();
|
|
||||||
// }
|
|
||||||
// else if ( ownerDescriptor instanceof CollectionReturn ) {
|
|
||||||
// CollectionReturn collRtn = (CollectionReturn) ownerDescriptor;
|
|
||||||
// String role = collRtn.getOwnerEntityName() + "." + collRtn.getOwnerProperty();
|
|
||||||
// CollectionPersister persister = getFactory().getMetamodel().collectionPersister( role );
|
|
||||||
// EntityType ownerType = (EntityType) persister.getElementType();
|
|
||||||
// entityName = ownerType.getAssociatedEntityName( getFactory() );
|
|
||||||
// }
|
|
||||||
// else if ( ownerDescriptor instanceof FetchReturn ) {
|
|
||||||
// FetchReturn fetchRtn = (FetchReturn) ownerDescriptor;
|
|
||||||
// Queryable persister = determineAppropriateOwnerPersister( fetchRtn.getOwner() );
|
|
||||||
// Type ownerType = persister.getPropertyType( fetchRtn.getOwnerProperty() );
|
|
||||||
// if ( ownerType.isEntityType() ) {
|
|
||||||
// entityName = ( (EntityType) ownerType ).getAssociatedEntityName( getFactory() );
|
|
||||||
// }
|
|
||||||
// else if ( ownerType.isCollectionType() ) {
|
|
||||||
// Type ownerCollectionElementType = ( (CollectionType) ownerType ).getElementType( getFactory() );
|
|
||||||
// if ( ownerCollectionElementType.isEntityType() ) {
|
|
||||||
// entityName = ( (EntityType) ownerCollectionElementType ).getAssociatedEntityName( getFactory() );
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if ( entityName == null ) {
|
|
||||||
// throw new HibernateException( "Could not determine fetch owner : " + ownerDescriptor );
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return (Queryable) getFactory().getMetamodel().entityPersister( entityName );
|
|
||||||
// }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getQueryIdentifier() {
|
|
||||||
return sql;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getSQLString() {
|
|
||||||
return sql;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set getQuerySpaces() {
|
|
||||||
return querySpaces;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected LockMode[] getLockModes(LockOptions lockOptions) {
|
|
||||||
return lockModes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Loadable[] getEntityPersisters() {
|
|
||||||
return entityPersisters;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected CollectionPersister[] getCollectionPersisters() {
|
|
||||||
return collectionPersisters;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int[] getCollectionOwners() {
|
|
||||||
return collectionOwners;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int[] getOwners() {
|
|
||||||
return entiytOwners;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List list(SharedSessionContractImplementor session, QueryParameters queryParameters) throws HibernateException {
|
|
||||||
return list( session, queryParameters, querySpaces, resultTypes );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String applyLocks(
|
|
||||||
String sql,
|
|
||||||
QueryParameters parameters,
|
|
||||||
Dialect dialect,
|
|
||||||
List<AfterLoadAction> afterLoadActions) throws QueryException {
|
|
||||||
final LockOptions lockOptions = parameters.getLockOptions();
|
|
||||||
if ( lockOptions == null ||
|
|
||||||
( lockOptions.getLockMode() == LockMode.NONE && lockOptions.getAliasLockCount() == 0 ) ) {
|
|
||||||
return sql;
|
|
||||||
}
|
|
||||||
|
|
||||||
// user is request locking, lets see if we can apply locking directly to the SQL...
|
|
||||||
|
|
||||||
// some dialects wont allow locking with paging...
|
|
||||||
afterLoadActions.add(
|
|
||||||
new AfterLoadAction() {
|
|
||||||
private final LockOptions originalLockOptions = lockOptions.makeCopy();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void afterLoad(SharedSessionContractImplementor session, Object entity, Loadable persister) {
|
|
||||||
( (Session) session ).buildLockRequest( originalLockOptions ).lock(
|
|
||||||
persister.getEntityName(),
|
|
||||||
entity
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
parameters.getLockOptions().setLockMode( LockMode.READ );
|
|
||||||
|
|
||||||
return sql;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ScrollableResultsImplementor scroll(final QueryParameters queryParameters, final SharedSessionContractImplementor session)
|
|
||||||
throws HibernateException {
|
|
||||||
|
|
||||||
ResultTransformer resultTransformer = queryParameters.getResultTransformer();
|
|
||||||
|
|
||||||
throw new NotYetImplementedFor6Exception( getClass() );
|
|
||||||
|
|
||||||
// final RowReader rowReader = ...;
|
|
||||||
//
|
|
||||||
// return scroll( queryParameters, resultTypes, rowReader, session );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String[] getResultRowAliases() {
|
|
||||||
return transformerAliases;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ResultTransformer resolveResultTransformer(ResultTransformer resultTransformer) {
|
|
||||||
throw new NotYetImplementedFor6Exception( getClass() );
|
|
||||||
// return HolderInstantiator.resolveResultTransformer( null, resultTransformer );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean[] includeInResultRow() {
|
|
||||||
return includeInResultRow;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Object getResultColumnOrRow(
|
|
||||||
Object[] row,
|
|
||||||
ResultTransformer transformer,
|
|
||||||
ResultSet rs,
|
|
||||||
SharedSessionContractImplementor session) throws SQLException, HibernateException {
|
|
||||||
return rowProcessor.buildResultRow( row, rs, transformer != null, session );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Object[] getResultRow(Object[] row, ResultSet rs, SharedSessionContractImplementor session)
|
|
||||||
throws SQLException, HibernateException {
|
|
||||||
return rowProcessor.buildResultRow( row, rs, session );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
protected List getResultList(List results, ResultTransformer resultTransformer) throws QueryException {
|
|
||||||
throw new NotYetImplementedFor6Exception( getClass() );
|
|
||||||
|
|
||||||
// // meant to handle dynamic instantiation queries...(Copy from QueryLoader)
|
|
||||||
// HolderInstantiator holderInstantiator = HolderInstantiator.getHolderInstantiator(
|
|
||||||
// null,
|
|
||||||
// resultTransformer,
|
|
||||||
// getReturnAliasesForTransformer()
|
|
||||||
// );
|
|
||||||
// if ( holderInstantiator.isRequired() ) {
|
|
||||||
// for ( int i = 0; i < results.size(); i++ ) {
|
|
||||||
// Object[] row = (Object[]) results.get( i );
|
|
||||||
// Object result = holderInstantiator.instantiate( row );
|
|
||||||
// results.set( i, result );
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return resultTransformer.transformList( results );
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// return results;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
private String[] getReturnAliasesForTransformer() {
|
|
||||||
return transformerAliases;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected EntityAliases[] getEntityAliases() {
|
|
||||||
return entityAliases;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected CollectionAliases[] getCollectionAliases() {
|
|
||||||
return collectionAliases;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int bindParameterValues(
|
|
||||||
PreparedStatement statement,
|
|
||||||
QueryParameters queryParameters,
|
|
||||||
int startIndex,
|
|
||||||
SharedSessionContractImplementor session) throws SQLException {
|
|
||||||
final Serializable optionalId = queryParameters.getOptionalId();
|
|
||||||
if ( optionalId != null ) {
|
|
||||||
paramValueBinders.get( 0 ).bind( statement, queryParameters, session, startIndex );
|
|
||||||
return session.getFactory().getMetamodel()
|
|
||||||
.entityPersister( queryParameters.getOptionalEntityName() )
|
|
||||||
.getIdentifierType()
|
|
||||||
.getColumnSpan( session.getFactory() );
|
|
||||||
}
|
|
||||||
|
|
||||||
int span = 0;
|
|
||||||
for ( ParameterBinder paramValueBinder : paramValueBinders ) {
|
|
||||||
span += paramValueBinder.bind(
|
|
||||||
statement,
|
|
||||||
queryParameters,
|
|
||||||
session,
|
|
||||||
startIndex + span
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return span;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void autoDiscoverTypes(ResultSet rs) {
|
|
||||||
try {
|
|
||||||
JdbcResultMetadata metadata = new JdbcResultMetadata( getFactory(), rs );
|
|
||||||
rowProcessor.prepareForAutoDiscovery( metadata );
|
|
||||||
|
|
||||||
List<String> aliases = new ArrayList<>();
|
|
||||||
List<Type> types = new ArrayList<>();
|
|
||||||
for ( ResultColumnProcessor resultProcessor : rowProcessor.getColumnProcessors() ) {
|
|
||||||
resultProcessor.performDiscovery( metadata, types, aliases );
|
|
||||||
}
|
|
||||||
|
|
||||||
validateAliases( aliases );
|
|
||||||
|
|
||||||
resultTypes = ArrayHelper.toTypeArray( types );
|
|
||||||
transformerAliases = ArrayHelper.toStringArray( aliases );
|
|
||||||
}
|
|
||||||
catch (SQLException e) {
|
|
||||||
throw new HibernateException( "Exception while trying to autodiscover types.", e );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void validateAliases(List<String> aliases) {
|
|
||||||
// lets make sure we did not end up with duplicate aliases. this can occur when the user supplied query
|
|
||||||
// did not rename same-named columns. e.g.:
|
|
||||||
// select u.username, u2.username from t_user u, t_user u2 ...
|
|
||||||
//
|
|
||||||
// the above will lead to an unworkable situation in most cases (the difference is how the driver/db
|
|
||||||
// handle this situation. But if the 'aliases' variable contains duplicate names, then we have that
|
|
||||||
// troublesome condition, so lets throw an error. See HHH-5992
|
|
||||||
final HashSet<String> aliasesSet = new HashSet<String>();
|
|
||||||
for ( String alias : aliases ) {
|
|
||||||
validateAlias( alias );
|
|
||||||
boolean alreadyExisted = !aliasesSet.add( alias );
|
|
||||||
if ( alreadyExisted ) {
|
|
||||||
throw new NonUniqueDiscoveredSqlAliasException(
|
|
||||||
"Encountered a duplicated sql alias [" + alias + "] during auto-discovery of a native-sql query"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("UnusedParameters")
|
|
||||||
protected void validateAlias(String alias) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link #resultTypes} can be overridden by {@link #autoDiscoverTypes(ResultSet)},
|
|
||||||
* *after* {@link #list(SharedSessionContractImplementor, QueryParameters)} has already been called. It's a bit of a
|
|
||||||
* chicken-and-the-egg issue since {@link #autoDiscoverTypes(ResultSet)} needs the {@link ResultSet}.
|
|
||||||
* <p/>
|
|
||||||
* As a hacky workaround, overriden here to provide the {@link #resultTypes}.
|
|
||||||
*
|
|
||||||
* see HHH-3051
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void putResultInQueryCache(
|
|
||||||
final SharedSessionContractImplementor session,
|
|
||||||
final QueryParameters queryParameters,
|
|
||||||
final Type[] resultTypes,
|
|
||||||
final QueryResultsCache queryCache,
|
|
||||||
final QueryKey key,
|
|
||||||
final List result) {
|
|
||||||
super.putResultInQueryCache( session, queryParameters, this.resultTypes, queryCache, key, result );
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.custom;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.hibernate.param.ParameterBinder;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extension point allowing any SQL query with named and positional parameters
|
|
||||||
* to be executed by Hibernate, returning managed entities, collections and
|
|
||||||
* simple scalar values.
|
|
||||||
*
|
|
||||||
* @author Gavin King
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public interface CustomQuery {
|
|
||||||
/**
|
|
||||||
* The SQL query string to be performed.
|
|
||||||
*
|
|
||||||
* @return The SQL statement string.
|
|
||||||
*/
|
|
||||||
String getSQL();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Any query spaces to apply to the query execution. Query spaces are
|
|
||||||
* used in Hibernate's auto-flushing mechanism to determine which
|
|
||||||
* entities need to be checked for pending changes.
|
|
||||||
*
|
|
||||||
* @return The query spaces
|
|
||||||
*/
|
|
||||||
Set<String> getQuerySpaces();
|
|
||||||
|
|
||||||
List<ParameterBinder> getParameterValueBinders();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A collection of {@link Return descriptors} describing the
|
|
||||||
* JDBC result set to be expected and how to map this result set.
|
|
||||||
*
|
|
||||||
* @return List of return descriptors.
|
|
||||||
*/
|
|
||||||
List<Return> getCustomQueryReturns();
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.custom;
|
|
||||||
import org.hibernate.LockMode;
|
|
||||||
import org.hibernate.loader.EntityAliases;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Spefically a fetch return that refers to an entity association.
|
|
||||||
*
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public class EntityFetchReturn extends FetchReturn {
|
|
||||||
private final EntityAliases entityAliases;
|
|
||||||
|
|
||||||
public EntityFetchReturn(
|
|
||||||
String alias,
|
|
||||||
EntityAliases entityAliases,
|
|
||||||
NonScalarReturn owner,
|
|
||||||
String ownerProperty,
|
|
||||||
LockMode lockMode) {
|
|
||||||
super( owner, ownerProperty, alias, lockMode );
|
|
||||||
this.entityAliases = entityAliases;
|
|
||||||
}
|
|
||||||
|
|
||||||
public EntityAliases getEntityAliases() {
|
|
||||||
return entityAliases;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.custom;
|
|
||||||
import org.hibernate.LockMode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a return which names a fetched association.
|
|
||||||
*
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public abstract class FetchReturn extends NonScalarReturn {
|
|
||||||
private final NonScalarReturn owner;
|
|
||||||
private final String ownerProperty;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a return descriptor for an association fetch.
|
|
||||||
*
|
|
||||||
* @param owner The return descriptor for the owner of the fetch
|
|
||||||
* @param ownerProperty The name of the property represernting the association being fetched
|
|
||||||
* @param alias The alias for the fetch
|
|
||||||
* @param lockMode The lock mode to apply to the fetched association.
|
|
||||||
*/
|
|
||||||
public FetchReturn(
|
|
||||||
NonScalarReturn owner,
|
|
||||||
String ownerProperty,
|
|
||||||
String alias,
|
|
||||||
LockMode lockMode) {
|
|
||||||
super( alias, lockMode );
|
|
||||||
this.owner = owner;
|
|
||||||
this.ownerProperty = ownerProperty;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the return descriptor for the owner of this fetch.
|
|
||||||
*
|
|
||||||
* @return The owner
|
|
||||||
*/
|
|
||||||
public NonScalarReturn getOwner() {
|
|
||||||
return owner;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The name of the property on the owner which represents this association.
|
|
||||||
*
|
|
||||||
* @return The property name.
|
|
||||||
*/
|
|
||||||
public String getOwnerProperty() {
|
|
||||||
return ownerProperty;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,97 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.custom;
|
|
||||||
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.ResultSetMetaData;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.sql.Types;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.type.Type;
|
|
||||||
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
|
|
||||||
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simplified access to JDBC ResultSetMetaData
|
|
||||||
*
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
class JdbcResultMetadata {
|
|
||||||
private final SessionFactoryImplementor factory;
|
|
||||||
private final ResultSet resultSet;
|
|
||||||
private final ResultSetMetaData resultSetMetaData;
|
|
||||||
|
|
||||||
public JdbcResultMetadata(SessionFactoryImplementor factory, ResultSet resultSet) throws HibernateException {
|
|
||||||
try {
|
|
||||||
this.factory = factory;
|
|
||||||
this.resultSet = resultSet;
|
|
||||||
this.resultSetMetaData = resultSet.getMetaData();
|
|
||||||
}
|
|
||||||
catch( SQLException e ) {
|
|
||||||
throw new HibernateException( "Could not extract result set metadata", e );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getColumnCount() throws HibernateException {
|
|
||||||
try {
|
|
||||||
return resultSetMetaData.getColumnCount();
|
|
||||||
}
|
|
||||||
catch( SQLException e ) {
|
|
||||||
throw new HibernateException( "Could not determine result set column count", e );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int resolveColumnPosition(String columnName) throws HibernateException {
|
|
||||||
try {
|
|
||||||
return resultSet.findColumn( columnName );
|
|
||||||
}
|
|
||||||
catch( SQLException e ) {
|
|
||||||
throw new HibernateException( "Could not resolve column name in result set [" + columnName + "]", e );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getColumnName(int position) throws HibernateException {
|
|
||||||
try {
|
|
||||||
return factory.getDialect().getColumnAliasExtractor().extractColumnAlias( resultSetMetaData, position );
|
|
||||||
}
|
|
||||||
catch( SQLException e ) {
|
|
||||||
throw new HibernateException( "Could not resolve column name [" + position + "]", e );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Type getHibernateType(int columnPos) throws SQLException {
|
|
||||||
int columnType = resultSetMetaData.getColumnType( columnPos );
|
|
||||||
int scale = resultSetMetaData.getScale( columnPos );
|
|
||||||
int precision = resultSetMetaData.getPrecision( columnPos );
|
|
||||||
|
|
||||||
int length = precision;
|
|
||||||
if ( columnType == Types.CHAR && precision == 0 ) {
|
|
||||||
length = resultSetMetaData.getColumnDisplaySize( columnPos );
|
|
||||||
}
|
|
||||||
|
|
||||||
final String hibernateTypeName = factory.getDialect().getHibernateTypeName(
|
|
||||||
columnType,
|
|
||||||
length,
|
|
||||||
precision,
|
|
||||||
scale
|
|
||||||
);
|
|
||||||
|
|
||||||
if ( hibernateTypeName != null ) {
|
|
||||||
return factory.getTypeConfiguration().getBasicTypeRegistry().getRegisteredType( hibernateTypeName );
|
|
||||||
}
|
|
||||||
|
|
||||||
final SqlTypeDescriptor columnSqlTypeDescriptor = factory.getTypeConfiguration()
|
|
||||||
.getSqlTypeDescriptorRegistry()
|
|
||||||
.getDescriptor( columnType );
|
|
||||||
final BasicJavaDescriptor<Object> relationalJtd = columnSqlTypeDescriptor.getJdbcRecommendedJavaTypeMapping( factory.getTypeConfiguration() );
|
|
||||||
|
|
||||||
return factory.getTypeConfiguration().getBasicTypeRegistry().resolve( relationalJtd, columnSqlTypeDescriptor );
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.custom;
|
|
||||||
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|
||||||
import org.hibernate.type.Type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents non-scalar returns within the custom query. Most of the heavy lifting for non-scalar results
|
|
||||||
* is done within Loader itself.
|
|
||||||
*
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public class NonScalarResultColumnProcessor implements ResultColumnProcessor {
|
|
||||||
private final int position;
|
|
||||||
|
|
||||||
public NonScalarResultColumnProcessor(int position) {
|
|
||||||
this.position = position;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void performDiscovery(JdbcResultMetadata metadata, List<Type> types, List<String> aliases) {
|
|
||||||
// nothing to discover for non-scalar results
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object extract(Object[] data, ResultSet resultSet, SharedSessionContractImplementor session)
|
|
||||||
throws SQLException, HibernateException {
|
|
||||||
return data[ position ];
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.custom;
|
|
||||||
import org.hibernate.HibernateException;
|
|
||||||
import org.hibernate.LockMode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents some non-scalar (entity/collection) return within the query result.
|
|
||||||
*
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public abstract class NonScalarReturn implements Return {
|
|
||||||
private final String alias;
|
|
||||||
private final LockMode lockMode;
|
|
||||||
|
|
||||||
public NonScalarReturn(String alias, LockMode lockMode) {
|
|
||||||
this.alias = alias;
|
|
||||||
if ( alias == null ) {
|
|
||||||
throw new HibernateException("alias must be specified");
|
|
||||||
}
|
|
||||||
this.lockMode = lockMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getAlias() {
|
|
||||||
return alias;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LockMode getLockMode() {
|
|
||||||
return lockMode;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.custom;
|
|
||||||
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|
||||||
import org.hibernate.type.Type;
|
|
||||||
|
|
||||||
/** Processor for each "column" in a custom query result. May map to more than one physical column in the JDBC ResultSert.
|
|
||||||
*
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
interface ResultColumnProcessor {
|
|
||||||
/**
|
|
||||||
* Perform discovery, if needed. Typically discovery activities include looking up the column name in the
|
|
||||||
* ResultSet or JDBC type codes.
|
|
||||||
*
|
|
||||||
* @param metadata Delegate for accessing metadata about the JDBC ResultSet
|
|
||||||
* @param types The building List of types
|
|
||||||
* @param aliases The building list of column names/aliases
|
|
||||||
*
|
|
||||||
* @throws SQLException Indicates a problem accessing the JDBC objects
|
|
||||||
* @throws HibernateException Indicates a higher-level problem already categorized by Hibernate
|
|
||||||
*/
|
|
||||||
void performDiscovery(JdbcResultMetadata metadata, List<Type> types, List<String> aliases)
|
|
||||||
throws SQLException, HibernateException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform The extraction
|
|
||||||
*
|
|
||||||
* @param data All non-scalar results (handled at a higher level than these processors)
|
|
||||||
* @param resultSet The JDBC result set.
|
|
||||||
* @param session The Hibernate Session
|
|
||||||
*
|
|
||||||
* @return The extracted value
|
|
||||||
*
|
|
||||||
* @throws SQLException Indicates a problem accessing the JDBC objects
|
|
||||||
* @throws HibernateException Indicates a higher-level problem already categorized by Hibernate
|
|
||||||
*/
|
|
||||||
Object extract(Object[] data, ResultSet resultSet, SharedSessionContractImplementor session)
|
|
||||||
throws SQLException, HibernateException;
|
|
||||||
}
|
|
|
@ -1,89 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.custom;
|
|
||||||
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Models an entire "row" of results within a custom query
|
|
||||||
*
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public class ResultRowProcessor {
|
|
||||||
private final boolean hasScalars;
|
|
||||||
private ResultColumnProcessor[] columnProcessors;
|
|
||||||
|
|
||||||
public ResultRowProcessor(boolean hasScalars, ResultColumnProcessor[] columnProcessors) {
|
|
||||||
this.hasScalars = hasScalars || ( columnProcessors == null || columnProcessors.length == 0 );
|
|
||||||
this.columnProcessors = columnProcessors;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ResultColumnProcessor[] getColumnProcessors() {
|
|
||||||
return columnProcessors;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void prepareForAutoDiscovery(JdbcResultMetadata metadata) throws SQLException {
|
|
||||||
if ( columnProcessors == null || columnProcessors.length == 0 ) {
|
|
||||||
int columns = metadata.getColumnCount();
|
|
||||||
columnProcessors = new ResultColumnProcessor[ columns ];
|
|
||||||
for ( int i = 1; i <= columns; i++ ) {
|
|
||||||
columnProcessors[ i - 1 ] = new ScalarResultColumnProcessor( i );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build a logical result row.
|
|
||||||
* <p/>
|
|
||||||
* At this point, Loader has already processed all non-scalar result data. We
|
|
||||||
* just need to account for scalar result data here...
|
|
||||||
*
|
|
||||||
* @param data Entity data defined as "root returns" and already handled by the
|
|
||||||
* normal Loader mechanism.
|
|
||||||
* @param resultSet The JDBC result set (positioned at the row currently being processed).
|
|
||||||
* @param hasTransformer Does this query have an associated {@link org.hibernate.transform.ResultTransformer}
|
|
||||||
* @param session The session from which the query request originated.
|
|
||||||
* @return The logical result row
|
|
||||||
* @throws java.sql.SQLException
|
|
||||||
* @throws org.hibernate.HibernateException
|
|
||||||
*/
|
|
||||||
public Object buildResultRow(Object[] data, ResultSet resultSet, boolean hasTransformer, SharedSessionContractImplementor session)
|
|
||||||
throws SQLException, HibernateException {
|
|
||||||
final Object[] resultRow = buildResultRow( data, resultSet, session );
|
|
||||||
if ( hasTransformer ) {
|
|
||||||
return resultRow;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return resultRow.length == 1
|
|
||||||
? resultRow[0]
|
|
||||||
: resultRow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object[] buildResultRow(Object[] data, ResultSet resultSet, SharedSessionContractImplementor session)
|
|
||||||
throws SQLException, HibernateException {
|
|
||||||
Object[] resultRow;
|
|
||||||
if ( !hasScalars ) {
|
|
||||||
resultRow = data;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// build an array with indices equal to the total number
|
|
||||||
// of actual returns in the result Hibernate will return
|
|
||||||
// for this query (scalars + non-scalars)
|
|
||||||
resultRow = new Object[ columnProcessors.length ];
|
|
||||||
for ( int i = 0; i < columnProcessors.length; i++ ) {
|
|
||||||
resultRow[i] = columnProcessors[i].extract( data, resultSet, session );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return resultRow;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.custom;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a return in a custom query.
|
|
||||||
*
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public interface Return {
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.custom;
|
|
||||||
import org.hibernate.LockMode;
|
|
||||||
import org.hibernate.loader.EntityAliases;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a return which names a "root" entity.
|
|
||||||
* <p/>
|
|
||||||
* A root entity means it is explicitly a "column" in the result, as opposed to
|
|
||||||
* a fetched association.
|
|
||||||
*
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public class RootReturn extends NonScalarReturn {
|
|
||||||
private final String entityName;
|
|
||||||
private final EntityAliases entityAliases;
|
|
||||||
|
|
||||||
public RootReturn(
|
|
||||||
String alias,
|
|
||||||
String entityName,
|
|
||||||
EntityAliases entityAliases,
|
|
||||||
LockMode lockMode) {
|
|
||||||
super( alias, lockMode );
|
|
||||||
this.entityName = entityName;
|
|
||||||
this.entityAliases = entityAliases;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getEntityName() {
|
|
||||||
return entityName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public EntityAliases getEntityAliases() {
|
|
||||||
return entityAliases;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.custom;
|
|
||||||
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|
||||||
import org.hibernate.type.Type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a scalar result within the custom query
|
|
||||||
*
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public class ScalarResultColumnProcessor implements ResultColumnProcessor {
|
|
||||||
private int position = -1;
|
|
||||||
private String alias;
|
|
||||||
private Type type;
|
|
||||||
|
|
||||||
public ScalarResultColumnProcessor(int position) {
|
|
||||||
this.position = position;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ScalarResultColumnProcessor(String alias, Type type) {
|
|
||||||
this.alias = alias;
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void performDiscovery(JdbcResultMetadata metadata, List<Type> types, List<String> aliases) throws SQLException {
|
|
||||||
if ( alias == null ) {
|
|
||||||
alias = metadata.getColumnName( position );
|
|
||||||
}
|
|
||||||
else if ( position < 0 ) {
|
|
||||||
position = metadata.resolveColumnPosition( alias );
|
|
||||||
}
|
|
||||||
if ( type == null ) {
|
|
||||||
type = metadata.getHibernateType( position );
|
|
||||||
}
|
|
||||||
types.add( type );
|
|
||||||
aliases.add( alias );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object extract(Object[] data, ResultSet resultSet, SharedSessionContractImplementor session)
|
|
||||||
throws SQLException, HibernateException {
|
|
||||||
return type.nullSafeGet( resultSet, alias, session, null );
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.custom;
|
|
||||||
import org.hibernate.type.Type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represent a scalar (aka simple value) return within a query result.
|
|
||||||
*
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public class ScalarReturn implements Return {
|
|
||||||
private final Type type;
|
|
||||||
private final String columnAlias;
|
|
||||||
|
|
||||||
public ScalarReturn(Type type, String columnAlias) {
|
|
||||||
this.type = type;
|
|
||||||
this.columnAlias = columnAlias;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Type getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getColumnAlias() {
|
|
||||||
return columnAlias;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
<!--
|
|
||||||
~ Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
~
|
|
||||||
~ License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
~ See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<html>
|
|
||||||
<head></head>
|
|
||||||
<body>
|
|
||||||
<p>
|
|
||||||
This package defines a framework for custom loaders that accept
|
|
||||||
handwritten SQL
|
|
||||||
</p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,37 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.custom.sql;
|
|
||||||
|
|
||||||
import java.sql.PreparedStatement;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
import org.hibernate.engine.spi.QueryParameters;
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|
||||||
import org.hibernate.engine.spi.TypedValue;
|
|
||||||
import org.hibernate.param.ParameterBinder;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public class NamedParamBinder implements ParameterBinder {
|
|
||||||
private final String name;
|
|
||||||
|
|
||||||
public NamedParamBinder(String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int bind(
|
|
||||||
PreparedStatement statement,
|
|
||||||
QueryParameters qp,
|
|
||||||
SharedSessionContractImplementor session,
|
|
||||||
int position) throws SQLException {
|
|
||||||
final TypedValue typedValue = qp.getNamedParameters().get( name );
|
|
||||||
typedValue.getType().nullSafeSet( statement, typedValue.getValue(), position, session );
|
|
||||||
return typedValue.getType().getColumnSpan( session.getFactory() );
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.custom.sql;
|
|
||||||
|
|
||||||
import java.sql.PreparedStatement;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
import org.hibernate.engine.spi.QueryParameters;
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|
||||||
import org.hibernate.engine.spi.TypedValue;
|
|
||||||
import org.hibernate.param.ParameterBinder;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public class PositionalParamBinder implements ParameterBinder {
|
|
||||||
private final int label;
|
|
||||||
|
|
||||||
public PositionalParamBinder(int label) {
|
|
||||||
this.label = label;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int bind(
|
|
||||||
PreparedStatement statement,
|
|
||||||
QueryParameters qp,
|
|
||||||
SharedSessionContractImplementor session,
|
|
||||||
int position) throws SQLException {
|
|
||||||
final TypedValue typedValue = qp.getNamedParameters().get( Integer.toString( label ) );
|
|
||||||
typedValue.getType().nullSafeSet( statement, typedValue.getValue(), position, session );
|
|
||||||
return typedValue.getType().getColumnSpan( session.getFactory() );
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,243 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.custom.sql;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
|
||||||
import org.hibernate.engine.query.spi.sql.NativeSQLQueryReturn;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.internal.CoreMessageLogger;
|
|
||||||
import org.hibernate.loader.custom.CustomQuery;
|
|
||||||
import org.hibernate.param.ParameterBinder;
|
|
||||||
import org.hibernate.persister.collection.SQLLoadableCollection;
|
|
||||||
import org.hibernate.persister.entity.SQLLoadable;
|
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements Hibernate's built-in support for native SQL queries.
|
|
||||||
* <p/>
|
|
||||||
* This support is built on top of the notion of "custom queries"...
|
|
||||||
*
|
|
||||||
* @author Gavin King
|
|
||||||
* @author Max Andersen
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public class SQLCustomQuery implements CustomQuery, Serializable {
|
|
||||||
|
|
||||||
private static final CoreMessageLogger LOG = Logger.getMessageLogger( CoreMessageLogger.class, SQLCustomQuery.class.getName() );
|
|
||||||
|
|
||||||
private final String sql;
|
|
||||||
private final Set querySpaces = new HashSet();
|
|
||||||
|
|
||||||
private final List<ParameterBinder> paramValueBinders;
|
|
||||||
|
|
||||||
private final List customQueryReturns = new ArrayList();
|
|
||||||
|
|
||||||
|
|
||||||
public String getSQL() {
|
|
||||||
return sql;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set getQuerySpaces() {
|
|
||||||
return querySpaces;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<ParameterBinder > getParameterValueBinders() {
|
|
||||||
return paramValueBinders;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List getCustomQueryReturns() {
|
|
||||||
return customQueryReturns;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SQLCustomQuery(
|
|
||||||
final String sqlQuery,
|
|
||||||
final NativeSQLQueryReturn[] queryReturns,
|
|
||||||
final Collection additionalQuerySpaces,
|
|
||||||
final SessionFactoryImplementor factory) throws HibernateException {
|
|
||||||
|
|
||||||
LOG.tracev( "Starting processing of sql query [{0}]", sqlQuery );
|
|
||||||
SQLQueryReturnProcessor processor = new SQLQueryReturnProcessor(queryReturns, factory);
|
|
||||||
SQLQueryReturnProcessor.ResultAliasContext aliasContext = processor.process();
|
|
||||||
|
|
||||||
|
|
||||||
// Map[] propertyResultMaps = (Map[]) processor.getPropertyResults().toArray( new Map[0] );
|
|
||||||
// Map[] collectionResultMaps = (Map[]) processor.getCollectionPropertyResults().toArray( new Map[0] );
|
|
||||||
//
|
|
||||||
// List collectionSuffixes = new ArrayList();
|
|
||||||
// List collectionOwnerAliases = processor.getCollectionOwnerAliases();
|
|
||||||
// List collectionPersisters = processor.getCollectionPersisters();
|
|
||||||
// int size = collectionPersisters.size();
|
|
||||||
// if (size!=0) {
|
|
||||||
// collectionOwners = new int[size];
|
|
||||||
// collectionRoles = new String[size];
|
|
||||||
// //collectionDescriptors = new CollectionAliases[size];
|
|
||||||
// for ( int i=0; i<size; i++ ) {
|
|
||||||
// CollectionPersister collectionPersister = (CollectionPersister) collectionPersisters.get(i);
|
|
||||||
// collectionRoles[i] = ( collectionPersister ).getRole();
|
|
||||||
// collectionOwners[i] = processor.getAliases().indexOf( collectionOwnerAliases.get(i) );
|
|
||||||
// String suffix = i + "__";
|
|
||||||
// collectionSuffixes.add(suffix);
|
|
||||||
// //collectionDescriptors[i] = new GeneratedCollectionAliases( collectionResultMaps[i], collectionPersister, suffix );
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// collectionRoles = null;
|
|
||||||
// //collectionDescriptors = null;
|
|
||||||
// collectionOwners = null;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// String[] aliases = ArrayHelper.toStringArray( processor.getAliases() );
|
|
||||||
// String[] collAliases = ArrayHelper.toStringArray( processor.getCollectionAliases() );
|
|
||||||
// String[] collSuffixes = ArrayHelper.toStringArray(collectionSuffixes);
|
|
||||||
//
|
|
||||||
// SQLLoadable[] entityPersisters = (SQLLoadable[]) processor.getPersisters().toArray( new SQLLoadable[0] );
|
|
||||||
// SQLLoadableCollection[] collPersisters = (SQLLoadableCollection[]) collectionPersisters.toArray( new SQLLoadableCollection[0] );
|
|
||||||
// lockModes = (LockMode[]) processor.getLockModes().toArray( new LockMode[0] );
|
|
||||||
//
|
|
||||||
// scalarColumnAliases = ArrayHelper.toStringArray( processor.getScalarColumnAliases() );
|
|
||||||
// scalarTypes = ArrayHelper.toTypeArray( processor.getScalarTypes() );
|
|
||||||
//
|
|
||||||
// // need to match the "sequence" of what we return. scalar first, entity last.
|
|
||||||
// returnAliases = ArrayHelper.join(scalarColumnAliases, aliases);
|
|
||||||
//
|
|
||||||
// String[] suffixes = BasicLoader.generateSuffixes(entityPersisters.length);
|
|
||||||
|
|
||||||
SQLQueryParser parser = new SQLQueryParser( sqlQuery, new ParserContext( aliasContext ), factory );
|
|
||||||
this.sql = parser.process();
|
|
||||||
|
|
||||||
this.paramValueBinders = parser.getParameterValueBinders();
|
|
||||||
|
|
||||||
// SQLQueryParser parser = new SQLQueryParser(
|
|
||||||
// sqlQuery,
|
|
||||||
// processor.getAlias2Persister(),
|
|
||||||
// processor.getAlias2Return(),
|
|
||||||
// aliases,
|
|
||||||
// collAliases,
|
|
||||||
// collPersisters,
|
|
||||||
// suffixes,
|
|
||||||
// collSuffixes
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// sql = parser.process();
|
|
||||||
//
|
|
||||||
// namedParameterBindPoints = parser.getNamedParameters();
|
|
||||||
|
|
||||||
|
|
||||||
customQueryReturns.addAll( processor.generateCustomReturns( parser.queryHasAliases() ) );
|
|
||||||
|
|
||||||
// // Populate entityNames, entityDescrptors and querySpaces
|
|
||||||
// entityNames = new String[entityPersisters.length];
|
|
||||||
// entityDescriptors = new EntityAliases[entityPersisters.length];
|
|
||||||
// for (int i = 0; i < entityPersisters.length; i++) {
|
|
||||||
// SQLLoadable persister = entityPersisters[i];
|
|
||||||
// //alias2Persister.put( aliases[i], persister );
|
|
||||||
// //TODO: Does not consider any other tables referenced in the query
|
|
||||||
// ArrayHelper.addAll( querySpaces, persister.getQuerySpaces() );
|
|
||||||
// entityNames[i] = persister.getEntityName();
|
|
||||||
// if ( parser.queryHasAliases() ) {
|
|
||||||
// entityDescriptors[i] = new DefaultEntityAliases(
|
|
||||||
// propertyResultMaps[i],
|
|
||||||
// entityPersisters[i],
|
|
||||||
// suffixes[i]
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// entityDescriptors[i] = new ColumnEntityAliases(
|
|
||||||
// propertyResultMaps[i],
|
|
||||||
// entityPersisters[i],
|
|
||||||
// suffixes[i]
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
if ( additionalQuerySpaces != null ) {
|
|
||||||
querySpaces.addAll( additionalQuerySpaces );
|
|
||||||
}
|
|
||||||
|
|
||||||
// if (size!=0) {
|
|
||||||
// collectionDescriptors = new CollectionAliases[size];
|
|
||||||
// for ( int i=0; i<size; i++ ) {
|
|
||||||
// CollectionPersister collectionPersister = (CollectionPersister) collectionPersisters.get(i);
|
|
||||||
// String suffix = i + "__";
|
|
||||||
// if( parser.queryHasAliases() ) {
|
|
||||||
// collectionDescriptors[i] = new GeneratedCollectionAliases( collectionResultMaps[i], collectionPersister, suffix );
|
|
||||||
// } else {
|
|
||||||
// collectionDescriptors[i] = new ColumnCollectionAliases( collectionResultMaps[i], (SQLLoadableCollection) collectionPersister );
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// collectionDescriptors = null;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// // Resolve owners
|
|
||||||
// Map alias2OwnerAlias = processor.getAlias2OwnerAlias();
|
|
||||||
// int[] ownersArray = new int[entityPersisters.length];
|
|
||||||
// for ( int j=0; j < aliases.length; j++ ) {
|
|
||||||
// String ownerAlias = (String) alias2OwnerAlias.get( aliases[j] );
|
|
||||||
// if ( StringHelper.isNotEmpty(ownerAlias) ) {
|
|
||||||
// ownersArray[j] = processor.getAliases().indexOf( ownerAlias );
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// ownersArray[j] = -1;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if ( ArrayHelper.isAllNegative(ownersArray) ) {
|
|
||||||
// ownersArray = null;
|
|
||||||
// }
|
|
||||||
// this.entityOwners = ownersArray;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static class ParserContext implements SQLQueryParser.ParserContext {
|
|
||||||
|
|
||||||
private final SQLQueryReturnProcessor.ResultAliasContext aliasContext;
|
|
||||||
|
|
||||||
public ParserContext(SQLQueryReturnProcessor.ResultAliasContext aliasContext) {
|
|
||||||
this.aliasContext = aliasContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEntityAlias(String alias) {
|
|
||||||
return getEntityPersisterByAlias( alias ) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SQLLoadable getEntityPersisterByAlias(String alias) {
|
|
||||||
return aliasContext.getEntityPersister( alias );
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getEntitySuffixByAlias(String alias) {
|
|
||||||
return aliasContext.getEntitySuffix( alias );
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isCollectionAlias(String alias) {
|
|
||||||
return getCollectionPersisterByAlias( alias ) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SQLLoadableCollection getCollectionPersisterByAlias(String alias) {
|
|
||||||
return aliasContext.getCollectionPersister( alias );
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCollectionSuffixByAlias(String alias) {
|
|
||||||
return aliasContext.getCollectionSuffix( alias );
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map getPropertyResultsMapByAlias(String alias) {
|
|
||||||
return aliasContext.getPropertyResultsMap( alias );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,351 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.custom.sql;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import org.hibernate.QueryException;
|
|
||||||
import org.hibernate.query.sql.internal.ParameterParser;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.param.ParameterBinder;
|
|
||||||
import org.hibernate.persister.collection.SQLLoadableCollection;
|
|
||||||
import org.hibernate.persister.entity.SQLLoadable;
|
|
||||||
import org.hibernate.query.sql.spi.ParameterRecognizer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Gavin King
|
|
||||||
* @author Max Andersen
|
|
||||||
* @author Steve Ebersole
|
|
||||||
* @author Paul Benedict
|
|
||||||
*/
|
|
||||||
public class SQLQueryParser {
|
|
||||||
private static final Pattern PREPARED_STATEMENT_PATTERN = Pattern.compile( "^\\{.*?\\}$" );
|
|
||||||
private static final String HIBERNATE_PLACEHOLDER_PREFIX = "h-";
|
|
||||||
private static final String DOMAIN_PLACEHOLDER = "h-domain";
|
|
||||||
private static final String CATALOG_PLACEHOLDER = "h-catalog";
|
|
||||||
private static final String SCHEMA_PLACEHOLDER = "h-schema";
|
|
||||||
|
|
||||||
private final SessionFactoryImplementor factory;
|
|
||||||
private final String originalQueryString;
|
|
||||||
private final ParserContext context;
|
|
||||||
|
|
||||||
private long aliasesFound;
|
|
||||||
|
|
||||||
private List<ParameterBinder> paramValueBinders;
|
|
||||||
|
|
||||||
interface ParserContext {
|
|
||||||
boolean isEntityAlias(String aliasName);
|
|
||||||
SQLLoadable getEntityPersisterByAlias(String alias);
|
|
||||||
String getEntitySuffixByAlias(String alias);
|
|
||||||
boolean isCollectionAlias(String aliasName);
|
|
||||||
SQLLoadableCollection getCollectionPersisterByAlias(String alias);
|
|
||||||
String getCollectionSuffixByAlias(String alias);
|
|
||||||
Map getPropertyResultsMapByAlias(String alias);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SQLQueryParser(String queryString, ParserContext context, SessionFactoryImplementor factory) {
|
|
||||||
this.originalQueryString = queryString;
|
|
||||||
this.context = context;
|
|
||||||
this.factory = factory;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<ParameterBinder> getParameterValueBinders() {
|
|
||||||
return paramValueBinders == null ? Collections.emptyList() : paramValueBinders;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean queryHasAliases() {
|
|
||||||
return aliasesFound>0;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String getOriginalQueryString() {
|
|
||||||
return originalQueryString;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String process() {
|
|
||||||
String processedSql = substituteBrackets( originalQueryString );
|
|
||||||
processedSql = substituteParams( processedSql );
|
|
||||||
return processedSql;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: should "record" how many properties we have reffered to - and if we
|
|
||||||
// don't get'em'all we throw an exception! Way better than trial and error ;)
|
|
||||||
protected String substituteBrackets(String sqlQuery) throws QueryException {
|
|
||||||
|
|
||||||
if ( PREPARED_STATEMENT_PATTERN.matcher( sqlQuery.trim() ).matches() ) {
|
|
||||||
return sqlQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
StringBuilder result = new StringBuilder( sqlQuery.length() + 20 );
|
|
||||||
int left, right;
|
|
||||||
|
|
||||||
// replace {....} with corresponding column aliases
|
|
||||||
for ( int curr = 0; curr < sqlQuery.length(); curr = right + 1 ) {
|
|
||||||
if ( ( left = sqlQuery.indexOf( '{', curr ) ) < 0 ) {
|
|
||||||
// No additional open braces found in the string, append the
|
|
||||||
// rest of the string in its entirety and quit this loop
|
|
||||||
result.append( sqlQuery.substring( curr ) );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// apend everything up until the next encountered open brace
|
|
||||||
result.append( sqlQuery.substring( curr, left ) );
|
|
||||||
|
|
||||||
if ( ( right = sqlQuery.indexOf( '}', left + 1 ) ) < 0 ) {
|
|
||||||
throw new QueryException( "Unmatched braces for alias path", sqlQuery );
|
|
||||||
}
|
|
||||||
|
|
||||||
final String aliasPath = sqlQuery.substring( left + 1, right );
|
|
||||||
boolean isPlaceholder = aliasPath.startsWith( HIBERNATE_PLACEHOLDER_PREFIX );
|
|
||||||
|
|
||||||
if ( isPlaceholder ) {
|
|
||||||
// Domain replacement
|
|
||||||
if ( DOMAIN_PLACEHOLDER.equals( aliasPath ) ) {
|
|
||||||
final String catalogName = factory.getSettings().getDefaultCatalogName();
|
|
||||||
if ( catalogName != null ) {
|
|
||||||
result.append( catalogName );
|
|
||||||
result.append( "." );
|
|
||||||
}
|
|
||||||
final String schemaName = factory.getSettings().getDefaultSchemaName();
|
|
||||||
if ( schemaName != null ) {
|
|
||||||
result.append( schemaName );
|
|
||||||
result.append( "." );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Schema replacement
|
|
||||||
else if ( SCHEMA_PLACEHOLDER.equals( aliasPath ) ) {
|
|
||||||
final String schemaName = factory.getSettings().getDefaultSchemaName();
|
|
||||||
if ( schemaName != null ) {
|
|
||||||
result.append(schemaName);
|
|
||||||
result.append(".");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Catalog replacement
|
|
||||||
else if ( CATALOG_PLACEHOLDER.equals( aliasPath ) ) {
|
|
||||||
final String catalogName = factory.getSettings().getDefaultCatalogName();
|
|
||||||
if ( catalogName != null ) {
|
|
||||||
result.append( catalogName );
|
|
||||||
result.append( "." );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new QueryException( "Unknown placeholder ", aliasPath );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (context != null) {
|
|
||||||
int firstDot = aliasPath.indexOf( '.' );
|
|
||||||
if ( firstDot == -1 ) {
|
|
||||||
if ( context.isEntityAlias( aliasPath ) ) {
|
|
||||||
// it is a simple table alias {foo}
|
|
||||||
result.append( aliasPath );
|
|
||||||
aliasesFound++;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// passing through anything we do not know : to support jdbc escape sequences HB-898
|
|
||||||
result.append( '{' ).append(aliasPath).append( '}' );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
final String aliasName = aliasPath.substring( 0, firstDot );
|
|
||||||
if ( context.isCollectionAlias( aliasName ) ) {
|
|
||||||
// The current alias is referencing the collection to be eagerly fetched
|
|
||||||
String propertyName = aliasPath.substring( firstDot + 1 );
|
|
||||||
result.append( resolveCollectionProperties( aliasName, propertyName ) );
|
|
||||||
aliasesFound++;
|
|
||||||
}
|
|
||||||
else if ( context.isEntityAlias( aliasName ) ) {
|
|
||||||
// it is a property reference {foo.bar}
|
|
||||||
String propertyName = aliasPath.substring( firstDot + 1 );
|
|
||||||
result.append( resolveProperties( aliasName, propertyName ) );
|
|
||||||
aliasesFound++;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// passing through anything we do not know : to support jdbc escape sequences HB-898
|
|
||||||
result.append( '{' ).append(aliasPath).append( '}' );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
result.append( '{' ).append(aliasPath).append( '}' );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Possibly handle :something parameters for the query ?
|
|
||||||
|
|
||||||
return result.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String resolveCollectionProperties(
|
|
||||||
String aliasName,
|
|
||||||
String propertyName) {
|
|
||||||
|
|
||||||
Map fieldResults = context.getPropertyResultsMapByAlias( aliasName );
|
|
||||||
SQLLoadableCollection collectionPersister = context.getCollectionPersisterByAlias( aliasName );
|
|
||||||
String collectionSuffix = context.getCollectionSuffixByAlias( aliasName );
|
|
||||||
|
|
||||||
if ( "*".equals( propertyName ) ) {
|
|
||||||
if( !fieldResults.isEmpty() ) {
|
|
||||||
throw new QueryException("Using return-propertys together with * syntax is not supported.");
|
|
||||||
}
|
|
||||||
|
|
||||||
String selectFragment = collectionPersister.selectFragment( aliasName, collectionSuffix );
|
|
||||||
aliasesFound++;
|
|
||||||
return selectFragment
|
|
||||||
+ ", "
|
|
||||||
+ resolveProperties( aliasName, propertyName );
|
|
||||||
}
|
|
||||||
else if ( "element.*".equals( propertyName ) ) {
|
|
||||||
return resolveProperties( aliasName, "*" );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
String[] columnAliases;
|
|
||||||
|
|
||||||
// Let return-propertys override whatever the persister has for aliases.
|
|
||||||
columnAliases = ( String[] ) fieldResults.get(propertyName);
|
|
||||||
if ( columnAliases==null ) {
|
|
||||||
columnAliases = collectionPersister.getCollectionPropertyColumnAliases( propertyName, collectionSuffix );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( columnAliases == null || columnAliases.length == 0 ) {
|
|
||||||
throw new QueryException(
|
|
||||||
"No column name found for property [" + propertyName + "] for alias [" + aliasName + "]",
|
|
||||||
originalQueryString
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if ( columnAliases.length != 1 ) {
|
|
||||||
// TODO: better error message since we actually support composites if names are explicitly listed.
|
|
||||||
throw new QueryException(
|
|
||||||
"SQL queries only support properties mapped to a single column - property [" +
|
|
||||||
propertyName + "] is mapped to " + columnAliases.length + " columns.",
|
|
||||||
originalQueryString
|
|
||||||
);
|
|
||||||
}
|
|
||||||
aliasesFound++;
|
|
||||||
return columnAliases[0];
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private String resolveProperties(String aliasName, String propertyName) {
|
|
||||||
Map fieldResults = context.getPropertyResultsMapByAlias( aliasName );
|
|
||||||
SQLLoadable persister = context.getEntityPersisterByAlias( aliasName );
|
|
||||||
String suffix = context.getEntitySuffixByAlias( aliasName );
|
|
||||||
|
|
||||||
if ( "*".equals( propertyName ) ) {
|
|
||||||
if( !fieldResults.isEmpty() ) {
|
|
||||||
throw new QueryException("Using return-propertys together with * syntax is not supported.");
|
|
||||||
}
|
|
||||||
aliasesFound++;
|
|
||||||
return persister.selectFragment( aliasName, suffix ) ;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
String[] columnAliases;
|
|
||||||
|
|
||||||
// Let return-propertys override whatever the persister has for aliases.
|
|
||||||
columnAliases = (String[]) fieldResults.get( propertyName );
|
|
||||||
if ( columnAliases == null ) {
|
|
||||||
columnAliases = persister.getSubclassPropertyColumnAliases( propertyName, suffix );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( columnAliases == null || columnAliases.length == 0 ) {
|
|
||||||
throw new QueryException(
|
|
||||||
"No column name found for property [" + propertyName + "] for alias [" + aliasName + "]",
|
|
||||||
originalQueryString
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if ( columnAliases.length != 1 ) {
|
|
||||||
// TODO: better error message since we actually support composites if names are explicitly listed.
|
|
||||||
throw new QueryException(
|
|
||||||
"SQL queries only support properties mapped to a single column - property [" + propertyName + "] is mapped to " + columnAliases.length + " columns.",
|
|
||||||
originalQueryString
|
|
||||||
);
|
|
||||||
}
|
|
||||||
aliasesFound++;
|
|
||||||
return columnAliases[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Substitues JDBC parameter placeholders (?) for all encountered
|
|
||||||
* parameter specifications. It also tracks the positions of these
|
|
||||||
* parameter specifications within the query string. This accounts for
|
|
||||||
* ordinal-params, named-params, and ejb3-positional-params.
|
|
||||||
*
|
|
||||||
* @param sqlString The query string.
|
|
||||||
* @return The SQL query with parameter substitution complete.
|
|
||||||
*/
|
|
||||||
private String substituteParams(String sqlString) {
|
|
||||||
final ParameterSubstitutionRecognizer recognizer = new ParameterSubstitutionRecognizer( factory );
|
|
||||||
ParameterParser.parse( sqlString, recognizer );
|
|
||||||
|
|
||||||
paramValueBinders = recognizer.getParameterValueBinders();
|
|
||||||
|
|
||||||
return recognizer.result.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class ParameterSubstitutionRecognizer implements ParameterRecognizer {
|
|
||||||
StringBuilder result = new StringBuilder();
|
|
||||||
|
|
||||||
int jdbcPositionalParamCount;
|
|
||||||
private List<ParameterBinder> paramValueBinders;
|
|
||||||
|
|
||||||
public ParameterSubstitutionRecognizer(SessionFactoryImplementor factory) {
|
|
||||||
this.jdbcPositionalParamCount = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void outParameter(int position) {
|
|
||||||
result.append( '?' );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void ordinalParameter(int position) {
|
|
||||||
result.append( '?' );
|
|
||||||
registerPositionParamBinder( jdbcPositionalParamCount++ );
|
|
||||||
}
|
|
||||||
|
|
||||||
private void registerPositionParamBinder(int label) {
|
|
||||||
if ( paramValueBinders == null ) {
|
|
||||||
paramValueBinders = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
paramValueBinders.add( new PositionalParamBinder( label ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void jpaPositionalParameter(int name, int position) {
|
|
||||||
result.append( '?' );
|
|
||||||
registerPositionParamBinder( name );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void namedParameter(String name, int position) {
|
|
||||||
result.append( '?' );
|
|
||||||
registerNamedParamBinder( name );
|
|
||||||
}
|
|
||||||
|
|
||||||
private void registerNamedParamBinder(String name) {
|
|
||||||
if ( paramValueBinders == null ) {
|
|
||||||
paramValueBinders = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
paramValueBinders.add( new NamedParamBinder( name ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void other(char character) {
|
|
||||||
result.append( character );
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<ParameterBinder> getParameterValueBinders() {
|
|
||||||
return paramValueBinders;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,642 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.custom.sql;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
|
||||||
import org.hibernate.MappingException;
|
|
||||||
import org.hibernate.engine.query.spi.sql.NativeSQLQueryCollectionReturn;
|
|
||||||
import org.hibernate.engine.query.spi.sql.NativeSQLQueryConstructorReturn;
|
|
||||||
import org.hibernate.engine.query.spi.sql.NativeSQLQueryJoinReturn;
|
|
||||||
import org.hibernate.engine.query.spi.sql.NativeSQLQueryNonScalarReturn;
|
|
||||||
import org.hibernate.engine.query.spi.sql.NativeSQLQueryReturn;
|
|
||||||
import org.hibernate.engine.query.spi.sql.NativeSQLQueryRootReturn;
|
|
||||||
import org.hibernate.engine.query.spi.sql.NativeSQLQueryScalarReturn;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.internal.CoreLogging;
|
|
||||||
import org.hibernate.internal.CoreMessageLogger;
|
|
||||||
import org.hibernate.loader.BasicLoader;
|
|
||||||
import org.hibernate.loader.CollectionAliases;
|
|
||||||
import org.hibernate.loader.ColumnEntityAliases;
|
|
||||||
import org.hibernate.loader.DefaultEntityAliases;
|
|
||||||
import org.hibernate.loader.EntityAliases;
|
|
||||||
import org.hibernate.loader.GeneratedCollectionAliases;
|
|
||||||
import org.hibernate.loader.custom.CollectionFetchReturn;
|
|
||||||
import org.hibernate.loader.custom.CollectionReturn;
|
|
||||||
import org.hibernate.loader.custom.ColumnCollectionAliases;
|
|
||||||
import org.hibernate.loader.custom.ConstructorReturn;
|
|
||||||
import org.hibernate.loader.custom.EntityFetchReturn;
|
|
||||||
import org.hibernate.loader.custom.FetchReturn;
|
|
||||||
import org.hibernate.loader.custom.NonScalarReturn;
|
|
||||||
import org.hibernate.loader.custom.Return;
|
|
||||||
import org.hibernate.loader.custom.RootReturn;
|
|
||||||
import org.hibernate.loader.custom.ScalarReturn;
|
|
||||||
import org.hibernate.persister.collection.CollectionPersister;
|
|
||||||
import org.hibernate.persister.collection.SQLLoadableCollection;
|
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
|
||||||
import org.hibernate.persister.entity.Joinable;
|
|
||||||
import org.hibernate.persister.entity.SQLLoadable;
|
|
||||||
import org.hibernate.type.EntityType;
|
|
||||||
import org.hibernate.type.Type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Responsible for processing the series of {@link org.hibernate.engine.query.spi.sql.NativeSQLQueryReturn returns}
|
|
||||||
* defined by a {@link org.hibernate.engine.query.spi.sql.NativeSQLQuerySpecification} and
|
|
||||||
* breaking them down into a series of {@link Return returns} for use within the
|
|
||||||
* {@link org.hibernate.loader.custom.CustomLoader}.
|
|
||||||
*
|
|
||||||
* @author Gavin King
|
|
||||||
* @author Max Andersen
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public class SQLQueryReturnProcessor {
|
|
||||||
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( SQLQueryReturnProcessor.class );
|
|
||||||
|
|
||||||
private static final NativeSQLQueryReturn[] NO_RETURNS = new NativeSQLQueryReturn[0];
|
|
||||||
|
|
||||||
private NativeSQLQueryReturn[] queryReturns;
|
|
||||||
|
|
||||||
// private final List persisters = new ArrayList();
|
|
||||||
|
|
||||||
private final Map alias2Return = new HashMap();
|
|
||||||
private final Map alias2OwnerAlias = new HashMap();
|
|
||||||
|
|
||||||
private final Map<String,EntityPersister> alias2Persister = new HashMap<String,EntityPersister>();
|
|
||||||
private final Map alias2Suffix = new HashMap();
|
|
||||||
|
|
||||||
private final Map<String,CollectionPersister> alias2CollectionPersister = new HashMap<String,CollectionPersister>();
|
|
||||||
private final Map alias2CollectionSuffix = new HashMap();
|
|
||||||
|
|
||||||
private final Map entityPropertyResultMaps = new HashMap();
|
|
||||||
private final Map collectionPropertyResultMaps = new HashMap();
|
|
||||||
|
|
||||||
// private final List scalarTypes = new ArrayList();
|
|
||||||
// private final List scalarColumnAliases = new ArrayList();
|
|
||||||
|
|
||||||
private final SessionFactoryImplementor factory;
|
|
||||||
|
|
||||||
// private List collectionOwnerAliases = new ArrayList();
|
|
||||||
// private List collectionAliases = new ArrayList();
|
|
||||||
// private List collectionPersisters = new ArrayList();
|
|
||||||
// private List collectionResults = new ArrayList();
|
|
||||||
|
|
||||||
private int entitySuffixSeed;
|
|
||||||
private int collectionSuffixSeed;
|
|
||||||
|
|
||||||
|
|
||||||
public SQLQueryReturnProcessor(NativeSQLQueryReturn[] queryReturns, SessionFactoryImplementor factory) {
|
|
||||||
this.queryReturns = queryReturns == null ? NO_RETURNS : queryReturns;
|
|
||||||
this.factory = factory;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ResultAliasContext {
|
|
||||||
public SQLLoadable getEntityPersister(String alias) {
|
|
||||||
return (SQLLoadable) alias2Persister.get( alias );
|
|
||||||
}
|
|
||||||
|
|
||||||
public SQLLoadableCollection getCollectionPersister(String alias) {
|
|
||||||
return (SQLLoadableCollection) alias2CollectionPersister.get( alias );
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getEntitySuffix(String alias) {
|
|
||||||
return (String) alias2Suffix.get( alias );
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCollectionSuffix(String alias) {
|
|
||||||
return (String) alias2CollectionSuffix.get( alias );
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getOwnerAlias(String alias) {
|
|
||||||
return (String) alias2OwnerAlias.get( alias );
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map getPropertyResultsMap(String alias) {
|
|
||||||
return internalGetPropertyResultsMap( alias );
|
|
||||||
}
|
|
||||||
|
|
||||||
public String[] collectQuerySpaces() {
|
|
||||||
final HashSet<String> spaces = new HashSet<String>();
|
|
||||||
collectQuerySpaces( spaces );
|
|
||||||
return spaces.toArray( new String[ spaces.size() ] );
|
|
||||||
}
|
|
||||||
|
|
||||||
public void collectQuerySpaces(Collection<String> spaces) {
|
|
||||||
for ( EntityPersister persister : alias2Persister.values() ) {
|
|
||||||
Collections.addAll( spaces, (String[]) persister.getQuerySpaces() );
|
|
||||||
}
|
|
||||||
for ( CollectionPersister persister : alias2CollectionPersister.values() ) {
|
|
||||||
final Type elementType = persister.getElementType();
|
|
||||||
if ( elementType.isEntityType() && ! elementType.isAnyType() ) {
|
|
||||||
final Joinable joinable = ( (EntityType) elementType ).getAssociatedJoinable( factory );
|
|
||||||
Collections.addAll( spaces, (String[]) ( (EntityPersister) joinable ).getQuerySpaces() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map internalGetPropertyResultsMap(String alias) {
|
|
||||||
NativeSQLQueryReturn rtn = ( NativeSQLQueryReturn ) alias2Return.get( alias );
|
|
||||||
if ( rtn instanceof NativeSQLQueryNonScalarReturn ) {
|
|
||||||
return ( ( NativeSQLQueryNonScalarReturn ) rtn ).getPropertyResultsMap();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean hasPropertyResultMap(String alias) {
|
|
||||||
Map propertyMaps = internalGetPropertyResultsMap( alias );
|
|
||||||
return propertyMaps != null && ! propertyMaps.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ResultAliasContext process() {
|
|
||||||
// first, break down the returns into maps keyed by alias
|
|
||||||
// so that role returns can be more easily resolved to their owners
|
|
||||||
for ( NativeSQLQueryReturn queryReturn : queryReturns ) {
|
|
||||||
if ( queryReturn instanceof NativeSQLQueryNonScalarReturn ) {
|
|
||||||
NativeSQLQueryNonScalarReturn rtn = (NativeSQLQueryNonScalarReturn) queryReturn;
|
|
||||||
alias2Return.put( rtn.getAlias(), rtn );
|
|
||||||
if ( rtn instanceof NativeSQLQueryJoinReturn ) {
|
|
||||||
NativeSQLQueryJoinReturn fetchReturn = (NativeSQLQueryJoinReturn) rtn;
|
|
||||||
alias2OwnerAlias.put( fetchReturn.getAlias(), fetchReturn.getOwnerAlias() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now, process the returns
|
|
||||||
for ( NativeSQLQueryReturn queryReturn : queryReturns ) {
|
|
||||||
processReturn( queryReturn );
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ResultAliasContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
private interface QueryReturnVisitor {
|
|
||||||
void visitScalarReturn(NativeSQLQueryScalarReturn rtn);
|
|
||||||
void visitRootReturn(NativeSQLQueryRootReturn rtn);
|
|
||||||
void visitCollectionReturn(NativeSQLQueryCollectionReturn rtn);
|
|
||||||
|
|
||||||
void visitFetch(NativeSQLQueryJoinReturn rtn);
|
|
||||||
|
|
||||||
void visitDynamicInstantiation(NativeSQLQueryConstructorReturn rtn);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void visitReturns(QueryReturnVisitor visitor) {
|
|
||||||
for ( NativeSQLQueryReturn queryReturn : queryReturns ) {
|
|
||||||
if ( NativeSQLQueryScalarReturn.class.isInstance( queryReturn ) ) {
|
|
||||||
visitor.visitScalarReturn( (NativeSQLQueryScalarReturn) queryReturn );
|
|
||||||
}
|
|
||||||
else if ( NativeSQLQueryRootReturn.class.isInstance( queryReturn ) ) {
|
|
||||||
visitor.visitRootReturn( (NativeSQLQueryRootReturn) queryReturn );
|
|
||||||
}
|
|
||||||
else if ( NativeSQLQueryCollectionReturn.class.isInstance( queryReturn ) ) {
|
|
||||||
visitor.visitCollectionReturn( (NativeSQLQueryCollectionReturn) queryReturn );
|
|
||||||
}
|
|
||||||
else if ( NativeSQLQueryJoinReturn.class.isInstance( queryReturn ) ) {
|
|
||||||
visitor.visitFetch( (NativeSQLQueryJoinReturn) queryReturn );
|
|
||||||
}
|
|
||||||
else if ( NativeSQLQueryConstructorReturn.class.isInstance( queryReturn ) ) {
|
|
||||||
visitor.visitDynamicInstantiation( (NativeSQLQueryConstructorReturn) queryReturn );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"Unrecognized NativeSQLQueryReturn concrete type : " + queryReturn
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Return> generateCustomReturns(boolean queryHadAliases) {
|
|
||||||
List<Return> customReturns = new ArrayList<Return>();
|
|
||||||
Map<String,Return> customReturnsByAlias = new HashMap<String,Return>();
|
|
||||||
for ( NativeSQLQueryReturn queryReturn : queryReturns ) {
|
|
||||||
if ( queryReturn instanceof NativeSQLQueryScalarReturn ) {
|
|
||||||
NativeSQLQueryScalarReturn rtn = (NativeSQLQueryScalarReturn) queryReturn;
|
|
||||||
customReturns.add( new ScalarReturn( rtn.getType(), rtn.getColumnAlias() ) );
|
|
||||||
}
|
|
||||||
else if ( queryReturn instanceof NativeSQLQueryRootReturn ) {
|
|
||||||
NativeSQLQueryRootReturn rtn = (NativeSQLQueryRootReturn) queryReturn;
|
|
||||||
String alias = rtn.getAlias();
|
|
||||||
EntityAliases entityAliases;
|
|
||||||
if ( queryHadAliases || hasPropertyResultMap( alias ) ) {
|
|
||||||
entityAliases = new DefaultEntityAliases(
|
|
||||||
(Map) entityPropertyResultMaps.get( alias ),
|
|
||||||
(SQLLoadable) alias2Persister.get( alias ),
|
|
||||||
(String) alias2Suffix.get( alias )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
entityAliases = new ColumnEntityAliases(
|
|
||||||
(Map) entityPropertyResultMaps.get( alias ),
|
|
||||||
(SQLLoadable) alias2Persister.get( alias ),
|
|
||||||
(String) alias2Suffix.get( alias )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
RootReturn customReturn = new RootReturn(
|
|
||||||
alias,
|
|
||||||
rtn.getReturnEntityName(),
|
|
||||||
entityAliases,
|
|
||||||
rtn.getLockMode()
|
|
||||||
);
|
|
||||||
customReturns.add( customReturn );
|
|
||||||
customReturnsByAlias.put( rtn.getAlias(), customReturn );
|
|
||||||
}
|
|
||||||
else if ( queryReturn instanceof NativeSQLQueryCollectionReturn ) {
|
|
||||||
NativeSQLQueryCollectionReturn rtn = (NativeSQLQueryCollectionReturn) queryReturn;
|
|
||||||
String alias = rtn.getAlias();
|
|
||||||
SQLLoadableCollection persister = (SQLLoadableCollection) alias2CollectionPersister.get( alias );
|
|
||||||
boolean isEntityElements = persister.getElementType().isEntityType();
|
|
||||||
CollectionAliases collectionAliases;
|
|
||||||
EntityAliases elementEntityAliases = null;
|
|
||||||
if ( queryHadAliases || hasPropertyResultMap( alias ) ) {
|
|
||||||
collectionAliases = new GeneratedCollectionAliases(
|
|
||||||
(Map) collectionPropertyResultMaps.get( alias ),
|
|
||||||
(SQLLoadableCollection) alias2CollectionPersister.get( alias ),
|
|
||||||
(String) alias2CollectionSuffix.get( alias )
|
|
||||||
);
|
|
||||||
if ( isEntityElements ) {
|
|
||||||
elementEntityAliases = new DefaultEntityAliases(
|
|
||||||
(Map) entityPropertyResultMaps.get( alias ),
|
|
||||||
(SQLLoadable) alias2Persister.get( alias ),
|
|
||||||
(String) alias2Suffix.get( alias )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
collectionAliases = new ColumnCollectionAliases(
|
|
||||||
(Map) collectionPropertyResultMaps.get( alias ),
|
|
||||||
(SQLLoadableCollection) alias2CollectionPersister.get( alias )
|
|
||||||
);
|
|
||||||
if ( isEntityElements ) {
|
|
||||||
elementEntityAliases = new ColumnEntityAliases(
|
|
||||||
(Map) entityPropertyResultMaps.get( alias ),
|
|
||||||
(SQLLoadable) alias2Persister.get( alias ),
|
|
||||||
(String) alias2Suffix.get( alias )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CollectionReturn customReturn = new CollectionReturn(
|
|
||||||
alias,
|
|
||||||
rtn.getOwnerEntityName(),
|
|
||||||
rtn.getOwnerProperty(),
|
|
||||||
collectionAliases,
|
|
||||||
elementEntityAliases,
|
|
||||||
rtn.getLockMode()
|
|
||||||
);
|
|
||||||
customReturns.add( customReturn );
|
|
||||||
customReturnsByAlias.put( rtn.getAlias(), customReturn );
|
|
||||||
}
|
|
||||||
else if ( queryReturn instanceof NativeSQLQueryJoinReturn ) {
|
|
||||||
NativeSQLQueryJoinReturn rtn = (NativeSQLQueryJoinReturn) queryReturn;
|
|
||||||
String alias = rtn.getAlias();
|
|
||||||
FetchReturn customReturn;
|
|
||||||
NonScalarReturn ownerCustomReturn = (NonScalarReturn) customReturnsByAlias.get( rtn.getOwnerAlias() );
|
|
||||||
if ( alias2CollectionPersister.containsKey( alias ) ) {
|
|
||||||
SQLLoadableCollection persister = (SQLLoadableCollection) alias2CollectionPersister.get( alias );
|
|
||||||
boolean isEntityElements = persister.getElementType().isEntityType();
|
|
||||||
CollectionAliases collectionAliases;
|
|
||||||
EntityAliases elementEntityAliases = null;
|
|
||||||
if ( queryHadAliases || hasPropertyResultMap( alias ) ) {
|
|
||||||
collectionAliases = new GeneratedCollectionAliases(
|
|
||||||
(Map) collectionPropertyResultMaps.get( alias ),
|
|
||||||
persister,
|
|
||||||
(String) alias2CollectionSuffix.get( alias )
|
|
||||||
);
|
|
||||||
if ( isEntityElements ) {
|
|
||||||
elementEntityAliases = new DefaultEntityAliases(
|
|
||||||
(Map) entityPropertyResultMaps.get( alias ),
|
|
||||||
(SQLLoadable) alias2Persister.get( alias ),
|
|
||||||
(String) alias2Suffix.get( alias )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
collectionAliases = new ColumnCollectionAliases(
|
|
||||||
(Map) collectionPropertyResultMaps.get( alias ),
|
|
||||||
persister
|
|
||||||
);
|
|
||||||
if ( isEntityElements ) {
|
|
||||||
elementEntityAliases = new ColumnEntityAliases(
|
|
||||||
(Map) entityPropertyResultMaps.get( alias ),
|
|
||||||
(SQLLoadable) alias2Persister.get( alias ),
|
|
||||||
(String) alias2Suffix.get( alias )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
customReturn = new CollectionFetchReturn(
|
|
||||||
alias,
|
|
||||||
ownerCustomReturn,
|
|
||||||
rtn.getOwnerProperty(),
|
|
||||||
collectionAliases,
|
|
||||||
elementEntityAliases,
|
|
||||||
rtn.getLockMode()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
EntityAliases entityAliases;
|
|
||||||
if ( queryHadAliases || hasPropertyResultMap( alias ) ) {
|
|
||||||
entityAliases = new DefaultEntityAliases(
|
|
||||||
(Map) entityPropertyResultMaps.get( alias ),
|
|
||||||
(SQLLoadable) alias2Persister.get( alias ),
|
|
||||||
(String) alias2Suffix.get( alias )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
entityAliases = new ColumnEntityAliases(
|
|
||||||
(Map) entityPropertyResultMaps.get( alias ),
|
|
||||||
(SQLLoadable) alias2Persister.get( alias ),
|
|
||||||
(String) alias2Suffix.get( alias )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
customReturn = new EntityFetchReturn(
|
|
||||||
alias,
|
|
||||||
entityAliases,
|
|
||||||
ownerCustomReturn,
|
|
||||||
rtn.getOwnerProperty(),
|
|
||||||
rtn.getLockMode()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
customReturns.add( customReturn );
|
|
||||||
customReturnsByAlias.put( alias, customReturn );
|
|
||||||
}
|
|
||||||
else if ( NativeSQLQueryConstructorReturn.class.isInstance( queryReturn ) ) {
|
|
||||||
final NativeSQLQueryConstructorReturn constructorReturn = (NativeSQLQueryConstructorReturn) queryReturn;
|
|
||||||
final ScalarReturn[] scalars = new ScalarReturn[ constructorReturn.getColumnReturns().length ];
|
|
||||||
int i = 0;
|
|
||||||
for ( NativeSQLQueryScalarReturn scalarReturn : constructorReturn.getColumnReturns() ) {
|
|
||||||
scalars[i++] = new ScalarReturn( scalarReturn.getType(), scalarReturn.getColumnAlias() );
|
|
||||||
}
|
|
||||||
customReturns.add( new ConstructorReturn( constructorReturn.getTargetClass(), scalars ) );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"Unrecognized NativeSQLQueryReturn concrete type : " + queryReturn
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return customReturns;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Return> generateCallableReturns() {
|
|
||||||
final List<Return> customReturns = new ArrayList<>();
|
|
||||||
|
|
||||||
visitReturns(
|
|
||||||
new QueryReturnVisitor() {
|
|
||||||
@Override
|
|
||||||
public void visitScalarReturn(NativeSQLQueryScalarReturn rtn) {
|
|
||||||
customReturns.add( new ScalarReturn( rtn.getType(), rtn.getColumnAlias() ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitRootReturn(NativeSQLQueryRootReturn rtn) {
|
|
||||||
customReturns.add(
|
|
||||||
new RootReturn(
|
|
||||||
rtn.getAlias(),
|
|
||||||
rtn.getReturnEntityName(),
|
|
||||||
new ColumnEntityAliases(
|
|
||||||
(Map) entityPropertyResultMaps.get( rtn.getAlias() ),
|
|
||||||
(SQLLoadable) alias2Persister.get( rtn.getAlias() ),
|
|
||||||
(String) alias2Suffix.get( rtn.getAlias() )
|
|
||||||
),
|
|
||||||
rtn.getLockMode()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitCollectionReturn(NativeSQLQueryCollectionReturn rtn) {
|
|
||||||
throw new UnsupportedOperationException( "Collection returns not supported for stored procedure mapping" );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitFetch(NativeSQLQueryJoinReturn rtn) {
|
|
||||||
throw new UnsupportedOperationException( "Collection returns not supported for stored procedure mapping" );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitDynamicInstantiation(NativeSQLQueryConstructorReturn rtn) {
|
|
||||||
final ScalarReturn[] scalars = new ScalarReturn[ rtn.getColumnReturns().length ];
|
|
||||||
int i = 0;
|
|
||||||
for ( NativeSQLQueryScalarReturn scalarReturn : rtn.getColumnReturns() ) {
|
|
||||||
scalars[i++] = new ScalarReturn( scalarReturn.getType(), scalarReturn.getColumnAlias() );
|
|
||||||
}
|
|
||||||
|
|
||||||
customReturns.add( new ConstructorReturn( rtn.getTargetClass(), scalars ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return customReturns;
|
|
||||||
}
|
|
||||||
|
|
||||||
private SQLLoadable getSQLLoadable(String entityName) throws MappingException {
|
|
||||||
EntityPersister persister = factory.getEntityPersister( entityName );
|
|
||||||
if ( !(persister instanceof SQLLoadable) ) {
|
|
||||||
throw new MappingException( "class persister is not SQLLoadable: " + entityName );
|
|
||||||
}
|
|
||||||
return (SQLLoadable) persister;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String generateEntitySuffix() {
|
|
||||||
return BasicLoader.generateSuffixes( entitySuffixSeed++, 1 )[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
private String generateCollectionSuffix() {
|
|
||||||
return collectionSuffixSeed++ + "__";
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processReturn(NativeSQLQueryReturn rtn) {
|
|
||||||
if ( rtn instanceof NativeSQLQueryScalarReturn ) {
|
|
||||||
processScalarReturn( ( NativeSQLQueryScalarReturn ) rtn );
|
|
||||||
}
|
|
||||||
else if ( rtn instanceof NativeSQLQueryRootReturn ) {
|
|
||||||
processRootReturn( ( NativeSQLQueryRootReturn ) rtn );
|
|
||||||
}
|
|
||||||
else if ( rtn instanceof NativeSQLQueryCollectionReturn ) {
|
|
||||||
processCollectionReturn( (NativeSQLQueryCollectionReturn) rtn );
|
|
||||||
}
|
|
||||||
else if ( NativeSQLQueryJoinReturn.class.isInstance( rtn ) ) {
|
|
||||||
processJoinReturn( ( NativeSQLQueryJoinReturn ) rtn );
|
|
||||||
}
|
|
||||||
else if ( NativeSQLQueryConstructorReturn.class.isInstance( rtn ) ) {
|
|
||||||
processConstructorReturn( (NativeSQLQueryConstructorReturn) rtn );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"Unrecognized NativeSQLQueryReturn concrete type encountered : " + rtn
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processConstructorReturn(NativeSQLQueryConstructorReturn rtn) {
|
|
||||||
//To change body of created methods use File | Settings | File Templates.
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processScalarReturn(NativeSQLQueryScalarReturn typeReturn) {
|
|
||||||
// scalarColumnAliases.add( typeReturn.getColumnAlias() );
|
|
||||||
// scalarTypes.add( typeReturn.getType() );
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processRootReturn(NativeSQLQueryRootReturn rootReturn) {
|
|
||||||
if ( alias2Persister.containsKey( rootReturn.getAlias() ) ) {
|
|
||||||
// already been processed...
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SQLLoadable persister = getSQLLoadable( rootReturn.getReturnEntityName() );
|
|
||||||
addPersister( rootReturn.getAlias(), rootReturn.getPropertyResultsMap(), persister );
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addPersister(String alias, Map propertyResult, SQLLoadable persister) {
|
|
||||||
alias2Persister.put( alias, persister );
|
|
||||||
String suffix = generateEntitySuffix();
|
|
||||||
LOG.tracev( "Mapping alias [{0}] to entity-suffix [{1}]", alias, suffix );
|
|
||||||
alias2Suffix.put( alias, suffix );
|
|
||||||
entityPropertyResultMaps.put( alias, propertyResult );
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addCollection(String role, String alias, Map propertyResults) {
|
|
||||||
SQLLoadableCollection collectionPersister = ( SQLLoadableCollection ) factory.getCollectionPersister( role );
|
|
||||||
alias2CollectionPersister.put( alias, collectionPersister );
|
|
||||||
String suffix = generateCollectionSuffix();
|
|
||||||
LOG.tracev( "Mapping alias [{0}] to collection-suffix [{1}]", alias, suffix );
|
|
||||||
alias2CollectionSuffix.put( alias, suffix );
|
|
||||||
collectionPropertyResultMaps.put( alias, propertyResults );
|
|
||||||
|
|
||||||
if ( collectionPersister.isOneToMany() || collectionPersister.isManyToMany() ) {
|
|
||||||
SQLLoadable persister = ( SQLLoadable ) collectionPersister.getElementPersister();
|
|
||||||
addPersister( alias, filter( propertyResults ), persister );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map filter(Map propertyResults) {
|
|
||||||
Map result = new HashMap( propertyResults.size() );
|
|
||||||
|
|
||||||
String keyPrefix = "element.";
|
|
||||||
|
|
||||||
Iterator iter = propertyResults.entrySet().iterator();
|
|
||||||
while ( iter.hasNext() ) {
|
|
||||||
Map.Entry element = ( Map.Entry ) iter.next();
|
|
||||||
String path = ( String ) element.getKey();
|
|
||||||
if ( path.startsWith( keyPrefix ) ) {
|
|
||||||
result.put( path.substring( keyPrefix.length() ), element.getValue() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processCollectionReturn(NativeSQLQueryCollectionReturn collectionReturn) {
|
|
||||||
// we are initializing an owned collection
|
|
||||||
//collectionOwners.add( new Integer(-1) );
|
|
||||||
// collectionOwnerAliases.add( null );
|
|
||||||
String role = collectionReturn.getOwnerEntityName() + '.' + collectionReturn.getOwnerProperty();
|
|
||||||
addCollection(
|
|
||||||
role,
|
|
||||||
collectionReturn.getAlias(),
|
|
||||||
collectionReturn.getPropertyResultsMap()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processJoinReturn(NativeSQLQueryJoinReturn fetchReturn) {
|
|
||||||
String alias = fetchReturn.getAlias();
|
|
||||||
// if ( alias2Persister.containsKey( alias ) || collectionAliases.contains( alias ) ) {
|
|
||||||
if ( alias2Persister.containsKey( alias ) || alias2CollectionPersister.containsKey( alias ) ) {
|
|
||||||
// already been processed...
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String ownerAlias = fetchReturn.getOwnerAlias();
|
|
||||||
|
|
||||||
// Make sure the owner alias is known...
|
|
||||||
if ( !alias2Return.containsKey( ownerAlias ) ) {
|
|
||||||
throw new HibernateException( "Owner alias [" + ownerAlias + "] is unknown for alias [" + alias + "]" );
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this return's alias has not been processed yet, do so b4 further processing of this return
|
|
||||||
if ( !alias2Persister.containsKey( ownerAlias ) ) {
|
|
||||||
NativeSQLQueryNonScalarReturn ownerReturn = ( NativeSQLQueryNonScalarReturn ) alias2Return.get(ownerAlias);
|
|
||||||
processReturn( ownerReturn );
|
|
||||||
}
|
|
||||||
|
|
||||||
SQLLoadable ownerPersister = ( SQLLoadable ) alias2Persister.get( ownerAlias );
|
|
||||||
Type returnType = ownerPersister.getPropertyType( fetchReturn.getOwnerProperty() );
|
|
||||||
|
|
||||||
if ( returnType.isCollectionType() ) {
|
|
||||||
String role = ownerPersister.getEntityName() + '.' + fetchReturn.getOwnerProperty();
|
|
||||||
addCollection( role, alias, fetchReturn.getPropertyResultsMap() );
|
|
||||||
// collectionOwnerAliases.add( ownerAlias );
|
|
||||||
}
|
|
||||||
else if ( returnType.isEntityType() ) {
|
|
||||||
EntityType eType = ( EntityType ) returnType;
|
|
||||||
String returnEntityName = eType.getAssociatedEntityName();
|
|
||||||
SQLLoadable persister = getSQLLoadable( returnEntityName );
|
|
||||||
addPersister( alias, fetchReturn.getPropertyResultsMap(), persister );
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// public List getCollectionAliases() {
|
|
||||||
// return collectionAliases;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /*public List getCollectionOwners() {
|
|
||||||
// return collectionOwners;
|
|
||||||
// }*/
|
|
||||||
//
|
|
||||||
// public List getCollectionOwnerAliases() {
|
|
||||||
// return collectionOwnerAliases;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public List getCollectionPersisters() {
|
|
||||||
// return collectionPersisters;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public Map getAlias2Persister() {
|
|
||||||
// return alias2Persister;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /*public boolean isCollectionInitializer() {
|
|
||||||
// return isCollectionInitializer;
|
|
||||||
// }*/
|
|
||||||
//
|
|
||||||
//// public List getPersisters() {
|
|
||||||
//// return persisters;
|
|
||||||
//// }
|
|
||||||
//
|
|
||||||
// public Map getAlias2OwnerAlias() {
|
|
||||||
// return alias2OwnerAlias;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public List getScalarTypes() {
|
|
||||||
// return scalarTypes;
|
|
||||||
// }
|
|
||||||
// public List getScalarColumnAliases() {
|
|
||||||
// return scalarColumnAliases;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public List getPropertyResults() {
|
|
||||||
// return propertyResults;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public List getCollectionPropertyResults() {
|
|
||||||
// return collectionResults;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// public Map getAlias2Return() {
|
|
||||||
// return alias2Return;
|
|
||||||
// }
|
|
||||||
}
|
|
|
@ -1,108 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.entity;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
|
||||||
import org.hibernate.LockOptions;
|
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|
||||||
import org.hibernate.loader.OuterJoinLoader;
|
|
||||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
|
||||||
import org.hibernate.transform.ResultTransformer;
|
|
||||||
import org.hibernate.type.Type;
|
|
||||||
|
|
||||||
public abstract class AbstractEntityLoader
|
|
||||||
extends OuterJoinLoader
|
|
||||||
implements UniqueEntityLoader {
|
|
||||||
protected final OuterJoinLoadable persister;
|
|
||||||
protected final Type uniqueKeyType;
|
|
||||||
protected final String entityName;
|
|
||||||
|
|
||||||
public AbstractEntityLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
Type uniqueKeyType,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) {
|
|
||||||
super( factory, loadQueryInfluencers );
|
|
||||||
this.uniqueKeyType = uniqueKeyType;
|
|
||||||
this.entityName = persister.getEntityName();
|
|
||||||
this.persister = persister;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object load(Serializable id, Object optionalObject, SharedSessionContractImplementor session) {
|
|
||||||
// this form is deprecated!
|
|
||||||
return load( id, optionalObject, session, LockOptions.NONE );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object load(Serializable id, Object optionalObject, SharedSessionContractImplementor session, LockOptions lockOptions) {
|
|
||||||
return load( session, id, optionalObject, id, lockOptions );
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Object load(
|
|
||||||
SharedSessionContractImplementor session,
|
|
||||||
Object id,
|
|
||||||
Object optionalObject,
|
|
||||||
Serializable optionalId,
|
|
||||||
LockOptions lockOptions) {
|
|
||||||
|
|
||||||
List list = loadEntity(
|
|
||||||
session,
|
|
||||||
id,
|
|
||||||
uniqueKeyType,
|
|
||||||
optionalObject,
|
|
||||||
entityName,
|
|
||||||
optionalId,
|
|
||||||
persister,
|
|
||||||
lockOptions
|
|
||||||
);
|
|
||||||
|
|
||||||
if ( list.size()==1 ) {
|
|
||||||
return list.get(0);
|
|
||||||
}
|
|
||||||
else if ( list.size()==0 ) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if ( getCollectionOwners()!=null ) {
|
|
||||||
return list.get(0);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new HibernateException(
|
|
||||||
"More than one row with the given identifier was found: " +
|
|
||||||
id +
|
|
||||||
", for class: " +
|
|
||||||
persister.getEntityName()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Object getResultColumnOrRow(
|
|
||||||
Object[] row,
|
|
||||||
ResultTransformer transformer,
|
|
||||||
ResultSet rs,
|
|
||||||
SharedSessionContractImplementor session) {
|
|
||||||
return row[ row.length - 1 ];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isSingleRowLoader() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,121 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.entity;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.hibernate.LockOptions;
|
|
||||||
import org.hibernate.engine.internal.BatchFetchQueueHelper;
|
|
||||||
import org.hibernate.engine.spi.QueryParameters;
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|
||||||
import org.hibernate.loader.Loader;
|
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
|
||||||
import org.hibernate.pretty.MessageHelper;
|
|
||||||
import org.hibernate.type.Type;
|
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The base contract for loaders capable of performing batch-fetch loading of entities using multiple primary key
|
|
||||||
* values in the SQL <tt>WHERE</tt> clause.
|
|
||||||
*
|
|
||||||
* @author Gavin King
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*
|
|
||||||
* @see BatchingEntityLoaderBuilder
|
|
||||||
* @see UniqueEntityLoader
|
|
||||||
*/
|
|
||||||
public abstract class BatchingEntityLoader implements UniqueEntityLoader {
|
|
||||||
private static final Logger log = Logger.getLogger( BatchingEntityLoader.class );
|
|
||||||
|
|
||||||
private final EntityPersister persister;
|
|
||||||
|
|
||||||
public BatchingEntityLoader(EntityPersister persister) {
|
|
||||||
this.persister = persister;
|
|
||||||
}
|
|
||||||
|
|
||||||
public EntityPersister persister() {
|
|
||||||
return persister;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object load(Serializable id, Object optionalObject, SharedSessionContractImplementor session) {
|
|
||||||
return load( id, optionalObject, session, LockOptions.NONE );
|
|
||||||
}
|
|
||||||
|
|
||||||
protected QueryParameters buildQueryParameters(
|
|
||||||
Serializable id,
|
|
||||||
Serializable[] ids,
|
|
||||||
Object optionalObject,
|
|
||||||
LockOptions lockOptions) {
|
|
||||||
Type[] types = new Type[ids.length];
|
|
||||||
Arrays.fill( types, persister().getIdentifierType() );
|
|
||||||
|
|
||||||
QueryParameters qp = new QueryParameters();
|
|
||||||
qp.setPositionalParameterTypes( types );
|
|
||||||
qp.setPositionalParameterValues( ids );
|
|
||||||
qp.setOptionalObject( optionalObject );
|
|
||||||
qp.setOptionalEntityName( persister().getEntityName() );
|
|
||||||
qp.setOptionalId( id );
|
|
||||||
qp.setLockOptions( lockOptions );
|
|
||||||
return qp;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Object getObjectFromList(List results, Serializable id, SharedSessionContractImplementor session) {
|
|
||||||
for ( Object obj : results ) {
|
|
||||||
final boolean equal = persister.getIdentifierType().isEqual(
|
|
||||||
id,
|
|
||||||
session.getContextEntityIdentifier( obj ),
|
|
||||||
session.getFactory()
|
|
||||||
);
|
|
||||||
if ( equal ) {
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Object doBatchLoad(
|
|
||||||
Serializable id,
|
|
||||||
Loader loaderToUse,
|
|
||||||
SharedSessionContractImplementor session,
|
|
||||||
Serializable[] ids,
|
|
||||||
Object optionalObject,
|
|
||||||
LockOptions lockOptions) {
|
|
||||||
if ( log.isDebugEnabled() ) {
|
|
||||||
log.debugf( "Batch loading entity: %s", MessageHelper.infoString( persister, ids, session.getFactory() ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
QueryParameters qp = buildQueryParameters( id, ids, optionalObject, lockOptions );
|
|
||||||
|
|
||||||
try {
|
|
||||||
final List results = loaderToUse.doQueryAndInitializeNonLazyCollections( session, qp, false );
|
|
||||||
log.debug( "Done entity batch load" );
|
|
||||||
// The EntityKey for any entity that is not found will remain in the batch.
|
|
||||||
// Explicitly remove the EntityKeys for entities that were not found to
|
|
||||||
// avoid including them in future batches that get executed.
|
|
||||||
BatchFetchQueueHelper.removeNotFoundBatchLoadableEntityKeys(
|
|
||||||
ids,
|
|
||||||
results,
|
|
||||||
persister(),
|
|
||||||
session
|
|
||||||
);
|
|
||||||
return getObjectFromList(results, id, session);
|
|
||||||
}
|
|
||||||
catch ( SQLException sqle ) {
|
|
||||||
throw session.getJdbcServices().getSqlExceptionHelper().convert(
|
|
||||||
sqle,
|
|
||||||
"could not load an entity batch: " + MessageHelper.infoString( persister(), ids, session.getFactory() ),
|
|
||||||
loaderToUse.getSQLString()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,117 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.entity;
|
|
||||||
|
|
||||||
import org.hibernate.LockMode;
|
|
||||||
import org.hibernate.LockOptions;
|
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The contract for building {@link UniqueEntityLoader} capable of performing batch-fetch loading. Intention
|
|
||||||
* is to build these instances, by first calling the static {@link #getBuilder}, and then calling the appropriate
|
|
||||||
* {@link #buildLoader} method.
|
|
||||||
*
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*
|
|
||||||
* @see org.hibernate.loader.BatchFetchStyle
|
|
||||||
*/
|
|
||||||
public abstract class BatchingEntityLoaderBuilder {
|
|
||||||
public static BatchingEntityLoaderBuilder getBuilder(SessionFactoryImplementor factory) {
|
|
||||||
switch ( factory.getSettings().getBatchFetchStyle() ) {
|
|
||||||
case PADDED: {
|
|
||||||
return PaddedBatchingEntityLoaderBuilder.INSTANCE;
|
|
||||||
}
|
|
||||||
case DYNAMIC: {
|
|
||||||
return DynamicBatchingEntityLoaderBuilder.INSTANCE;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return org.hibernate.loader.entity.plan.LegacyBatchingEntityLoaderBuilder.INSTANCE;
|
|
||||||
// return LegacyBatchingEntityLoaderBuilder.INSTANCE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds a batch-fetch capable loader based on the given persister, lock-mode, etc.
|
|
||||||
*
|
|
||||||
* @param persister The entity persister
|
|
||||||
* @param batchSize The maximum number of ids to batch-fetch at once
|
|
||||||
* @param lockMode The lock mode
|
|
||||||
* @param factory The SessionFactory
|
|
||||||
* @param influencers Any influencers that should affect the built query
|
|
||||||
*
|
|
||||||
* @return The loader.
|
|
||||||
*/
|
|
||||||
public UniqueEntityLoader buildLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
int batchSize,
|
|
||||||
LockMode lockMode,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers influencers) {
|
|
||||||
if ( batchSize <= 1 ) {
|
|
||||||
// no batching
|
|
||||||
return buildNonBatchingLoader( persister, lockMode, factory, influencers );
|
|
||||||
}
|
|
||||||
return buildBatchingLoader( persister, batchSize, lockMode, factory, influencers );
|
|
||||||
}
|
|
||||||
|
|
||||||
protected UniqueEntityLoader buildNonBatchingLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
LockMode lockMode,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers influencers) {
|
|
||||||
return new EntityLoader( persister, lockMode, factory, influencers );
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract UniqueEntityLoader buildBatchingLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
int batchSize,
|
|
||||||
LockMode lockMode,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers influencers);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds a batch-fetch capable loader based on the given persister, lock-options, etc.
|
|
||||||
*
|
|
||||||
* @param persister The entity persister
|
|
||||||
* @param batchSize The maximum number of ids to batch-fetch at once
|
|
||||||
* @param lockOptions The lock options
|
|
||||||
* @param factory The SessionFactory
|
|
||||||
* @param influencers Any influencers that should affect the built query
|
|
||||||
*
|
|
||||||
* @return The loader.
|
|
||||||
*/
|
|
||||||
public UniqueEntityLoader buildLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
int batchSize,
|
|
||||||
LockOptions lockOptions,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers influencers) {
|
|
||||||
if ( batchSize <= 1 ) {
|
|
||||||
// no batching
|
|
||||||
return buildNonBatchingLoader( persister, lockOptions, factory, influencers );
|
|
||||||
}
|
|
||||||
return buildBatchingLoader( persister, batchSize, lockOptions, factory, influencers );
|
|
||||||
}
|
|
||||||
|
|
||||||
protected UniqueEntityLoader buildNonBatchingLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
LockOptions lockOptions,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers influencers) {
|
|
||||||
return new EntityLoader( persister, lockOptions, factory, influencers );
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract UniqueEntityLoader buildBatchingLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
int batchSize,
|
|
||||||
LockOptions lockOptions,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers influencers);
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.entity;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
import org.hibernate.FetchMode;
|
|
||||||
import org.hibernate.LockOptions;
|
|
||||||
import org.hibernate.MappingException;
|
|
||||||
import org.hibernate.engine.spi.CascadeStyle;
|
|
||||||
import org.hibernate.engine.spi.CascadingAction;
|
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.loader.AbstractEntityJoinWalker;
|
|
||||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
|
||||||
import org.hibernate.type.AssociationType;
|
|
||||||
|
|
||||||
public class CascadeEntityJoinWalker extends AbstractEntityJoinWalker {
|
|
||||||
|
|
||||||
private final CascadingAction cascadeAction;
|
|
||||||
|
|
||||||
public CascadeEntityJoinWalker(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
CascadingAction action,
|
|
||||||
SessionFactoryImplementor factory)
|
|
||||||
throws MappingException {
|
|
||||||
super( persister, factory, LoadQueryInfluencers.NONE );
|
|
||||||
this.cascadeAction = action;
|
|
||||||
StringBuilder whereCondition = whereString( getAlias(), persister.getIdentifierColumnNames(), 1 )
|
|
||||||
//include the discriminator and class-level where, but not filters
|
|
||||||
.append( persister.filterFragment( getAlias(), Collections.EMPTY_MAP ) );
|
|
||||||
|
|
||||||
initAll( whereCondition.toString(), "", LockOptions.READ );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isJoinedFetchEnabled(AssociationType type, FetchMode config, CascadeStyle cascadeStyle) {
|
|
||||||
return ( type.isEntityType() || type.isCollectionType() ) &&
|
|
||||||
( cascadeStyle == null || cascadeStyle.doCascade( cascadeAction ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isTooManyCollections() {
|
|
||||||
return countCollectionPersisters( associations ) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getComment() {
|
|
||||||
return "load " + getPersister().getEntityName();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.entity;
|
|
||||||
import org.hibernate.MappingException;
|
|
||||||
import org.hibernate.engine.spi.CascadingAction;
|
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.loader.JoinWalker;
|
|
||||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
|
||||||
|
|
||||||
public class CascadeEntityLoader extends AbstractEntityLoader {
|
|
||||||
|
|
||||||
public CascadeEntityLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
CascadingAction action,
|
|
||||||
SessionFactoryImplementor factory) throws MappingException {
|
|
||||||
super(
|
|
||||||
persister,
|
|
||||||
persister.getIdentifierType(),
|
|
||||||
factory,
|
|
||||||
LoadQueryInfluencers.NONE
|
|
||||||
);
|
|
||||||
|
|
||||||
JoinWalker walker = new CascadeEntityJoinWalker(
|
|
||||||
persister,
|
|
||||||
action,
|
|
||||||
factory
|
|
||||||
);
|
|
||||||
initFromWalker( walker );
|
|
||||||
|
|
||||||
postInstantiate();
|
|
||||||
|
|
||||||
if ( LOG.isDebugEnabled() ) {
|
|
||||||
LOG.debugf( "Static select for action %s on entity %s: %s", action, entityName, getSQLString() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,119 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.entity;
|
|
||||||
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
|
||||||
import org.hibernate.LockMode;
|
|
||||||
import org.hibernate.MappingException;
|
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|
||||||
import org.hibernate.internal.CoreMessageLogger;
|
|
||||||
import org.hibernate.internal.util.collections.ArrayHelper;
|
|
||||||
import org.hibernate.loader.JoinWalker;
|
|
||||||
import org.hibernate.loader.OuterJoinLoader;
|
|
||||||
import org.hibernate.persister.collection.QueryableCollection;
|
|
||||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
|
||||||
import org.hibernate.transform.ResultTransformer;
|
|
||||||
import org.hibernate.type.Type;
|
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Gavin King
|
|
||||||
*/
|
|
||||||
public class CollectionElementLoader extends OuterJoinLoader {
|
|
||||||
|
|
||||||
private static final CoreMessageLogger LOG = Logger.getMessageLogger(
|
|
||||||
CoreMessageLogger.class,
|
|
||||||
CollectionElementLoader.class.getName()
|
|
||||||
);
|
|
||||||
|
|
||||||
private final OuterJoinLoadable persister;
|
|
||||||
private final Type keyType;
|
|
||||||
private final Type indexType;
|
|
||||||
private final String entityName;
|
|
||||||
|
|
||||||
public CollectionElementLoader(
|
|
||||||
QueryableCollection collectionPersister,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
|
||||||
super( factory, loadQueryInfluencers );
|
|
||||||
|
|
||||||
this.keyType = collectionPersister.getKeyType();
|
|
||||||
this.indexType = collectionPersister.getIndexType();
|
|
||||||
this.persister = (OuterJoinLoadable) collectionPersister.getElementPersister();
|
|
||||||
this.entityName = persister.getEntityName();
|
|
||||||
|
|
||||||
JoinWalker walker = new EntityJoinWalker(
|
|
||||||
persister,
|
|
||||||
ArrayHelper.join(
|
|
||||||
collectionPersister.getKeyColumnNames(),
|
|
||||||
collectionPersister.toColumns( "index" )
|
|
||||||
),
|
|
||||||
1,
|
|
||||||
LockMode.NONE,
|
|
||||||
factory,
|
|
||||||
loadQueryInfluencers
|
|
||||||
);
|
|
||||||
initFromWalker( walker );
|
|
||||||
|
|
||||||
postInstantiate();
|
|
||||||
|
|
||||||
if ( LOG.isDebugEnabled() ) {
|
|
||||||
LOG.debugf( "Static select for entity %s: %s", entityName, getSQLString() );
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object loadElement(SharedSessionContractImplementor session, Object key, Object index)
|
|
||||||
throws HibernateException {
|
|
||||||
|
|
||||||
List list = loadEntity(
|
|
||||||
session,
|
|
||||||
key,
|
|
||||||
index,
|
|
||||||
keyType,
|
|
||||||
indexType,
|
|
||||||
persister
|
|
||||||
);
|
|
||||||
|
|
||||||
if ( list.size() == 1 ) {
|
|
||||||
return list.get( 0 );
|
|
||||||
}
|
|
||||||
else if ( list.size() == 0 ) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if ( getCollectionOwners() != null ) {
|
|
||||||
return list.get( 0 );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new HibernateException( "More than one row was found" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Object getResultColumnOrRow(
|
|
||||||
Object[] row,
|
|
||||||
ResultTransformer transformer,
|
|
||||||
ResultSet rs,
|
|
||||||
SharedSessionContractImplementor session) {
|
|
||||||
return row[row.length - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isSingleRowLoader() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,580 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.entity;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.lang.reflect.Array;
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.sql.Statement;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.hibernate.LockMode;
|
|
||||||
import org.hibernate.LockOptions;
|
|
||||||
import org.hibernate.dialect.Dialect;
|
|
||||||
import org.hibernate.dialect.pagination.LimitHelper;
|
|
||||||
import org.hibernate.engine.internal.BatchFetchQueueHelper;
|
|
||||||
import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
|
|
||||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
|
||||||
import org.hibernate.engine.spi.EntityEntry;
|
|
||||||
import org.hibernate.engine.spi.EntityKey;
|
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|
||||||
import org.hibernate.engine.spi.PersistenceContext;
|
|
||||||
import org.hibernate.engine.spi.QueryParameters;
|
|
||||||
import org.hibernate.engine.spi.RowSelection;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|
||||||
import org.hibernate.engine.spi.Status;
|
|
||||||
import org.hibernate.event.spi.EventSource;
|
|
||||||
import org.hibernate.event.spi.LoadEvent;
|
|
||||||
import org.hibernate.event.spi.LoadEventListener;
|
|
||||||
import org.hibernate.internal.util.StringHelper;
|
|
||||||
import org.hibernate.internal.util.collections.ArrayHelper;
|
|
||||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
|
||||||
import org.hibernate.loader.ast.spi.AfterLoadAction;
|
|
||||||
import org.hibernate.persister.entity.MultiLoadOptions;
|
|
||||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
|
||||||
import org.hibernate.pretty.MessageHelper;
|
|
||||||
import org.hibernate.type.Type;
|
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A BatchingEntityLoaderBuilder that builds UniqueEntityLoader instances capable of dynamically building
|
|
||||||
* its batch-fetch SQL based on the actual number of entity ids waiting to be fetched.
|
|
||||||
*
|
|
||||||
* @see Dialect#getDefaultBatchLoadSizingStrategy
|
|
||||||
*
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public class DynamicBatchingEntityLoaderBuilder extends BatchingEntityLoaderBuilder {
|
|
||||||
private static final Logger log = Logger.getLogger( DynamicBatchingEntityLoaderBuilder.class );
|
|
||||||
|
|
||||||
public static final DynamicBatchingEntityLoaderBuilder INSTANCE = new DynamicBatchingEntityLoaderBuilder();
|
|
||||||
|
|
||||||
public List multiLoad(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
Serializable[] ids,
|
|
||||||
SharedSessionContractImplementor session,
|
|
||||||
MultiLoadOptions loadOptions) {
|
|
||||||
if ( loadOptions.isOrderReturnEnabled() ) {
|
|
||||||
return performOrderedMultiLoad( persister, ids, session, loadOptions );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return performUnorderedMultiLoad( persister, ids, session, loadOptions );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private List performOrderedMultiLoad(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
Serializable[] ids,
|
|
||||||
SharedSessionContractImplementor session,
|
|
||||||
MultiLoadOptions loadOptions) {
|
|
||||||
assert loadOptions.isOrderReturnEnabled();
|
|
||||||
|
|
||||||
final List result = CollectionHelper.arrayList( ids.length );
|
|
||||||
|
|
||||||
final LockOptions lockOptions = (loadOptions.getLockOptions() == null)
|
|
||||||
? new LockOptions( LockMode.NONE )
|
|
||||||
: loadOptions.getLockOptions();
|
|
||||||
|
|
||||||
Integer batchSize = loadOptions.getBatchSize();
|
|
||||||
if ( batchSize == null || batchSize <= 0 ) {
|
|
||||||
batchSize = session.getJdbcServices().getJdbcEnvironment().getDialect().getDefaultBatchLoadSizingStrategy().determineOptimalBatchLoadSize(
|
|
||||||
persister.getIdentifierType().getColumnSpan( session.getFactory() ),
|
|
||||||
ids.length
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final List<Object> idsInBatch = new ArrayList<>();
|
|
||||||
final List<Integer> elementPositionsLoadedByBatch = new ArrayList<>();
|
|
||||||
|
|
||||||
for ( int i = 0; i < ids.length; i++ ) {
|
|
||||||
final Serializable id = ids[i];
|
|
||||||
final EntityKey entityKey = new EntityKey( id, persister );
|
|
||||||
|
|
||||||
if ( loadOptions.isSessionCheckingEnabled() || loadOptions.isSecondLevelCacheCheckingEnabled() ) {
|
|
||||||
LoadEvent loadEvent = new LoadEvent(
|
|
||||||
id,
|
|
||||||
persister.getMappedClass().getName(),
|
|
||||||
lockOptions,
|
|
||||||
(EventSource) session
|
|
||||||
);
|
|
||||||
|
|
||||||
Object managedEntity = null;
|
|
||||||
|
|
||||||
if ( loadOptions.isSessionCheckingEnabled() ) {
|
|
||||||
// look for it in the Session first
|
|
||||||
CacheEntityLoaderHelper.PersistenceContextEntry persistenceContextEntry = CacheEntityLoaderHelper.INSTANCE
|
|
||||||
.loadFromSessionCache(
|
|
||||||
loadEvent,
|
|
||||||
entityKey,
|
|
||||||
LoadEventListener.GET
|
|
||||||
);
|
|
||||||
managedEntity = persistenceContextEntry.getEntity();
|
|
||||||
|
|
||||||
if ( managedEntity != null && !loadOptions.isReturnOfDeletedEntitiesEnabled() && !persistenceContextEntry
|
|
||||||
.isManaged() ) {
|
|
||||||
// put a null in the result
|
|
||||||
result.add( i, null );
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( managedEntity == null && loadOptions.isSecondLevelCacheCheckingEnabled() ) {
|
|
||||||
// look for it in the SessionFactory
|
|
||||||
managedEntity = CacheEntityLoaderHelper.INSTANCE.loadFromSecondLevelCache(
|
|
||||||
loadEvent,
|
|
||||||
persister,
|
|
||||||
entityKey
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( managedEntity != null ) {
|
|
||||||
result.add( i, managedEntity );
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we did not hit any of the continues above, then we need to batch
|
|
||||||
// load the entity state.
|
|
||||||
idsInBatch.add( ids[i] );
|
|
||||||
|
|
||||||
if ( idsInBatch.size() >= batchSize ) {
|
|
||||||
// we've hit the allotted max-batch-size, perform an "intermediate load"
|
|
||||||
performOrderedBatchLoad( idsInBatch, lockOptions, persister, session );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the EntityKey instance for use later!
|
|
||||||
result.add( i, entityKey );
|
|
||||||
elementPositionsLoadedByBatch.add( i );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !idsInBatch.isEmpty() ) {
|
|
||||||
// we still have ids to load from the processing above since the last max-batch-size trigger,
|
|
||||||
// perform a load for them
|
|
||||||
performOrderedBatchLoad( idsInBatch, lockOptions, persister, session );
|
|
||||||
}
|
|
||||||
|
|
||||||
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
|
|
||||||
for ( Integer position : elementPositionsLoadedByBatch ) {
|
|
||||||
// the element value at this position in the result List should be
|
|
||||||
// the EntityKey for that entity; reuse it!
|
|
||||||
final EntityKey entityKey = (EntityKey) result.get( position );
|
|
||||||
Object entity = persistenceContext.getEntity( entityKey );
|
|
||||||
if ( entity != null && !loadOptions.isReturnOfDeletedEntitiesEnabled() ) {
|
|
||||||
// make sure it is not DELETED
|
|
||||||
final EntityEntry entry = persistenceContext.getEntry( entity );
|
|
||||||
if ( entry.getStatus() == Status.DELETED || entry.getStatus() == Status.GONE ) {
|
|
||||||
// the entity is locally deleted, and the options ask that we not return such entities...
|
|
||||||
entity = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.set( position, entity );
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void performOrderedBatchLoad(
|
|
||||||
List<Object> idsInBatch,
|
|
||||||
LockOptions lockOptions,
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
SharedSessionContractImplementor session) {
|
|
||||||
final int batchSize = idsInBatch.size();
|
|
||||||
final DynamicEntityLoader batchingLoader = new DynamicEntityLoader(
|
|
||||||
persister,
|
|
||||||
batchSize,
|
|
||||||
lockOptions,
|
|
||||||
session.getFactory(),
|
|
||||||
session.getLoadQueryInfluencers()
|
|
||||||
);
|
|
||||||
|
|
||||||
final Object[] idsInBatchArray = idsInBatch.toArray( new Object[ idsInBatch.size() ] );
|
|
||||||
|
|
||||||
QueryParameters qp = buildMultiLoadQueryParameters( persister, idsInBatchArray, lockOptions );
|
|
||||||
batchingLoader.doEntityBatchFetch( session, qp, idsInBatchArray );
|
|
||||||
|
|
||||||
idsInBatch.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
protected List performUnorderedMultiLoad(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
Serializable[] ids,
|
|
||||||
SharedSessionContractImplementor session,
|
|
||||||
MultiLoadOptions loadOptions) {
|
|
||||||
assert !loadOptions.isOrderReturnEnabled();
|
|
||||||
|
|
||||||
final List result = CollectionHelper.arrayList( ids.length );
|
|
||||||
|
|
||||||
final LockOptions lockOptions = (loadOptions.getLockOptions() == null)
|
|
||||||
? new LockOptions( LockMode.NONE )
|
|
||||||
: loadOptions.getLockOptions();
|
|
||||||
|
|
||||||
if ( loadOptions.isSessionCheckingEnabled() || loadOptions.isSecondLevelCacheCheckingEnabled() ) {
|
|
||||||
// the user requested that we exclude ids corresponding to already managed
|
|
||||||
// entities from the generated load SQL. So here we will iterate all
|
|
||||||
// incoming id values and see whether it corresponds to an existing
|
|
||||||
// entity associated with the PC - if it does we add it to the result
|
|
||||||
// list immediately and remove its id from the group of ids to load.
|
|
||||||
boolean foundAnyManagedEntities = false;
|
|
||||||
final List<Serializable> nonManagedIds = new ArrayList<Serializable>();
|
|
||||||
for ( Serializable id : ids ) {
|
|
||||||
final EntityKey entityKey = new EntityKey( id, persister );
|
|
||||||
|
|
||||||
LoadEvent loadEvent = new LoadEvent(
|
|
||||||
id,
|
|
||||||
persister.getMappedClass().getName(),
|
|
||||||
lockOptions,
|
|
||||||
(EventSource) session
|
|
||||||
);
|
|
||||||
|
|
||||||
Object managedEntity = null;
|
|
||||||
|
|
||||||
// look for it in the Session first
|
|
||||||
CacheEntityLoaderHelper.PersistenceContextEntry persistenceContextEntry = CacheEntityLoaderHelper.INSTANCE
|
|
||||||
.loadFromSessionCache(
|
|
||||||
loadEvent,
|
|
||||||
entityKey,
|
|
||||||
LoadEventListener.GET
|
|
||||||
);
|
|
||||||
if ( loadOptions.isSessionCheckingEnabled() ) {
|
|
||||||
managedEntity = persistenceContextEntry.getEntity();
|
|
||||||
|
|
||||||
if ( managedEntity != null && !loadOptions.isReturnOfDeletedEntitiesEnabled() && !persistenceContextEntry
|
|
||||||
.isManaged() ) {
|
|
||||||
foundAnyManagedEntities = true;
|
|
||||||
result.add( null );
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( managedEntity == null && loadOptions.isSecondLevelCacheCheckingEnabled() ) {
|
|
||||||
managedEntity = CacheEntityLoaderHelper.INSTANCE.loadFromSecondLevelCache(
|
|
||||||
loadEvent,
|
|
||||||
persister,
|
|
||||||
entityKey
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( managedEntity != null ) {
|
|
||||||
foundAnyManagedEntities = true;
|
|
||||||
result.add( managedEntity );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
nonManagedIds.add( id );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( foundAnyManagedEntities ) {
|
|
||||||
if ( nonManagedIds.isEmpty() ) {
|
|
||||||
// all of the given ids were already associated with the Session
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// over-write the ids to be loaded with the collection of
|
|
||||||
// just non-managed ones
|
|
||||||
ids = nonManagedIds.toArray(
|
|
||||||
(Serializable[]) Array.newInstance(
|
|
||||||
ids.getClass().getComponentType(),
|
|
||||||
nonManagedIds.size()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int numberOfIdsLeft = ids.length;
|
|
||||||
final int maxBatchSize;
|
|
||||||
if ( loadOptions.getBatchSize() != null && loadOptions.getBatchSize() > 0 ) {
|
|
||||||
maxBatchSize = loadOptions.getBatchSize();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
maxBatchSize = session.getJdbcServices().getJdbcEnvironment().getDialect().getDefaultBatchLoadSizingStrategy().determineOptimalBatchLoadSize(
|
|
||||||
persister.getIdentifierType().getColumnSpan( session.getFactory() ),
|
|
||||||
numberOfIdsLeft
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
int idPosition = 0;
|
|
||||||
while ( numberOfIdsLeft > 0 ) {
|
|
||||||
int batchSize = Math.min( numberOfIdsLeft, maxBatchSize );
|
|
||||||
final DynamicEntityLoader batchingLoader = new DynamicEntityLoader(
|
|
||||||
persister,
|
|
||||||
batchSize,
|
|
||||||
lockOptions,
|
|
||||||
session.getFactory(),
|
|
||||||
session.getLoadQueryInfluencers()
|
|
||||||
);
|
|
||||||
|
|
||||||
Serializable[] idsInBatch = new Serializable[batchSize];
|
|
||||||
System.arraycopy( ids, idPosition, idsInBatch, 0, batchSize );
|
|
||||||
|
|
||||||
QueryParameters qp = buildMultiLoadQueryParameters( persister, idsInBatch, lockOptions );
|
|
||||||
result.addAll( batchingLoader.doEntityBatchFetch( session, qp, idsInBatch ) );
|
|
||||||
|
|
||||||
numberOfIdsLeft = numberOfIdsLeft - batchSize;
|
|
||||||
idPosition += batchSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static QueryParameters buildMultiLoadQueryParameters(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
Object[] ids,
|
|
||||||
LockOptions lockOptions) {
|
|
||||||
Type[] types = new Type[ids.length];
|
|
||||||
Arrays.fill( types, persister.getIdentifierType() );
|
|
||||||
|
|
||||||
QueryParameters qp = new QueryParameters();
|
|
||||||
qp.setOptionalEntityName( persister.getEntityName() );
|
|
||||||
qp.setPositionalParameterTypes( types );
|
|
||||||
qp.setPositionalParameterValues( ids );
|
|
||||||
qp.setLockOptions( lockOptions );
|
|
||||||
qp.setOptionalObject( null );
|
|
||||||
qp.setOptionalId( null );
|
|
||||||
return qp;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected UniqueEntityLoader buildBatchingLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
int batchSize,
|
|
||||||
LockMode lockMode,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers influencers) {
|
|
||||||
return new DynamicBatchingEntityLoader( persister, batchSize, lockMode, factory, influencers );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected UniqueEntityLoader buildBatchingLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
int batchSize,
|
|
||||||
LockOptions lockOptions,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers influencers) {
|
|
||||||
return new DynamicBatchingEntityLoader( persister, batchSize, lockOptions, factory, influencers );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class DynamicBatchingEntityLoader extends BatchingEntityLoader {
|
|
||||||
private final int maxBatchSize;
|
|
||||||
private final UniqueEntityLoader singleKeyLoader;
|
|
||||||
private final DynamicEntityLoader dynamicLoader;
|
|
||||||
|
|
||||||
public DynamicBatchingEntityLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
int maxBatchSize,
|
|
||||||
LockMode lockMode,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) {
|
|
||||||
super( persister );
|
|
||||||
this.maxBatchSize = maxBatchSize;
|
|
||||||
this.singleKeyLoader = new EntityLoader( persister, 1, lockMode, factory, loadQueryInfluencers );
|
|
||||||
this.dynamicLoader = new DynamicEntityLoader( persister, maxBatchSize, lockMode, factory, loadQueryInfluencers );
|
|
||||||
}
|
|
||||||
|
|
||||||
public DynamicBatchingEntityLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
int maxBatchSize,
|
|
||||||
LockOptions lockOptions,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) {
|
|
||||||
super( persister );
|
|
||||||
this.maxBatchSize = maxBatchSize;
|
|
||||||
this.singleKeyLoader = new EntityLoader( persister, 1, lockOptions, factory, loadQueryInfluencers );
|
|
||||||
this.dynamicLoader = new DynamicEntityLoader( persister, maxBatchSize, lockOptions, factory, loadQueryInfluencers );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object load(
|
|
||||||
Serializable id,
|
|
||||||
Object optionalObject,
|
|
||||||
SharedSessionContractImplementor session,
|
|
||||||
LockOptions lockOptions) {
|
|
||||||
final Object[] batch = session.getPersistenceContextInternal()
|
|
||||||
.getBatchFetchQueue()
|
|
||||||
.getBatchLoadableEntityIds( persister(), id, maxBatchSize );
|
|
||||||
|
|
||||||
final int numberOfIds = ArrayHelper.countNonNull( batch );
|
|
||||||
if ( numberOfIds <= 1 ) {
|
|
||||||
final Object result = singleKeyLoader.load( id, optionalObject, session );
|
|
||||||
if ( result == null ) {
|
|
||||||
// There was no entity with the specified ID. Make sure the EntityKey does not remain
|
|
||||||
// in the batch to avoid including it in future batches that get executed.
|
|
||||||
BatchFetchQueueHelper.removeBatchLoadableEntityKey( id, persister(), session );
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Serializable[] idsToLoad = new Serializable[numberOfIds];
|
|
||||||
System.arraycopy( batch, 0, idsToLoad, 0, numberOfIds );
|
|
||||||
|
|
||||||
if ( log.isDebugEnabled() ) {
|
|
||||||
log.debugf( "Batch loading entity: %s", MessageHelper.infoString( persister(), idsToLoad, session.getFactory() ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
QueryParameters qp = buildQueryParameters( id, idsToLoad, optionalObject, lockOptions );
|
|
||||||
List results = dynamicLoader.doEntityBatchFetch( session, qp, idsToLoad );
|
|
||||||
|
|
||||||
// The EntityKey for any entity that is not found will remain in the batch.
|
|
||||||
// Explicitly remove the EntityKeys for entities that were not found to
|
|
||||||
// avoid including them in future batches that get executed.
|
|
||||||
BatchFetchQueueHelper.removeNotFoundBatchLoadableEntityKeys( idsToLoad, results, persister(), session );
|
|
||||||
|
|
||||||
return getObjectFromList( results, id, session );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static class DynamicEntityLoader extends EntityLoader {
|
|
||||||
// todo : see the discussion on org.hibernate.loader.collection.DynamicBatchingCollectionInitializerBuilder.DynamicBatchingCollectionLoader
|
|
||||||
|
|
||||||
private final String sqlTemplate;
|
|
||||||
private final String alias;
|
|
||||||
|
|
||||||
public DynamicEntityLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
int maxBatchSize,
|
|
||||||
LockOptions lockOptions,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) {
|
|
||||||
this( persister, maxBatchSize, lockOptions.getLockMode(), factory, loadQueryInfluencers );
|
|
||||||
}
|
|
||||||
|
|
||||||
public DynamicEntityLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
int maxBatchSize,
|
|
||||||
LockMode lockMode,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) {
|
|
||||||
super( persister, -1, lockMode, factory, loadQueryInfluencers );
|
|
||||||
|
|
||||||
EntityJoinWalker walker = new EntityJoinWalker(
|
|
||||||
persister,
|
|
||||||
persister.getIdentifierColumnNames(),
|
|
||||||
-1,
|
|
||||||
lockMode,
|
|
||||||
factory,
|
|
||||||
loadQueryInfluencers) {
|
|
||||||
@Override
|
|
||||||
protected StringBuilder whereString(String alias, String[] columnNames, int batchSize) {
|
|
||||||
return StringHelper.buildBatchFetchRestrictionFragment(
|
|
||||||
alias,
|
|
||||||
columnNames,
|
|
||||||
getFactory().getDialect()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
initFromWalker( walker );
|
|
||||||
this.sqlTemplate = walker.getSQLString();
|
|
||||||
this.alias = walker.getAlias();
|
|
||||||
postInstantiate();
|
|
||||||
|
|
||||||
if ( LOG.isDebugEnabled() ) {
|
|
||||||
LOG.debugf(
|
|
||||||
"SQL-template for dynamic entity [%s] batch-fetching [%s] : %s",
|
|
||||||
entityName,
|
|
||||||
lockMode,
|
|
||||||
sqlTemplate
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isSingleRowLoader() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isSubselectLoadingEnabled() {
|
|
||||||
return persister.hasSubselectLoadableCollections();
|
|
||||||
}
|
|
||||||
|
|
||||||
public List doEntityBatchFetch(
|
|
||||||
SharedSessionContractImplementor session,
|
|
||||||
QueryParameters queryParameters,
|
|
||||||
Object[] ids) {
|
|
||||||
final JdbcServices jdbcServices = session.getJdbcServices();
|
|
||||||
final String sql = StringHelper.expandBatchIdPlaceholder(
|
|
||||||
sqlTemplate,
|
|
||||||
ids,
|
|
||||||
alias,
|
|
||||||
persister.getKeyColumnNames(),
|
|
||||||
jdbcServices.getJdbcEnvironment().getDialect()
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
|
||||||
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
|
|
||||||
boolean defaultReadOnlyOrig = persistenceContext.isDefaultReadOnly();
|
|
||||||
if ( queryParameters.isReadOnlyInitialized() ) {
|
|
||||||
// The read-only/modifiable mode for the query was explicitly set.
|
|
||||||
// Temporarily set the default read-only/modifiable setting to the query's setting.
|
|
||||||
persistenceContext.setDefaultReadOnly( queryParameters.isReadOnly() );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// The read-only/modifiable setting for the query was not initialized.
|
|
||||||
// Use the default read-only/modifiable from the persistence context instead.
|
|
||||||
queryParameters.setReadOnly( persistenceContext.isDefaultReadOnly() );
|
|
||||||
}
|
|
||||||
persistenceContext.beforeLoad();
|
|
||||||
List results;
|
|
||||||
try {
|
|
||||||
try {
|
|
||||||
results = doTheLoad( sql, queryParameters, session );
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
persistenceContext.afterLoad();
|
|
||||||
}
|
|
||||||
persistenceContext.initializeNonLazyCollections();
|
|
||||||
log.debug( "Done batch load" );
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
// Restore the original default
|
|
||||||
persistenceContext.setDefaultReadOnly( defaultReadOnlyOrig );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch ( SQLException sqle ) {
|
|
||||||
throw jdbcServices.getSqlExceptionHelper().convert(
|
|
||||||
sqle,
|
|
||||||
"could not load an entity batch: " + MessageHelper.infoString(
|
|
||||||
getEntityPersisters()[0],
|
|
||||||
ids,
|
|
||||||
session.getFactory()
|
|
||||||
),
|
|
||||||
sql
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List doTheLoad(String sql, QueryParameters queryParameters, SharedSessionContractImplementor session) throws SQLException {
|
|
||||||
final RowSelection selection = queryParameters.getRowSelection();
|
|
||||||
final int maxRows = LimitHelper.hasMaxRows( selection ) ?
|
|
||||||
selection.getMaxRows() :
|
|
||||||
Integer.MAX_VALUE;
|
|
||||||
|
|
||||||
final List<AfterLoadAction> afterLoadActions = new ArrayList<>();
|
|
||||||
final SqlStatementWrapper wrapper = executeQueryStatement( sql, queryParameters, false, afterLoadActions, session );
|
|
||||||
final ResultSet rs = wrapper.getResultSet();
|
|
||||||
final Statement st = wrapper.getStatement();
|
|
||||||
try {
|
|
||||||
return processResultSet( rs, queryParameters, session, false, null, maxRows, afterLoadActions );
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
final JdbcCoordinator jdbcCoordinator = session.getJdbcCoordinator();
|
|
||||||
jdbcCoordinator.getLogicalConnection().getResourceRegistry().release( st );
|
|
||||||
jdbcCoordinator.afterStatementExecution();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,241 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.entity;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
import org.hibernate.FetchMode;
|
|
||||||
import org.hibernate.LockMode;
|
|
||||||
import org.hibernate.LockOptions;
|
|
||||||
import org.hibernate.MappingException;
|
|
||||||
import org.hibernate.engine.spi.CascadeStyle;
|
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.loader.AbstractEntityJoinWalker;
|
|
||||||
import org.hibernate.loader.OuterJoinableAssociation;
|
|
||||||
import org.hibernate.loader.PropertyPath;
|
|
||||||
import org.hibernate.persister.collection.QueryableCollection;
|
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
|
||||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
|
||||||
import org.hibernate.sql.JoinType;
|
|
||||||
import org.hibernate.type.AssociationType;
|
|
||||||
import org.hibernate.type.CompositeType;
|
|
||||||
import org.hibernate.type.EntityType;
|
|
||||||
import org.hibernate.type.Type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A walker for loaders that fetch entities
|
|
||||||
*
|
|
||||||
* @see EntityLoader
|
|
||||||
* @author Gavin King
|
|
||||||
*/
|
|
||||||
public class EntityJoinWalker extends AbstractEntityJoinWalker {
|
|
||||||
|
|
||||||
private final LockOptions lockOptions = new LockOptions();
|
|
||||||
private final int[][] compositeKeyManyToOneTargetIndices;
|
|
||||||
|
|
||||||
public EntityJoinWalker(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
String[] uniqueKey,
|
|
||||||
int batchSize,
|
|
||||||
LockMode lockMode,
|
|
||||||
final SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
|
||||||
super( persister, factory, loadQueryInfluencers );
|
|
||||||
|
|
||||||
this.lockOptions.setLockMode(lockMode);
|
|
||||||
|
|
||||||
StringBuilder whereCondition = whereString( getAlias(), uniqueKey, batchSize )
|
|
||||||
//include the discriminator and class-level where, but not filters
|
|
||||||
.append( persister.filterFragment( getAlias(), Collections.EMPTY_MAP ) );
|
|
||||||
|
|
||||||
AssociationInitCallbackImpl callback = new AssociationInitCallbackImpl( factory );
|
|
||||||
initAll( whereCondition.toString(), "", lockOptions, callback );
|
|
||||||
this.compositeKeyManyToOneTargetIndices = callback.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
public EntityJoinWalker(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
String[] uniqueKey,
|
|
||||||
int batchSize,
|
|
||||||
LockOptions lockOptions,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
|
||||||
super( persister, factory, loadQueryInfluencers );
|
|
||||||
LockOptions.copy(lockOptions, this.lockOptions);
|
|
||||||
|
|
||||||
StringBuilder whereCondition = whereString( getAlias(), uniqueKey, batchSize )
|
|
||||||
//include the discriminator and class-level where, but not filters
|
|
||||||
.append( persister.filterFragment( getAlias(), Collections.EMPTY_MAP ) );
|
|
||||||
|
|
||||||
AssociationInitCallbackImpl callback = new AssociationInitCallbackImpl( factory );
|
|
||||||
initAll( whereCondition.toString(), "", lockOptions, callback );
|
|
||||||
this.compositeKeyManyToOneTargetIndices = callback.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected JoinType getJoinType(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
PropertyPath 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 ( lockOptions.getLockMode().greaterThan( LockMode.READ ) ) {
|
|
||||||
return JoinType.NONE;
|
|
||||||
}
|
|
||||||
if ( isTooDeep( currentDepth )
|
|
||||||
|| ( associationType.isCollectionType() && isTooManyCollections() ) ) {
|
|
||||||
return JoinType.NONE;
|
|
||||||
}
|
|
||||||
if ( !isJoinedFetchEnabledInMapping( metadataFetchMode, associationType )
|
|
||||||
&& !isJoinFetchEnabledByProfile( persister, path, propertyNumber ) ) {
|
|
||||||
return JoinType.NONE;
|
|
||||||
}
|
|
||||||
if ( isDuplicateAssociation( lhsTable, lhsColumns, associationType ) ) {
|
|
||||||
return JoinType.NONE;
|
|
||||||
}
|
|
||||||
return getJoinType( nullable, currentDepth );
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getComment() {
|
|
||||||
return "load " + getPersister().getEntityName();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int[][] getCompositeKeyManyToOneTargetIndices() {
|
|
||||||
return compositeKeyManyToOneTargetIndices;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class AssociationInitCallbackImpl implements AssociationInitCallback {
|
|
||||||
private final SessionFactoryImplementor factory;
|
|
||||||
private final HashMap<String,OuterJoinableAssociation> associationsByAlias
|
|
||||||
= new HashMap<String, OuterJoinableAssociation>();
|
|
||||||
private final HashMap<String,Integer> positionsByAlias = new HashMap<String, Integer>();
|
|
||||||
private final ArrayList<String> aliasesForAssociationsWithCompositesIds
|
|
||||||
= new ArrayList<String>();
|
|
||||||
|
|
||||||
public AssociationInitCallbackImpl(SessionFactoryImplementor factory) {
|
|
||||||
this.factory = factory;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void associationProcessed(OuterJoinableAssociation oja, int position) {
|
|
||||||
associationsByAlias.put( oja.getRhsAlias(), oja );
|
|
||||||
positionsByAlias.put( oja.getRhsAlias(), position );
|
|
||||||
EntityPersister entityPersister = null;
|
|
||||||
if ( oja.getJoinableType().isCollectionType() ) {
|
|
||||||
entityPersister = ( ( QueryableCollection) oja.getJoinable() ).getElementPersister();
|
|
||||||
}
|
|
||||||
else if ( oja.getJoinableType().isEntityType() ) {
|
|
||||||
entityPersister = ( EntityPersister ) oja.getJoinable();
|
|
||||||
}
|
|
||||||
if ( entityPersister != null
|
|
||||||
&& entityPersister.getIdentifierType().isComponentType()
|
|
||||||
&& ! entityPersister.getEntityMetamodel().getIdentifierProperty().isEmbedded()
|
|
||||||
&& hasAssociation( (CompositeType) entityPersister.getIdentifierType() ) ) {
|
|
||||||
aliasesForAssociationsWithCompositesIds.add( oja.getRhsAlias() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean hasAssociation(CompositeType componentType) {
|
|
||||||
for ( Type subType : componentType.getSubtypes() ) {
|
|
||||||
if ( subType.isEntityType() ) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if ( subType.isComponentType() && hasAssociation( ( (CompositeType) subType ) ) ) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int[][] resolve() {
|
|
||||||
int[][] compositeKeyManyToOneTargetIndices = null;
|
|
||||||
for ( final String aliasWithCompositeId : aliasesForAssociationsWithCompositesIds ) {
|
|
||||||
final OuterJoinableAssociation joinWithCompositeId = associationsByAlias.get( aliasWithCompositeId );
|
|
||||||
final ArrayList<Integer> keyManyToOneTargetIndices = new ArrayList<Integer>();
|
|
||||||
// for each association with a composite id containing key-many-to-one(s), find the bidirectional side of
|
|
||||||
// each key-many-to-one (if exists) to see if it is eager as well. If so, we need to track the indices
|
|
||||||
EntityPersister entityPersister = null;
|
|
||||||
if ( joinWithCompositeId.getJoinableType().isCollectionType() ) {
|
|
||||||
entityPersister = ( ( QueryableCollection) joinWithCompositeId.getJoinable() ).getElementPersister();
|
|
||||||
}
|
|
||||||
else if ( joinWithCompositeId.getJoinableType().isEntityType() ) {
|
|
||||||
entityPersister = ( EntityPersister ) joinWithCompositeId.getJoinable();
|
|
||||||
}
|
|
||||||
|
|
||||||
findKeyManyToOneTargetIndices(
|
|
||||||
keyManyToOneTargetIndices,
|
|
||||||
joinWithCompositeId,
|
|
||||||
(CompositeType) entityPersister.getIdentifierType()
|
|
||||||
);
|
|
||||||
|
|
||||||
if ( ! keyManyToOneTargetIndices.isEmpty() ) {
|
|
||||||
if ( compositeKeyManyToOneTargetIndices == null ) {
|
|
||||||
compositeKeyManyToOneTargetIndices = new int[ associationsByAlias.size() ][];
|
|
||||||
}
|
|
||||||
int position = positionsByAlias.get( aliasWithCompositeId );
|
|
||||||
compositeKeyManyToOneTargetIndices[position] = new int[ keyManyToOneTargetIndices.size() ];
|
|
||||||
int i = 0;
|
|
||||||
for ( int index : keyManyToOneTargetIndices ) {
|
|
||||||
compositeKeyManyToOneTargetIndices[position][i] = index;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return compositeKeyManyToOneTargetIndices;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void findKeyManyToOneTargetIndices(
|
|
||||||
ArrayList<Integer> keyManyToOneTargetIndices,
|
|
||||||
OuterJoinableAssociation joinWithCompositeId,
|
|
||||||
CompositeType componentType) {
|
|
||||||
for ( Type subType : componentType.getSubtypes() ) {
|
|
||||||
if ( subType.isEntityType() ) {
|
|
||||||
Integer index = locateKeyManyToOneTargetIndex( joinWithCompositeId, (EntityType) subType );
|
|
||||||
if ( index != null ) {
|
|
||||||
keyManyToOneTargetIndices.add( index );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ( subType.isComponentType() ) {
|
|
||||||
findKeyManyToOneTargetIndices(
|
|
||||||
keyManyToOneTargetIndices,
|
|
||||||
joinWithCompositeId,
|
|
||||||
(CompositeType) subType
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Integer locateKeyManyToOneTargetIndex(OuterJoinableAssociation joinWithCompositeId, EntityType keyManyToOneType) {
|
|
||||||
// the lhs (if one) is a likely candidate
|
|
||||||
if ( joinWithCompositeId.getLhsAlias() != null ) {
|
|
||||||
final OuterJoinableAssociation lhs = associationsByAlias.get( joinWithCompositeId.getLhsAlias() );
|
|
||||||
if ( keyManyToOneType.getAssociatedEntityName( factory ).equals( lhs.getJoinableType().getAssociatedEntityName( factory ) ) ) {
|
|
||||||
return positionsByAlias.get( lhs.getRhsAlias() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// otherwise, seek out OuterJoinableAssociation which are RHS of given OuterJoinableAssociation
|
|
||||||
// (joinWithCompositeId)
|
|
||||||
for ( OuterJoinableAssociation oja : associationsByAlias.values() ) {
|
|
||||||
if ( oja.getLhsAlias() != null && oja.getLhsAlias().equals( joinWithCompositeId.getRhsAlias() ) ) {
|
|
||||||
if ( keyManyToOneType.equals( oja.getJoinableType() ) ) {
|
|
||||||
return positionsByAlias.get( oja.getLhsAlias() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,156 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.entity;
|
|
||||||
|
|
||||||
import org.hibernate.LockMode;
|
|
||||||
import org.hibernate.LockOptions;
|
|
||||||
import org.hibernate.MappingException;
|
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|
||||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
|
||||||
import org.hibernate.type.Type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads an entity instance using outerjoin fetching to fetch associated entities.
|
|
||||||
* <br>
|
|
||||||
* The <tt>EntityPersister</tt> must implement <tt>Loadable</tt>. For other entities,
|
|
||||||
* create a customized subclass of <tt>Loader</tt>.
|
|
||||||
*
|
|
||||||
* @author Gavin King
|
|
||||||
*/
|
|
||||||
public class EntityLoader extends AbstractEntityLoader {
|
|
||||||
|
|
||||||
private final boolean batchLoader;
|
|
||||||
private final int[][] compositeKeyManyToOneTargetIndices;
|
|
||||||
|
|
||||||
public EntityLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
LockMode lockMode,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
|
||||||
this( persister, 1, lockMode, factory, loadQueryInfluencers );
|
|
||||||
}
|
|
||||||
|
|
||||||
public EntityLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
LockOptions lockOptions,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
|
||||||
this( persister, 1, lockOptions, factory, loadQueryInfluencers );
|
|
||||||
}
|
|
||||||
|
|
||||||
public EntityLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
int batchSize,
|
|
||||||
LockMode lockMode,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
|
||||||
this(
|
|
||||||
persister,
|
|
||||||
persister.getIdentifierColumnNames(),
|
|
||||||
persister.getIdentifierType(),
|
|
||||||
batchSize,
|
|
||||||
lockMode,
|
|
||||||
factory,
|
|
||||||
loadQueryInfluencers
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public EntityLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
int batchSize,
|
|
||||||
LockOptions lockOptions,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
|
||||||
this(
|
|
||||||
persister,
|
|
||||||
persister.getIdentifierColumnNames(),
|
|
||||||
persister.getIdentifierType(),
|
|
||||||
batchSize,
|
|
||||||
lockOptions,
|
|
||||||
factory,
|
|
||||||
loadQueryInfluencers
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public EntityLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
String[] uniqueKey,
|
|
||||||
Type uniqueKeyType,
|
|
||||||
int batchSize,
|
|
||||||
LockMode lockMode,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
|
||||||
super( persister, uniqueKeyType, factory, loadQueryInfluencers );
|
|
||||||
|
|
||||||
EntityJoinWalker walker = new EntityJoinWalker(
|
|
||||||
persister,
|
|
||||||
uniqueKey,
|
|
||||||
batchSize,
|
|
||||||
lockMode,
|
|
||||||
factory,
|
|
||||||
loadQueryInfluencers
|
|
||||||
);
|
|
||||||
initFromWalker( walker );
|
|
||||||
this.compositeKeyManyToOneTargetIndices = walker.getCompositeKeyManyToOneTargetIndices();
|
|
||||||
postInstantiate();
|
|
||||||
|
|
||||||
batchLoader = batchSize > 1;
|
|
||||||
|
|
||||||
if ( LOG.isDebugEnabled() ) {
|
|
||||||
LOG.debugf( "Static select for entity %s [%s]: %s", entityName, lockMode, getSQLString() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public EntityLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
String[] uniqueKey,
|
|
||||||
Type uniqueKeyType,
|
|
||||||
int batchSize,
|
|
||||||
LockOptions lockOptions,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
|
||||||
super( persister, uniqueKeyType, factory, loadQueryInfluencers );
|
|
||||||
|
|
||||||
EntityJoinWalker walker = new EntityJoinWalker(
|
|
||||||
persister,
|
|
||||||
uniqueKey,
|
|
||||||
batchSize,
|
|
||||||
lockOptions,
|
|
||||||
factory,
|
|
||||||
loadQueryInfluencers
|
|
||||||
);
|
|
||||||
initFromWalker( walker );
|
|
||||||
this.compositeKeyManyToOneTargetIndices = walker.getCompositeKeyManyToOneTargetIndices();
|
|
||||||
postInstantiate();
|
|
||||||
|
|
||||||
batchLoader = batchSize > 1;
|
|
||||||
|
|
||||||
if ( LOG.isDebugEnabled() ) {
|
|
||||||
LOG.debugf( "Static select for entity %s [%s:%s]: %s",
|
|
||||||
entityName,
|
|
||||||
lockOptions.getLockMode(),
|
|
||||||
lockOptions.getTimeOut(),
|
|
||||||
getSQLString() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object loadByUniqueKey(SharedSessionContractImplementor session, Object key) {
|
|
||||||
return load( session, key, null, null, LockOptions.NONE );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isSingleRowLoader() {
|
|
||||||
return !batchLoader;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int[][] getCompositeKeyManyToOneTargetIndices() {
|
|
||||||
return compositeKeyManyToOneTargetIndices;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,126 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.entity;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.hibernate.LockMode;
|
|
||||||
import org.hibernate.LockOptions;
|
|
||||||
import org.hibernate.engine.internal.BatchFetchQueueHelper;
|
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|
||||||
import org.hibernate.internal.util.collections.ArrayHelper;
|
|
||||||
import org.hibernate.loader.Loader;
|
|
||||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* No longer used, see {@link org.hibernate.loader.entity.plan.LegacyBatchingEntityLoaderBuilder} instead.
|
|
||||||
*
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public class LegacyBatchingEntityLoaderBuilder extends BatchingEntityLoaderBuilder {
|
|
||||||
public static final LegacyBatchingEntityLoaderBuilder INSTANCE = new LegacyBatchingEntityLoaderBuilder();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected UniqueEntityLoader buildBatchingLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
int batchSize,
|
|
||||||
LockMode lockMode,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers influencers) {
|
|
||||||
return new LegacyBatchingEntityLoader( persister, batchSize, lockMode, factory, influencers );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected UniqueEntityLoader buildBatchingLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
int batchSize,
|
|
||||||
LockOptions lockOptions,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers influencers) {
|
|
||||||
return new LegacyBatchingEntityLoader( persister, batchSize, lockOptions, factory, influencers );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class LegacyBatchingEntityLoader extends BatchingEntityLoader implements UniqueEntityLoader {
|
|
||||||
private final int[] batchSizes;
|
|
||||||
private final Loader[] loaders;
|
|
||||||
|
|
||||||
public LegacyBatchingEntityLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
int maxBatchSize,
|
|
||||||
LockMode lockMode,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) {
|
|
||||||
super( persister );
|
|
||||||
this.batchSizes = ArrayHelper.getBatchSizes( maxBatchSize );
|
|
||||||
this.loaders = new Loader[ batchSizes.length ];
|
|
||||||
for ( int i = 0; i < batchSizes.length; i++ ) {
|
|
||||||
this.loaders[i] = new EntityLoader( persister, batchSizes[i], lockMode, factory, loadQueryInfluencers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public LegacyBatchingEntityLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
int maxBatchSize,
|
|
||||||
LockOptions lockOptions,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) {
|
|
||||||
super( persister );
|
|
||||||
this.batchSizes = ArrayHelper.getBatchSizes( maxBatchSize );
|
|
||||||
this.loaders = new Loader[ batchSizes.length ];
|
|
||||||
for ( int i = 0; i < batchSizes.length; i++ ) {
|
|
||||||
this.loaders[i] = new EntityLoader( persister, batchSizes[i], lockOptions, factory, loadQueryInfluencers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object load(Serializable id, Object optionalObject, SharedSessionContractImplementor session, LockOptions lockOptions) {
|
|
||||||
final Object[] batch = session.getPersistenceContextInternal()
|
|
||||||
.getBatchFetchQueue()
|
|
||||||
.getBatchLoadableEntityIds( persister(), id, batchSizes[0] );
|
|
||||||
|
|
||||||
for ( int i = 0; i < batchSizes.length-1; i++) {
|
|
||||||
final int smallBatchSize = batchSizes[i];
|
|
||||||
if ( batch[smallBatchSize-1] != null ) {
|
|
||||||
Serializable[] smallBatch = new Serializable[smallBatchSize];
|
|
||||||
System.arraycopy(batch, 0, smallBatch, 0, smallBatchSize);
|
|
||||||
// for now...
|
|
||||||
final List results = loaders[i].loadEntityBatch(
|
|
||||||
session,
|
|
||||||
smallBatch,
|
|
||||||
persister().getIdentifierType(),
|
|
||||||
optionalObject,
|
|
||||||
persister().getEntityName(),
|
|
||||||
id,
|
|
||||||
persister(),
|
|
||||||
lockOptions
|
|
||||||
);
|
|
||||||
// The EntityKey for any entity that is not found will remain in the batch.
|
|
||||||
// Explicitly remove the EntityKeys for entities that were not found to
|
|
||||||
// avoid including them in future batches that get executed.
|
|
||||||
BatchFetchQueueHelper.removeNotFoundBatchLoadableEntityKeys(
|
|
||||||
smallBatch,
|
|
||||||
results,
|
|
||||||
persister(),
|
|
||||||
session
|
|
||||||
);
|
|
||||||
return getObjectFromList(results, id, session); //EARLY EXIT
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final Object result = ( (UniqueEntityLoader) loaders[batchSizes.length-1] ).load(id, optionalObject, session);
|
|
||||||
if ( result == null ) {
|
|
||||||
// There was no entity with the specified ID. Make sure the EntityKey does not remain
|
|
||||||
// in the batch to avoid including it in future batches that get executed.
|
|
||||||
BatchFetchQueueHelper.removeBatchLoadableEntityKey( id, persister(), session );
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,130 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.entity;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
|
||||||
import org.hibernate.LockMode;
|
|
||||||
import org.hibernate.LockOptions;
|
|
||||||
import org.hibernate.engine.internal.BatchFetchQueueHelper;
|
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|
||||||
import org.hibernate.internal.util.collections.ArrayHelper;
|
|
||||||
import org.hibernate.loader.Loader;
|
|
||||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
class PaddedBatchingEntityLoaderBuilder extends BatchingEntityLoaderBuilder {
|
|
||||||
public static final PaddedBatchingEntityLoaderBuilder INSTANCE = new PaddedBatchingEntityLoaderBuilder();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected UniqueEntityLoader buildBatchingLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
int batchSize,
|
|
||||||
LockMode lockMode,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers influencers) {
|
|
||||||
return new PaddedBatchingEntityLoader( persister, batchSize, lockMode, factory, influencers );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected UniqueEntityLoader buildBatchingLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
int batchSize,
|
|
||||||
LockOptions lockOptions,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers influencers) {
|
|
||||||
return new PaddedBatchingEntityLoader( persister, batchSize, lockOptions, factory, influencers );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class PaddedBatchingEntityLoader extends BatchingEntityLoader {
|
|
||||||
private final int[] batchSizes;
|
|
||||||
private final Loader[] loaders;
|
|
||||||
|
|
||||||
public PaddedBatchingEntityLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
int maxBatchSize,
|
|
||||||
LockMode lockMode,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) {
|
|
||||||
super( persister );
|
|
||||||
this.batchSizes = ArrayHelper.getBatchSizes( maxBatchSize );
|
|
||||||
this.loaders = new Loader[ batchSizes.length ];
|
|
||||||
for ( int i = 0; i < batchSizes.length; i++ ) {
|
|
||||||
this.loaders[i] = new EntityLoader( persister, batchSizes[i], lockMode, factory, loadQueryInfluencers);
|
|
||||||
}
|
|
||||||
validate( maxBatchSize );
|
|
||||||
}
|
|
||||||
|
|
||||||
private void validate(int max) {
|
|
||||||
// these are more indicative of internal problems then user error...
|
|
||||||
if ( batchSizes[0] != max ) {
|
|
||||||
throw new HibernateException( "Unexpected batch size spread" );
|
|
||||||
}
|
|
||||||
if ( batchSizes[batchSizes.length-1] != 1 ) {
|
|
||||||
throw new HibernateException( "Unexpected batch size spread" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public PaddedBatchingEntityLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
int maxBatchSize,
|
|
||||||
LockOptions lockOptions,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) {
|
|
||||||
super( persister );
|
|
||||||
this.batchSizes = ArrayHelper.getBatchSizes( maxBatchSize );
|
|
||||||
this.loaders = new Loader[ batchSizes.length ];
|
|
||||||
for ( int i = 0; i < batchSizes.length; i++ ) {
|
|
||||||
this.loaders[i] = new EntityLoader( persister, batchSizes[i], lockOptions, factory, loadQueryInfluencers);
|
|
||||||
}
|
|
||||||
validate( maxBatchSize );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object load(Serializable id, Object optionalObject, SharedSessionContractImplementor session, LockOptions lockOptions) {
|
|
||||||
final Object[] batch = session.getPersistenceContextInternal()
|
|
||||||
.getBatchFetchQueue()
|
|
||||||
.getBatchLoadableEntityIds( persister(), id, batchSizes[0] );
|
|
||||||
|
|
||||||
final int numberOfIds = ArrayHelper.countNonNull( batch );
|
|
||||||
if ( numberOfIds <= 1 ) {
|
|
||||||
final Object result = ( (UniqueEntityLoader) loaders[batchSizes.length-1] ).load( id, optionalObject, session );
|
|
||||||
if ( result == null ) {
|
|
||||||
// There was no entity with the specified ID. Make sure the EntityKey does not remain
|
|
||||||
// in the batch to avoid including it in future batches that get executed.
|
|
||||||
BatchFetchQueueHelper.removeBatchLoadableEntityKey( id, persister(), session );
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uses the first batch-size bigger than the number of actual ids in the batch
|
|
||||||
int indexToUse = batchSizes.length-1;
|
|
||||||
for ( int i = 0; i < batchSizes.length-1; i++ ) {
|
|
||||||
if ( batchSizes[i] >= numberOfIds ) {
|
|
||||||
indexToUse = i;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final Serializable[] idsToLoad = new Serializable[ batchSizes[indexToUse] ];
|
|
||||||
System.arraycopy( batch, 0, idsToLoad, 0, numberOfIds );
|
|
||||||
for ( int i = numberOfIds; i < batchSizes[indexToUse]; i++ ) {
|
|
||||||
idsToLoad[i] = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
return doBatchLoad( id, loaders[indexToUse], session, idsToLoad, optionalObject, lockOptions );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.entity;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
|
||||||
import org.hibernate.LockOptions;
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads entities for a <tt>EntityPersister</tt>
|
|
||||||
*
|
|
||||||
* @author Gavin King
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public interface UniqueEntityLoader {
|
|
||||||
/**
|
|
||||||
* Load an entity instance. If <tt>optionalObject</tt> is supplied,
|
|
||||||
* load the entity state into the given (uninitialized) object.
|
|
||||||
*
|
|
||||||
* @deprecated use {@link #load(java.io.Serializable, Object, SharedSessionContractImplementor, LockOptions)} instead.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings( {"JavaDoc"})
|
|
||||||
@Deprecated
|
|
||||||
Object load(Serializable id, Object optionalObject, SharedSessionContractImplementor session) throws HibernateException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load an entity instance by id. If <tt>optionalObject</tt> is supplied (non-<tt>null</tt>,
|
|
||||||
* the entity state is loaded into that object instance instead of instantiating a new one.
|
|
||||||
*
|
|
||||||
* @param id The id to be loaded
|
|
||||||
* @param optionalObject The (optional) entity instance in to which to load the state
|
|
||||||
* @param session The session from which the request originated
|
|
||||||
* @param lockOptions The lock options.
|
|
||||||
*
|
|
||||||
* @return The loaded entity
|
|
||||||
*
|
|
||||||
* @throws HibernateException indicates problem performing the load.
|
|
||||||
*/
|
|
||||||
Object load(
|
|
||||||
Serializable id,
|
|
||||||
Object optionalObject,
|
|
||||||
SharedSessionContractImplementor session,
|
|
||||||
LockOptions lockOptions);
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
<!--
|
|
||||||
~ Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
~
|
|
||||||
~ License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
~ See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<html>
|
|
||||||
<head></head>
|
|
||||||
<body>
|
|
||||||
<p>
|
|
||||||
This package defines entity loaders
|
|
||||||
</p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,41 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.entity.plan;
|
|
||||||
|
|
||||||
import org.hibernate.LockMode;
|
|
||||||
import org.hibernate.LockOptions;
|
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.loader.entity.BatchingEntityLoaderBuilder;
|
|
||||||
import org.hibernate.loader.entity.UniqueEntityLoader;
|
|
||||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for LoadPlan-based BatchingEntityLoaderBuilder implementations. Mainly we handle the common
|
|
||||||
* "no batching" case here to use the LoadPlan-based EntityLoader
|
|
||||||
*
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public abstract class AbstractBatchingEntityLoaderBuilder extends BatchingEntityLoaderBuilder {
|
|
||||||
@Override
|
|
||||||
protected UniqueEntityLoader buildNonBatchingLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
LockMode lockMode,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers influencers) {
|
|
||||||
return EntityLoader.forEntity( persister ).withLockMode( lockMode ).withInfluencers( influencers ).byPrimaryKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected UniqueEntityLoader buildNonBatchingLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
LockOptions lockOptions,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers influencers) {
|
|
||||||
return EntityLoader.forEntity( persister ).withLockOptions( lockOptions ).withInfluencers( influencers ).byPrimaryKey();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,276 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.entity.plan;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.hibernate.AssertionFailure;
|
|
||||||
import org.hibernate.HibernateException;
|
|
||||||
import org.hibernate.LockOptions;
|
|
||||||
import org.hibernate.engine.spi.EffectiveEntityGraph;
|
|
||||||
import org.hibernate.engine.spi.QueryParameters;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|
||||||
import org.hibernate.graph.GraphSemantic;
|
|
||||||
import org.hibernate.internal.CoreLogging;
|
|
||||||
import org.hibernate.internal.CoreMessageLogger;
|
|
||||||
import org.hibernate.loader.entity.UniqueEntityLoader;
|
|
||||||
import org.hibernate.loader.plan.build.internal.FetchGraphLoadPlanBuildingStrategy;
|
|
||||||
import org.hibernate.loader.plan.build.internal.FetchStyleLoadPlanBuildingAssociationVisitationStrategy;
|
|
||||||
import org.hibernate.loader.plan.build.internal.LoadGraphLoadPlanBuildingStrategy;
|
|
||||||
import org.hibernate.loader.plan.build.spi.LoadPlanBuildingAssociationVisitationStrategy;
|
|
||||||
import org.hibernate.loader.plan.build.spi.MetamodelDrivenLoadPlanBuilder;
|
|
||||||
import org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader;
|
|
||||||
import org.hibernate.loader.plan.exec.internal.BatchingLoadQueryDetailsFactory;
|
|
||||||
import org.hibernate.loader.plan.exec.internal.EntityLoadQueryDetails;
|
|
||||||
import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters;
|
|
||||||
import org.hibernate.loader.plan.exec.spi.LoadQueryDetails;
|
|
||||||
import org.hibernate.loader.plan.spi.LoadPlan;
|
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
|
||||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
|
||||||
import org.hibernate.pretty.MessageHelper;
|
|
||||||
import org.hibernate.type.Type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A UniqueEntityLoader implementation based on using LoadPlans
|
|
||||||
*
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public abstract class AbstractLoadPlanBasedEntityLoader extends AbstractLoadPlanBasedLoader implements UniqueEntityLoader {
|
|
||||||
private static final CoreMessageLogger log = CoreLogging.messageLogger( AbstractLoadPlanBasedEntityLoader.class );
|
|
||||||
|
|
||||||
private final OuterJoinLoadable entityPersister;
|
|
||||||
private final Type uniqueKeyType;
|
|
||||||
private final String entityName;
|
|
||||||
|
|
||||||
private final EntityLoadQueryDetails staticLoadQuery;
|
|
||||||
|
|
||||||
public AbstractLoadPlanBasedEntityLoader(
|
|
||||||
OuterJoinLoadable entityPersister,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
String[] uniqueKeyColumnNames,
|
|
||||||
Type uniqueKeyType,
|
|
||||||
QueryBuildingParameters buildingParameters) {
|
|
||||||
super( factory );
|
|
||||||
this.entityPersister = entityPersister;
|
|
||||||
this.uniqueKeyType = uniqueKeyType;
|
|
||||||
this.entityName = entityPersister.getEntityName();
|
|
||||||
|
|
||||||
final LoadPlanBuildingAssociationVisitationStrategy strategy;
|
|
||||||
|
|
||||||
final EffectiveEntityGraph effectiveEntityGraph = buildingParameters.getQueryInfluencers().getEffectiveEntityGraph();
|
|
||||||
if ( effectiveEntityGraph.getSemantic() == GraphSemantic.FETCH ) {
|
|
||||||
strategy = new FetchGraphLoadPlanBuildingStrategy(
|
|
||||||
factory,
|
|
||||||
effectiveEntityGraph.getGraph(),
|
|
||||||
buildingParameters.getQueryInfluencers(),
|
|
||||||
buildingParameters.getLockOptions() != null ? buildingParameters.getLockOptions().getLockMode() : buildingParameters.getLockMode()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else if ( effectiveEntityGraph.getSemantic() == GraphSemantic.LOAD ) {
|
|
||||||
strategy = new LoadGraphLoadPlanBuildingStrategy(
|
|
||||||
factory,
|
|
||||||
effectiveEntityGraph.getGraph(),
|
|
||||||
buildingParameters.getQueryInfluencers(),
|
|
||||||
buildingParameters.getLockOptions() != null ? buildingParameters.getLockOptions().getLockMode() : buildingParameters.getLockMode()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
strategy = new FetchStyleLoadPlanBuildingAssociationVisitationStrategy(
|
|
||||||
factory,
|
|
||||||
buildingParameters.getQueryInfluencers(),
|
|
||||||
buildingParameters.getLockOptions() != null ? buildingParameters.getLockOptions().getLockMode() : buildingParameters.getLockMode()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final LoadPlan plan = MetamodelDrivenLoadPlanBuilder.buildRootEntityLoadPlan( strategy, entityPersister );
|
|
||||||
this.staticLoadQuery = BatchingLoadQueryDetailsFactory.INSTANCE.makeEntityLoadQueryDetails(
|
|
||||||
plan,
|
|
||||||
uniqueKeyColumnNames,
|
|
||||||
buildingParameters,
|
|
||||||
factory
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected AbstractLoadPlanBasedEntityLoader(
|
|
||||||
OuterJoinLoadable entityPersister,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
EntityLoadQueryDetails entityLoaderQueryDetailsTemplate,
|
|
||||||
Type uniqueKeyType,
|
|
||||||
QueryBuildingParameters buildingParameters) {
|
|
||||||
super( factory );
|
|
||||||
this.entityPersister = entityPersister;
|
|
||||||
this.uniqueKeyType = uniqueKeyType;
|
|
||||||
this.entityName = entityPersister.getEntityName();
|
|
||||||
|
|
||||||
this.staticLoadQuery = BatchingLoadQueryDetailsFactory.INSTANCE.makeEntityLoadQueryDetails(
|
|
||||||
entityLoaderQueryDetailsTemplate,
|
|
||||||
buildingParameters
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected LoadQueryDetails getStaticLoadQuery() {
|
|
||||||
return staticLoadQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String getEntityName() {
|
|
||||||
return entityName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by wrappers that batch load entities
|
|
||||||
* @param persister only needed for logging
|
|
||||||
* @param lockOptions
|
|
||||||
*/
|
|
||||||
public final List loadEntityBatch(
|
|
||||||
final SharedSessionContractImplementor session,
|
|
||||||
final Serializable[] ids,
|
|
||||||
final Type idType,
|
|
||||||
final Object optionalObject,
|
|
||||||
final String optionalEntityName,
|
|
||||||
final Serializable optionalId,
|
|
||||||
final EntityPersister persister,
|
|
||||||
LockOptions lockOptions) throws HibernateException {
|
|
||||||
|
|
||||||
if ( log.isDebugEnabled() ) {
|
|
||||||
log.debugf( "Batch loading entity: %s", MessageHelper.infoString( persister, ids, getFactory() ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
final Type[] types = new Type[ids.length];
|
|
||||||
Arrays.fill( types, idType );
|
|
||||||
List result;
|
|
||||||
try {
|
|
||||||
final QueryParameters qp = new QueryParameters();
|
|
||||||
qp.setPositionalParameterTypes( types );
|
|
||||||
qp.setPositionalParameterValues( ids );
|
|
||||||
qp.setLockOptions( lockOptions );
|
|
||||||
|
|
||||||
result = executeLoad(
|
|
||||||
session,
|
|
||||||
qp,
|
|
||||||
staticLoadQuery,
|
|
||||||
false,
|
|
||||||
null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
catch ( SQLException sqle ) {
|
|
||||||
throw getFactory().getSQLExceptionHelper().convert(
|
|
||||||
sqle,
|
|
||||||
"could not load an entity batch: " + MessageHelper.infoString( entityPersister, ids, getFactory() ),
|
|
||||||
staticLoadQuery.getSqlStatement()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
log.debug( "Done entity batch load" );
|
|
||||||
|
|
||||||
return result;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object load(Serializable id, Object optionalObject, SharedSessionContractImplementor session) throws HibernateException {
|
|
||||||
return load( id, optionalObject, session, LockOptions.NONE );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object load(Serializable id, Object optionalObject, SharedSessionContractImplementor session, LockOptions lockOptions) {
|
|
||||||
|
|
||||||
final Object result;
|
|
||||||
try {
|
|
||||||
final QueryParameters qp = new QueryParameters();
|
|
||||||
qp.setPositionalParameterTypes( new Type[] { entityPersister.getIdentifierType() } );
|
|
||||||
qp.setPositionalParameterValues( new Object[] { id } );
|
|
||||||
qp.setOptionalObject( optionalObject );
|
|
||||||
qp.setOptionalEntityName( entityPersister.getEntityName() );
|
|
||||||
qp.setOptionalId( id );
|
|
||||||
qp.setLockOptions( lockOptions );
|
|
||||||
|
|
||||||
final List results = executeLoad(
|
|
||||||
session,
|
|
||||||
qp,
|
|
||||||
staticLoadQuery,
|
|
||||||
false,
|
|
||||||
null
|
|
||||||
);
|
|
||||||
result = extractEntityResult( results, id );
|
|
||||||
}
|
|
||||||
catch ( SQLException sqle ) {
|
|
||||||
throw session.getJdbcServices().getSqlExceptionHelper().convert(
|
|
||||||
sqle,
|
|
||||||
"could not load an entity: " + MessageHelper.infoString(
|
|
||||||
entityPersister,
|
|
||||||
id,
|
|
||||||
entityPersister.getIdentifierType(),
|
|
||||||
getFactory()
|
|
||||||
),
|
|
||||||
staticLoadQuery.getSqlStatement()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
log.debugf( "Done entity load : %s#%s", getEntityName(), id );
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated {@link #extractEntityResult(List, Serializable)} should be used instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
protected Object extractEntityResult(List results) {
|
|
||||||
return extractEntityResult( results, null );
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Object extractEntityResult(List results, Serializable id) {
|
|
||||||
if ( results.size() == 0 ) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
else if ( results.size() == 1 ) {
|
|
||||||
return results.get( 0 );
|
|
||||||
}
|
|
||||||
else if ( staticLoadQuery.hasCollectionInitializers() ) {
|
|
||||||
final Object row = results.get( 0 );
|
|
||||||
if ( row.getClass().isArray() ) {
|
|
||||||
// the logical type of the result list is List<Object[]>. See if the contained
|
|
||||||
// array contains just one element, and return that if so
|
|
||||||
final Object[] rowArray = (Object[]) row;
|
|
||||||
if ( rowArray.length == 1 ) {
|
|
||||||
return rowArray[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return row;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( id == null ) {
|
|
||||||
throw new HibernateException(
|
|
||||||
"Unable to interpret given query results in terms of a load-entity query for " +
|
|
||||||
entityName
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new HibernateException(
|
|
||||||
"More than one row with the given identifier was found: " +
|
|
||||||
id +
|
|
||||||
", for class: " +
|
|
||||||
entityName
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int[] getNamedParameterLocs(String name) {
|
|
||||||
throw new AssertionFailure("no named parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void autoDiscoverTypes(ResultSet rs) {
|
|
||||||
throw new AssertionFailure("Auto discover types not supported in this loader");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,114 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.entity.plan;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.hibernate.LockOptions;
|
|
||||||
import org.hibernate.engine.spi.QueryParameters;
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|
||||||
import org.hibernate.loader.Loader;
|
|
||||||
import org.hibernate.loader.entity.UniqueEntityLoader;
|
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
|
||||||
import org.hibernate.pretty.MessageHelper;
|
|
||||||
import org.hibernate.type.Type;
|
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The base contract for UniqueEntityLoader implementations capable of performing batch-fetch loading of entities
|
|
||||||
* using multiple primary key values in the SQL <tt>WHERE</tt> clause.
|
|
||||||
* <p/>
|
|
||||||
* Typically these are
|
|
||||||
*
|
|
||||||
* @author Gavin King
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*
|
|
||||||
* @see org.hibernate.loader.entity.BatchingEntityLoaderBuilder
|
|
||||||
* @see org.hibernate.loader.entity.UniqueEntityLoader
|
|
||||||
*/
|
|
||||||
public abstract class BatchingEntityLoader implements UniqueEntityLoader {
|
|
||||||
private static final Logger log = Logger.getLogger( BatchingEntityLoader.class );
|
|
||||||
|
|
||||||
private final EntityPersister persister;
|
|
||||||
|
|
||||||
public BatchingEntityLoader(EntityPersister persister) {
|
|
||||||
this.persister = persister;
|
|
||||||
}
|
|
||||||
|
|
||||||
public EntityPersister persister() {
|
|
||||||
return persister;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object load(Serializable id, Object optionalObject, SharedSessionContractImplementor session) {
|
|
||||||
return load( id, optionalObject, session, LockOptions.NONE );
|
|
||||||
}
|
|
||||||
|
|
||||||
protected QueryParameters buildQueryParameters(
|
|
||||||
Serializable id,
|
|
||||||
Serializable[] ids,
|
|
||||||
Object optionalObject,
|
|
||||||
LockOptions lockOptions) {
|
|
||||||
Type[] types = new Type[ids.length];
|
|
||||||
Arrays.fill( types, persister().getIdentifierType() );
|
|
||||||
|
|
||||||
QueryParameters qp = new QueryParameters();
|
|
||||||
qp.setPositionalParameterTypes( types );
|
|
||||||
qp.setPositionalParameterValues( ids );
|
|
||||||
qp.setOptionalObject( optionalObject );
|
|
||||||
qp.setOptionalEntityName( persister().getEntityName() );
|
|
||||||
qp.setOptionalId( id );
|
|
||||||
qp.setLockOptions( lockOptions );
|
|
||||||
return qp;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Object getObjectFromList(List results, Serializable id, SharedSessionContractImplementor session) {
|
|
||||||
for ( Object obj : results ) {
|
|
||||||
final boolean equal = persister.getIdentifierType().isEqual(
|
|
||||||
id,
|
|
||||||
session.getContextEntityIdentifier( obj ),
|
|
||||||
session.getFactory()
|
|
||||||
);
|
|
||||||
if ( equal ) {
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Object doBatchLoad(
|
|
||||||
Serializable id,
|
|
||||||
Loader loaderToUse,
|
|
||||||
SharedSessionContractImplementor session,
|
|
||||||
Serializable[] ids,
|
|
||||||
Object optionalObject,
|
|
||||||
LockOptions lockOptions) {
|
|
||||||
if ( log.isDebugEnabled() ) {
|
|
||||||
log.debugf( "Batch loading entity: %s", MessageHelper.infoString( persister, ids, session.getFactory() ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
QueryParameters qp = buildQueryParameters( id, ids, optionalObject, lockOptions );
|
|
||||||
|
|
||||||
try {
|
|
||||||
final List results = loaderToUse.doQueryAndInitializeNonLazyCollections( session, qp, false );
|
|
||||||
log.debug( "Done entity batch load" );
|
|
||||||
return getObjectFromList(results, id, session);
|
|
||||||
}
|
|
||||||
catch ( SQLException sqle ) {
|
|
||||||
throw session.getJdbcServices().getSqlExceptionHelper().convert(
|
|
||||||
sqle,
|
|
||||||
"could not load an entity batch: " + MessageHelper.infoString( persister(), ids, session.getFactory() ),
|
|
||||||
loaderToUse.getSQLString()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,179 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.entity.plan;
|
|
||||||
|
|
||||||
import org.hibernate.LockMode;
|
|
||||||
import org.hibernate.LockOptions;
|
|
||||||
import org.hibernate.MappingException;
|
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.internal.CoreLogging;
|
|
||||||
import org.hibernate.loader.plan.exec.internal.EntityLoadQueryDetails;
|
|
||||||
import org.hibernate.loader.plan.exec.query.internal.QueryBuildingParametersImpl;
|
|
||||||
import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters;
|
|
||||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
|
||||||
import org.hibernate.type.Type;
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* UniqueEntityLoader implementation that is the main functionality for LoadPlan-based Entity loading.
|
|
||||||
* <p/>
|
|
||||||
* Can handle batch-loading as well as non-pk, unique-key loading,
|
|
||||||
* <p/>
|
|
||||||
* Much is ultimately delegated to its superclass, AbstractLoadPlanBasedEntityLoader. However:
|
|
||||||
*
|
|
||||||
* Loads an entity instance using outerjoin fetching to fetch associated entities.
|
|
||||||
* <br>
|
|
||||||
* The <tt>EntityPersister</tt> must implement <tt>Loadable</tt>. For other entities,
|
|
||||||
* create a customized subclass of <tt>Loader</tt>.
|
|
||||||
*
|
|
||||||
* @author Gavin King
|
|
||||||
* @author Steve Ebersole
|
|
||||||
* @author Gail Badner
|
|
||||||
*/
|
|
||||||
public class EntityLoader extends AbstractLoadPlanBasedEntityLoader {
|
|
||||||
private static final Logger log = CoreLogging.logger( EntityLoader.class );
|
|
||||||
|
|
||||||
public static Builder forEntity(OuterJoinLoadable persister) {
|
|
||||||
return new Builder( persister );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Builder {
|
|
||||||
private final OuterJoinLoadable persister;
|
|
||||||
private EntityLoader entityLoaderTemplate;
|
|
||||||
private int batchSize = 1;
|
|
||||||
private LoadQueryInfluencers influencers = LoadQueryInfluencers.NONE;
|
|
||||||
private LockMode lockMode = LockMode.NONE;
|
|
||||||
private LockOptions lockOptions;
|
|
||||||
|
|
||||||
public Builder(OuterJoinLoadable persister) {
|
|
||||||
this.persister = persister;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder withEntityLoaderTemplate(EntityLoader entityLoaderTemplate) {
|
|
||||||
this.entityLoaderTemplate = entityLoaderTemplate;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder withBatchSize(int batchSize) {
|
|
||||||
this.batchSize = batchSize;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder withInfluencers(LoadQueryInfluencers influencers) {
|
|
||||||
this.influencers = influencers;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder withLockMode(LockMode lockMode) {
|
|
||||||
this.lockMode = lockMode;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder withLockOptions(LockOptions lockOptions) {
|
|
||||||
this.lockOptions = lockOptions;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public EntityLoader byPrimaryKey() {
|
|
||||||
return byUniqueKey( persister.getIdentifierColumnNames(), persister.getIdentifierType() );
|
|
||||||
}
|
|
||||||
|
|
||||||
public EntityLoader byUniqueKey(String[] keyColumnNames, Type keyType) {
|
|
||||||
// capture current values in a new instance of QueryBuildingParametersImpl
|
|
||||||
if ( entityLoaderTemplate == null ) {
|
|
||||||
return new EntityLoader(
|
|
||||||
persister.getFactory(),
|
|
||||||
persister,
|
|
||||||
keyColumnNames,
|
|
||||||
keyType,
|
|
||||||
new QueryBuildingParametersImpl(
|
|
||||||
influencers,
|
|
||||||
batchSize,
|
|
||||||
lockMode,
|
|
||||||
lockOptions
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return new EntityLoader(
|
|
||||||
persister.getFactory(),
|
|
||||||
persister,
|
|
||||||
entityLoaderTemplate,
|
|
||||||
keyType,
|
|
||||||
new QueryBuildingParametersImpl(
|
|
||||||
influencers,
|
|
||||||
batchSize,
|
|
||||||
lockMode,
|
|
||||||
lockOptions
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private EntityLoader(
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
String[] uniqueKeyColumnNames,
|
|
||||||
Type uniqueKeyType,
|
|
||||||
QueryBuildingParameters buildingParameters) throws MappingException {
|
|
||||||
super( persister, factory, uniqueKeyColumnNames, uniqueKeyType, buildingParameters );
|
|
||||||
if ( log.isDebugEnabled() ) {
|
|
||||||
if ( buildingParameters.getLockOptions() != null ) {
|
|
||||||
log.debugf(
|
|
||||||
"Static select for entity %s [%s:%s]: %s",
|
|
||||||
getEntityName(),
|
|
||||||
buildingParameters.getLockOptions().getLockMode(),
|
|
||||||
buildingParameters.getLockOptions().getTimeOut(),
|
|
||||||
getStaticLoadQuery().getSqlStatement()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else if ( buildingParameters.getLockMode() != null ) {
|
|
||||||
log.debugf(
|
|
||||||
"Static select for entity %s [%s]: %s",
|
|
||||||
getEntityName(),
|
|
||||||
buildingParameters.getLockMode(),
|
|
||||||
getStaticLoadQuery().getSqlStatement()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private EntityLoader(
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
EntityLoader entityLoaderTemplate,
|
|
||||||
Type uniqueKeyType,
|
|
||||||
QueryBuildingParameters buildingParameters) throws MappingException {
|
|
||||||
super( persister, factory, entityLoaderTemplate.getStaticLoadQuery(), uniqueKeyType, buildingParameters );
|
|
||||||
if ( log.isDebugEnabled() ) {
|
|
||||||
if ( buildingParameters.getLockOptions() != null ) {
|
|
||||||
log.debugf(
|
|
||||||
"Static select for entity %s [%s:%s]: %s",
|
|
||||||
getEntityName(),
|
|
||||||
buildingParameters.getLockOptions().getLockMode(),
|
|
||||||
buildingParameters.getLockOptions().getTimeOut(),
|
|
||||||
getStaticLoadQuery().getSqlStatement()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else if ( buildingParameters.getLockMode() != null ) {
|
|
||||||
log.debugf(
|
|
||||||
"Static select for entity %s [%s]: %s",
|
|
||||||
getEntityName(),
|
|
||||||
buildingParameters.getLockMode(),
|
|
||||||
getStaticLoadQuery().getSqlStatement()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected EntityLoadQueryDetails getStaticLoadQuery() {
|
|
||||||
return (EntityLoadQueryDetails) super.getStaticLoadQuery();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,140 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.entity.plan;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.hibernate.LockMode;
|
|
||||||
import org.hibernate.LockOptions;
|
|
||||||
import org.hibernate.engine.internal.BatchFetchQueueHelper;
|
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|
||||||
import org.hibernate.internal.util.collections.ArrayHelper;
|
|
||||||
import org.hibernate.loader.entity.UniqueEntityLoader;
|
|
||||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* LoadPlan-based implementation of the legacy batch loading strategy
|
|
||||||
*
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public class LegacyBatchingEntityLoaderBuilder extends AbstractBatchingEntityLoaderBuilder {
|
|
||||||
public static final LegacyBatchingEntityLoaderBuilder INSTANCE = new LegacyBatchingEntityLoaderBuilder();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected UniqueEntityLoader buildBatchingLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
int batchSize,
|
|
||||||
LockMode lockMode,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers influencers) {
|
|
||||||
return new LegacyBatchingEntityLoader( persister, batchSize, lockMode, factory, influencers );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected UniqueEntityLoader buildBatchingLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
int batchSize,
|
|
||||||
LockOptions lockOptions,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers influencers) {
|
|
||||||
return new LegacyBatchingEntityLoader( persister, batchSize, lockOptions, factory, influencers );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class LegacyBatchingEntityLoader extends BatchingEntityLoader {
|
|
||||||
private final int[] batchSizes;
|
|
||||||
private final EntityLoader[] loaders;
|
|
||||||
|
|
||||||
public LegacyBatchingEntityLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
int maxBatchSize,
|
|
||||||
LockMode lockMode,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) {
|
|
||||||
this( persister, maxBatchSize, lockMode, null, factory, loadQueryInfluencers );
|
|
||||||
}
|
|
||||||
|
|
||||||
public LegacyBatchingEntityLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
int maxBatchSize,
|
|
||||||
LockOptions lockOptions,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) {
|
|
||||||
this( persister, maxBatchSize, null, lockOptions, factory, loadQueryInfluencers );
|
|
||||||
}
|
|
||||||
|
|
||||||
protected LegacyBatchingEntityLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
int maxBatchSize,
|
|
||||||
LockMode lockMode,
|
|
||||||
LockOptions lockOptions,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) {
|
|
||||||
super( persister );
|
|
||||||
this.batchSizes = ArrayHelper.getBatchSizes( maxBatchSize );
|
|
||||||
this.loaders = new EntityLoader[ batchSizes.length ];
|
|
||||||
final EntityLoader.Builder entityLoaderBuilder = EntityLoader.forEntity( persister )
|
|
||||||
.withInfluencers( loadQueryInfluencers )
|
|
||||||
.withLockMode( lockMode )
|
|
||||||
.withLockOptions( lockOptions );
|
|
||||||
|
|
||||||
// we create a first entity loader to use it as a template for the others
|
|
||||||
this.loaders[0] = entityLoaderBuilder.withBatchSize( batchSizes[0] ).byPrimaryKey();
|
|
||||||
|
|
||||||
for ( int i = 1; i < batchSizes.length; i++ ) {
|
|
||||||
this.loaders[i] = entityLoaderBuilder.withEntityLoaderTemplate( this.loaders[0] ).withBatchSize( batchSizes[i] ).byPrimaryKey();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object load(Serializable id, Object optionalObject, SharedSessionContractImplementor session, LockOptions lockOptions) {
|
|
||||||
final Object[] batch = session.getPersistenceContextInternal()
|
|
||||||
.getBatchFetchQueue()
|
|
||||||
.getBatchLoadableEntityIds( persister(), id, batchSizes[0] );
|
|
||||||
|
|
||||||
for ( int i = 0; i < batchSizes.length-1; i++) {
|
|
||||||
final int smallBatchSize = batchSizes[i];
|
|
||||||
if ( batch[smallBatchSize-1] != null ) {
|
|
||||||
Serializable[] smallBatch = new Serializable[smallBatchSize];
|
|
||||||
System.arraycopy(batch, 0, smallBatch, 0, smallBatchSize);
|
|
||||||
// for now...
|
|
||||||
final List results = loaders[i].loadEntityBatch(
|
|
||||||
session,
|
|
||||||
smallBatch,
|
|
||||||
persister().getIdentifierType(),
|
|
||||||
optionalObject,
|
|
||||||
persister().getEntityName(),
|
|
||||||
id,
|
|
||||||
persister(),
|
|
||||||
lockOptions
|
|
||||||
);
|
|
||||||
// The EntityKey for any entity that is not found will remain in the batch.
|
|
||||||
// Explicitly remove the EntityKeys for entities that were not found to
|
|
||||||
// avoid including them in future batches that get executed.
|
|
||||||
BatchFetchQueueHelper.removeNotFoundBatchLoadableEntityKeys(
|
|
||||||
smallBatch,
|
|
||||||
results,
|
|
||||||
persister(),
|
|
||||||
session
|
|
||||||
);
|
|
||||||
|
|
||||||
//EARLY EXIT
|
|
||||||
return getObjectFromList( results, id, session );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final Object result = ( loaders[batchSizes.length-1] ).load( id, optionalObject, session, lockOptions );
|
|
||||||
if ( result == null ) {
|
|
||||||
// There was no entity with the specified ID. Make sure the EntityKey does not remain
|
|
||||||
// in the batch to avoid including it in future batches that get executed.
|
|
||||||
BatchFetchQueueHelper.removeBatchLoadableEntityKey( id, persister(), session );
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
<!--
|
|
||||||
~ Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
~
|
|
||||||
~ License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
~ See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<html>
|
|
||||||
<head></head>
|
|
||||||
<body>
|
|
||||||
<p>
|
|
||||||
This package defines a loader for the AST-based query parser
|
|
||||||
</p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue