diff --git a/build.gradle b/build.gradle index 247aacecab..eef628e62a 100644 --- a/build.gradle +++ b/build.gradle @@ -483,3 +483,5 @@ subprojects { subProject -> } } + +task release(type: Task, dependsOn: 'release:release') diff --git a/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java b/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java index d847c28a4a..b50a8c2477 100644 --- a/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java +++ b/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java @@ -26,7 +26,6 @@ package org.hibernate; import java.io.Serializable; import org.hibernate.procedure.ProcedureCall; -import org.hibernate.procedure.ProcedureCallMemento; /** * Contract methods shared between {@link Session} and {@link StatelessSession}. diff --git a/hibernate-core/src/main/java/org/hibernate/cache/internal/RegionFactoryInitiator.java b/hibernate-core/src/main/java/org/hibernate/cache/internal/RegionFactoryInitiator.java index e00bbcbbb7..c0c82c2cd4 100644 --- a/hibernate-core/src/main/java/org/hibernate/cache/internal/RegionFactoryInitiator.java +++ b/hibernate-core/src/main/java/org/hibernate/cache/internal/RegionFactoryInitiator.java @@ -61,14 +61,20 @@ public class RegionFactoryInitiator implements StandardServiceInitiator anns = ( List ) defaults.get( SequenceGenerator.class ); if ( anns != null ) { @@ -231,6 +234,9 @@ public final class AnnotationBinder { } } } + + // queries ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + { List anns = ( List ) defaults.get( NamedQuery.class ); if ( anns != null ) { @@ -247,6 +253,9 @@ public final class AnnotationBinder { } } } + + // result-set-mappings ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + { List anns = ( List ) defaults.get( SqlResultSetMapping.class ); if ( anns != null ) { @@ -256,6 +265,8 @@ public final class AnnotationBinder { } } + // stored procs ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + { final List annotations = (List) defaults.get( NamedStoredProcedureQuery.class ); @@ -265,7 +276,6 @@ public final class AnnotationBinder { } } } - { final List annotations = (List) defaults.get( NamedStoredProcedureQueries.class ); diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/BinderHelper.java b/hibernate-core/src/main/java/org/hibernate/cfg/BinderHelper.java index 03aafe41db..962c868c21 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/BinderHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/BinderHelper.java @@ -341,6 +341,7 @@ public class BinderHelper { */ if ( value instanceof ToOne ) { ( (ToOne) value ).setReferencedPropertyName( syntheticPropertyName ); + ( (ToOne) value ).setReferenceToPrimaryKey( syntheticPropertyName == null ); mappings.addUniquePropertyReference( ownerEntity.getEntityName(), syntheticPropertyName ); } else if ( value instanceof Collection ) { diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java b/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java index 3b2dad3220..4247205711 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java @@ -84,6 +84,8 @@ import org.hibernate.annotations.common.reflection.java.JavaReflectionManager; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.boot.registry.internal.StandardServiceRegistryImpl; +import org.hibernate.cache.spi.GeneralDataRegion; +import org.hibernate.cfg.annotations.NamedEntityGraphDefinition; import org.hibernate.cfg.annotations.NamedProcedureCallDefinition; import org.hibernate.cfg.annotations.reflection.JPAMetadataProvider; import org.hibernate.context.spi.CurrentTenantIdentifierResolver; @@ -217,6 +219,7 @@ public class Configuration implements Serializable { protected Map namedSqlQueries; protected Map namedProcedureCallMap; protected Map sqlResultSetMappings; + protected Map namedEntityGraphMap; protected Map typeDefs; protected Map filterDefinitions; @@ -299,6 +302,7 @@ public class Configuration implements Serializable { namedQueries = new HashMap(); namedSqlQueries = new HashMap(); sqlResultSetMappings = new HashMap(); + namedEntityGraphMap = new HashMap(); typeDefs = new HashMap(); filterDefinitions = new HashMap(); @@ -2619,6 +2623,12 @@ public class Configuration implements Serializable { } } + public java.util.Collection getNamedEntityGraphs() { + return namedEntityGraphMap == null + ? Collections.emptyList() + : namedEntityGraphMap.values(); + } + // Mappings impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -2886,6 +2896,16 @@ public class Configuration implements Serializable { } } + @Override + public void addNamedEntityGraphDefintion(NamedEntityGraphDefinition definition) + throws DuplicateMappingException { + final String name = definition.getRegisteredName(); + final NamedEntityGraphDefinition previous = namedEntityGraphMap.put( name, definition ); + if ( previous != null ) { + throw new DuplicateMappingException( "NamedEntityGraph", name ); + } + } + public void addDefaultSQLQuery(String name, NamedSQLQueryDefinition query) { applySQLQuery( name, query ); defaultNamedNativeQueryNames.add( name ); diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/HbmBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/HbmBinder.java index 1e5776524b..58a905531f 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/HbmBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/HbmBinder.java @@ -1620,6 +1620,7 @@ public final class HbmBinder { if ( ukName != null ) { manyToOne.setReferencedPropertyName( ukName.getValue() ); } + manyToOne.setReferenceToPrimaryKey( manyToOne.getReferencedPropertyName() == null ); manyToOne.setReferencedEntityName( getEntityName( node, mappings ) ); @@ -1702,6 +1703,7 @@ public final class HbmBinder { Attribute ukName = node.attribute( "property-ref" ); if ( ukName != null ) oneToOne.setReferencedPropertyName( ukName.getValue() ); + oneToOne.setReferenceToPrimaryKey( oneToOne.getReferencedPropertyName() == null ); oneToOne.setPropertyName( node.attributeValue( "name" ) ); diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/Mappings.java b/hibernate-core/src/main/java/org/hibernate/cfg/Mappings.java index c3e803c625..cfeb18e6d7 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/Mappings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/Mappings.java @@ -36,6 +36,7 @@ import org.hibernate.MappingException; import org.hibernate.annotations.AnyMetaDef; import org.hibernate.annotations.common.reflection.ReflectionManager; import org.hibernate.annotations.common.reflection.XClass; +import org.hibernate.cfg.annotations.NamedEntityGraphDefinition; import org.hibernate.cfg.annotations.NamedProcedureCallDefinition; import org.hibernate.engine.ResultSetMappingDefinition; import org.hibernate.engine.spi.FilterDefinition; @@ -347,6 +348,15 @@ public interface Mappings { */ public void addNamedProcedureCallDefinition(NamedProcedureCallDefinition definition) throws DuplicateMappingException; + /** + * Adds metadata for a named entity graph to this repository + * + * @param namedEntityGraphDefinition The procedure call information + * + * @throws DuplicateMappingException If an entity graph already exists with that name. + */ + public void addNamedEntityGraphDefintion(NamedEntityGraphDefinition namedEntityGraphDefinition); + /** * Get the metadata for a named SQL result set mapping. * diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/OneToOneSecondPass.java b/hibernate-core/src/main/java/org/hibernate/cfg/OneToOneSecondPass.java index bfcfe0ec01..693821baf3 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/OneToOneSecondPass.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/OneToOneSecondPass.java @@ -39,6 +39,7 @@ import org.hibernate.mapping.ManyToOne; import org.hibernate.mapping.OneToOne; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Property; +import org.hibernate.mapping.Selectable; import org.hibernate.mapping.SimpleValue; import org.hibernate.type.ForeignKeyDirection; @@ -211,12 +212,10 @@ public class OneToOneSecondPass implements SecondPass { else { propertyHolder.addProperty( prop, inferredData.getDeclaringClass() ); } + + value.setReferencedPropertyName( mappedBy ); // HHH-6813 - // If otherSide's id is derived, do not set EntityType#uniqueKeyPropertyName. - // EntityType#isReferenceToPrimaryKey() assumes that, if it's set, - // a PK is not referenced. Example: - // // Foo: @Id long id, @OneToOne(mappedBy="foo") Bar bar // Bar: @Id @OneToOne Foo foo boolean referencesDerivedId = false; @@ -227,8 +226,14 @@ public class OneToOneSecondPass implements SecondPass { catch ( MappingException e ) { // ignore } - String referencedPropertyName = referencesDerivedId ? null : mappedBy; - value.setReferencedPropertyName( referencedPropertyName ); + boolean referenceToPrimaryKey = referencesDerivedId || mappedBy == null; + value.setReferenceToPrimaryKey( referenceToPrimaryKey ); + + // If the other side is a derived ID, prevent an infinite + // loop of attempts to resolve identifiers. + if ( referencesDerivedId ) { + ( (ManyToOne) otherSideProperty.getValue() ).setReferenceToPrimaryKey( false ); + } String propertyRef = value.getReferencedPropertyName(); if ( propertyRef != null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java index f665c3aedd..261b49ba0c 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java @@ -1511,6 +1511,7 @@ public abstract class CollectionBinder { ( (ManyToOne) value ).setReferencedPropertyName( referencedPropertyName ); mappings.addUniquePropertyReference( referencedEntity.getEntityName(), referencedPropertyName ); } + ( (ManyToOne) value ).setReferenceToPrimaryKey( referencedPropertyName == null ); value.createForeignKey(); } else { diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/EntityBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/EntityBinder.java index 1fc2e7a75f..6a66786fe9 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/EntityBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/EntityBinder.java @@ -31,6 +31,8 @@ import javax.persistence.Access; import javax.persistence.Entity; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; +import javax.persistence.NamedEntityGraph; +import javax.persistence.NamedEntityGraphs; import javax.persistence.PrimaryKeyJoinColumn; import javax.persistence.SecondaryTable; import javax.persistence.SecondaryTables; @@ -83,7 +85,6 @@ import org.hibernate.cfg.ObjectNameSource; import org.hibernate.cfg.PropertyHolder; import org.hibernate.cfg.UniqueConstraintHolder; import org.hibernate.engine.OptimisticLockStyle; -import org.hibernate.engine.internal.Versioning; import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle; import org.hibernate.engine.spi.FilterDefinition; import org.hibernate.internal.CoreMessageLogger; @@ -159,8 +160,27 @@ public class EntityBinder { this.annotatedClass = annotatedClass; bindEjb3Annotation( ejb3Ann ); bindHibernateAnnotation(); + processNamedEntityGraphs(); } + private void processNamedEntityGraphs() { + processNamedEntityGraph( annotatedClass.getAnnotation( NamedEntityGraph.class ) ); + final NamedEntityGraphs graphs = annotatedClass.getAnnotation( NamedEntityGraphs.class ); + if ( graphs != null ) { + for ( NamedEntityGraph graph : graphs.value() ) { + processNamedEntityGraph( graph ); + } + } + } + + private void processNamedEntityGraph(NamedEntityGraph annotation) { + if ( annotation == null ) { + return; + } + mappings.addNamedEntityGraphDefintion( new NamedEntityGraphDefinition( annotation, name, persistentClass.getEntityName() ) ); + } + + @SuppressWarnings("SimplifiableConditionalExpression") private void bindHibernateAnnotation() { { diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/NamedEntityGraphDefinition.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/NamedEntityGraphDefinition.java new file mode 100644 index 0000000000..4c10a940b1 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/NamedEntityGraphDefinition.java @@ -0,0 +1,59 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cfg.annotations; + +import javax.persistence.NamedEntityGraph; + +/** + * Models the definition of a {@link NamedEntityGraph} annotation + * + * @author Steve Ebersole + */ +public class NamedEntityGraphDefinition { + private final NamedEntityGraph annotation; + private final String jpaEntityName; + private final String entityName; + + public NamedEntityGraphDefinition(NamedEntityGraph annotation, String jpaEntityName, String entityName) { + this.annotation = annotation; + this.jpaEntityName = jpaEntityName; + this.entityName = entityName; + } + + public String getRegisteredName() { + return jpaEntityName; + } + + public String getJpaEntityName() { + return jpaEntityName; + } + + public String getEntityName() { + return entityName; + } + + public NamedEntityGraph getAnnotation() { + return annotation; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index e222f76af5..62cebdb5f3 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -299,6 +299,12 @@ public abstract class Dialect implements ConversionContext { // database type mapping support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + /** + * Allows the Dialect to contribute additional types + * + * @param typeContributions Callback to contribute the types + * @param serviceRegistry The service registry + */ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) { // by default, nothing to do } @@ -1362,7 +1368,7 @@ public abstract class Dialect implements ConversionContext { * @param lockOptions the lock options to apply * @return The appropriate FOR UPDATE OF column_list clause string. */ - @SuppressWarnings( {"unchecked"}) + @SuppressWarnings({"unchecked", "UnusedParameters"}) public String getForUpdateString(String aliases, LockOptions lockOptions) { LockMode lockMode = lockOptions.getLockMode(); final Iterator> itr = lockOptions.getAliasLockIterator(); @@ -1612,6 +1618,7 @@ public abstract class Dialect implements ConversionContext { * * @throws SQLException Indicates problems registering the param. */ + @SuppressWarnings("UnusedParameters") public int registerResultSetOutParameter(CallableStatement statement, String name) throws SQLException { throw new UnsupportedOperationException( getClass().getName() + @@ -1644,6 +1651,7 @@ public abstract class Dialect implements ConversionContext { * * @throws SQLException Indicates problems extracting the result set. */ + @SuppressWarnings("UnusedParameters") public ResultSet getResultSet(CallableStatement statement, int position) throws SQLException { throw new UnsupportedOperationException( getClass().getName() + " does not support resultsets via stored procedures" @@ -1661,6 +1669,7 @@ public abstract class Dialect implements ConversionContext { * * @throws SQLException Indicates problems extracting the result set. */ + @SuppressWarnings("UnusedParameters") public ResultSet getResultSet(CallableStatement statement, String name) throws SQLException { throw new UnsupportedOperationException( getClass().getName() + " does not support resultsets via stored procedures" diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/dialect/internal/DialectResolverInitiator.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/dialect/internal/DialectResolverInitiator.java index 582d095a13..befec6fe51 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/dialect/internal/DialectResolverInitiator.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/dialect/internal/DialectResolverInitiator.java @@ -40,7 +40,11 @@ import org.hibernate.service.spi.ServiceRegistryImplementor; * * @author Steve Ebersole */ +@SuppressWarnings("deprecation") public class DialectResolverInitiator implements StandardServiceInitiator { + /** + * Singleton access + */ public static final DialectResolverInitiator INSTANCE = new DialectResolverInitiator(); @Override diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/JdbcEnvironmentImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/JdbcEnvironmentImpl.java index 30e1b1ba9a..b1ec2086b1 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/JdbcEnvironmentImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/JdbcEnvironmentImpl.java @@ -45,9 +45,9 @@ import org.hibernate.engine.jdbc.env.spi.QualifiedObjectNameSupport; import org.hibernate.engine.jdbc.env.spi.SQLStateType; import org.hibernate.engine.jdbc.env.spi.SchemaNameResolver; import org.hibernate.engine.jdbc.env.spi.StandardQualifiedObjectNameSupportImpl; -import org.hibernate.engine.jdbc.internal.TypeInfo; import org.hibernate.engine.jdbc.internal.TypeInfoExtracter; import org.hibernate.engine.jdbc.spi.SqlExceptionHelper; +import org.hibernate.engine.jdbc.spi.TypeInfo; import org.hibernate.exception.internal.SQLExceptionTypeDelegate; import org.hibernate.exception.internal.SQLStateConversionDelegate; import org.hibernate.exception.internal.StandardSQLExceptionConverter; diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/spi/JdbcEnvironment.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/spi/JdbcEnvironment.java index 401597507b..8a51092567 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/spi/JdbcEnvironment.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/spi/JdbcEnvironment.java @@ -26,8 +26,8 @@ package org.hibernate.engine.jdbc.env.spi; import java.util.Set; import org.hibernate.dialect.Dialect; -import org.hibernate.engine.jdbc.internal.TypeInfo; import org.hibernate.engine.jdbc.spi.SqlExceptionHelper; +import org.hibernate.engine.jdbc.spi.TypeInfo; import org.hibernate.metamodel.spi.relational.Identifier; import org.hibernate.service.Service; diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcServicesImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcServicesImpl.java index 691536210c..f86d9689a1 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcServicesImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcServicesImpl.java @@ -70,7 +70,6 @@ public class JdbcServicesImpl implements JdbcServices, ServiceRegistryAwareServi @Override public void configure(Map configValues) { this.jdbcEnvironment = serviceRegistry.getService( JdbcEnvironment.class ); - this.connectionProvider = serviceRegistry.getService( ConnectionProvider.class ); final boolean showSQL = ConfigurationHelper.getBoolean( Environment.SHOW_SQL, configValues, false ); final boolean formatSQL = ConfigurationHelper.getBoolean( Environment.FORMAT_SQL, configValues, false ); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/TypeInfoExtracter.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/TypeInfoExtracter.java index 8b690c8ea4..7f96bb0d5c 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/TypeInfoExtracter.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/TypeInfoExtracter.java @@ -30,6 +30,9 @@ import java.util.LinkedHashSet; import org.jboss.logging.Logger; +import org.hibernate.engine.jdbc.spi.TypeInfo; +import org.hibernate.engine.jdbc.spi.TypeNullability; +import org.hibernate.engine.jdbc.spi.TypeSearchability; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.collections.ArrayHelper; diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/ConnectionObserverAdapter.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/ConnectionObserverAdapter.java index 32408525b2..0682794954 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/ConnectionObserverAdapter.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/ConnectionObserverAdapter.java @@ -26,6 +26,8 @@ package org.hibernate.engine.jdbc.spi; import java.sql.Connection; /** + * A no-op adapter for ConnectionObserver. + * * @author Steve Ebersole */ public class ConnectionObserverAdapter implements ConnectionObserver { diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/JdbcCoordinator.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/JdbcCoordinator.java index e9c8a48d45..fa0d16e6a9 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/JdbcCoordinator.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/JdbcCoordinator.java @@ -153,7 +153,8 @@ public interface JdbcCoordinator extends Serializable { * * @throws org.hibernate.TransactionException Indicates the time out period has already been exceeded. */ - public int determineRemainingTransactionTimeOutPeriod(); + public int determineRemainingTransactionTimeOutPeriod(); + /** * Register a JDBC statement. * @@ -199,9 +200,15 @@ public interface JdbcCoordinator extends Serializable { * Release all registered resources. */ public void releaseResources(); - + + /** + * Enable connection releases + */ public void enableReleases(); - + + /** + * Disable connection releases + */ public void disableReleases(); /** @@ -211,5 +218,10 @@ public interface JdbcCoordinator extends Serializable { */ public void registerLastQuery(Statement statement); + /** + * Can this coordinator be serialized? + * + * @return {@code true} indicates the coordinator can be serialized. + */ public boolean isReadyForSerialization(); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/JdbcServices.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/JdbcServices.java index a4fa8422ed..e4092dcbe5 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/JdbcServices.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/JdbcServices.java @@ -23,8 +23,6 @@ */ package org.hibernate.engine.jdbc.spi; -import java.sql.ResultSet; - import org.hibernate.dialect.Dialect; import org.hibernate.engine.jdbc.LobCreationContext; import org.hibernate.engine.jdbc.LobCreator; @@ -90,7 +88,7 @@ public interface JdbcServices extends Service { public LobCreator getLobCreator(LobCreationContext lobCreationContext); /** - * Obtain service for wrapping a {@link ResultSet} in a "column name cache" wrapper. + * Obtain service for wrapping a {@link java.sql.ResultSet} in a "column name cache" wrapper. * @return The ResultSet wrapper. */ public ResultSetWrapper getResultSetWrapper(); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/LogicalConnectionImplementor.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/LogicalConnectionImplementor.java index 7d442e7616..6d7695503f 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/LogicalConnectionImplementor.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/LogicalConnectionImplementor.java @@ -78,14 +78,35 @@ public interface LogicalConnectionImplementor extends LogicalConnection { * with which to reconnect. It is an error to pass a connection in the other strategies. */ public void manualReconnect(Connection suppliedConnection); - + + /** + * Perform an aggressive release + */ public void aggressiveRelease(); - + + /** + * Release any held connection. + * + * @throws JDBCException Indicates a problem releasing the connection + */ public void releaseConnection() throws JDBCException; + /** + * Is this logical connection in auto-commit mode? + * + * @return {@code true} if auto-commit + */ public boolean isAutoCommit(); + /** + * Callback to notify all registered observers of a connection being prepared. + */ public void notifyObserversStatementPrepared(); - + + /** + * Does this logical connection wrap a user/application supplied connection? + * + * @return {@code true} if the underlying connection was user supplied. + */ public boolean isUserSuppliedConnection(); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/ResultSetReturn.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/ResultSetReturn.java index 2dbcc27ed1..af47caf49b 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/ResultSetReturn.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/ResultSetReturn.java @@ -34,75 +34,84 @@ import java.sql.Statement; * * TODO: This could eventually utilize the new Return interface. It would be * great to have a common API shared. + * + * Generally the methods here dealing with CallableStatement are extremely limited, relying on the legacy + * * * @author Brett Meyer + * @author Steve Ebersole */ public interface ResultSetReturn { /** - * Extract the ResultSet from the statement. If user passes {@link CallableStatement} - * reference, method calls {@link #extract(CallableStatement)} internally. + * Extract the ResultSet from the PreparedStatement. + *

+ * If user passes {@link CallableStatement} reference, this method calls {@link #extract(CallableStatement)} + * internally. Otherwise, generally speaking, {@link java.sql.PreparedStatement#executeQuery()} is called * - * @param statement + * @param statement The PreparedStatement from which to extract the ResultSet * - * @return the ResultSet + * @return The extracted ResultSet */ - public ResultSet extract( PreparedStatement statement ); + public ResultSet extract(PreparedStatement statement); /** - * Extract the ResultSet from the statement. + * Extract the ResultSet from the CallableStatement. Note that this is the limited legacy form which delegates to + * {@link org.hibernate.dialect.Dialect#getResultSet}. Better option is to integrate + * {@link org.hibernate.procedure.ProcedureCall}-like hooks * - * @param statement + * @param callableStatement The CallableStatement from which to extract the ResultSet * - * @return the ResultSet + * @return The extracted ResultSet */ - public ResultSet extract( CallableStatement statement ); + public ResultSet extract(CallableStatement callableStatement); /** - * Extract the ResultSet from the statement. + * Performs the given SQL statement, expecting a ResultSet in return * - * @param statement - * @param sql + * @param statement The JDBC Statement object to use + * @param sql The SQL to execute * - * @return the ResultSet + * @return The resulting ResultSet */ - public ResultSet extract( Statement statement, String sql ); + public ResultSet extract(Statement statement, String sql); /** - * Execute the Statement query and, if results in a ResultSet, extract it. + * Execute the PreparedStatement return its first ResultSet, if any. If there is no ResultSet, returns {@code null} * - * @param statement + * @param statement The PreparedStatement to execute * - * @return the ResultSet + * @return The extracted ResultSet, or {@code null} */ - public ResultSet execute( PreparedStatement statement ); + public ResultSet execute(PreparedStatement statement); /** - * Execute the Statement query and, if results in a ResultSet, extract it. + * Performs the given SQL statement, returning its first ResultSet, if any. If there is no ResultSet, + * returns {@code null} * - * @param statement - * @param sql + * @param statement The JDBC Statement object to use + * @param sql The SQL to execute * - * @return the ResultSet + * @return The extracted ResultSet, or {@code null} */ - public ResultSet execute( Statement statement, String sql ); + public ResultSet execute(Statement statement, String sql); /** - * Execute the Statement queryUpdate. + * Execute the PreparedStatement, returning its "affected row count". * - * @param statement + * @param statement The PreparedStatement to execute * - * @return int + * @return The {@link java.sql.PreparedStatement#executeUpdate()} result */ - public int executeUpdate( PreparedStatement statement ); + public int executeUpdate(PreparedStatement statement); /** - * Execute the Statement query and, if results in a ResultSet, extract it. + * Execute the given SQL statement returning its "affected row count". * - * @param statement - * @param sql + * @param statement The JDBC Statement object to use + * @param sql The SQL to execute * - * @return the ResultSet + * @return The {@link java.sql.PreparedStatement#executeUpdate(String)} result */ - public int executeUpdate( Statement statement, String sql ); + public int executeUpdate(Statement statement, String sql); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/SqlStatementLogger.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/SqlStatementLogger.java index f656ed018c..79624ac5f8 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/SqlStatementLogger.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/SqlStatementLogger.java @@ -97,6 +97,12 @@ public class SqlStatementLogger { logStatement( statement, FormatStyle.BASIC.getFormatter() ); } + /** + * Log a SQL statement string using the specified formatter + * + * @param statement The SQL statement. + * @param formatter The formatter to use. + */ public void logStatement(String statement, Formatter formatter) { if ( format ) { if ( logToStdout || LOG.isDebugEnabled() ) { diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/StatementPreparer.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/StatementPreparer.java index bcefd9daa0..5c18468c73 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/StatementPreparer.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/StatementPreparer.java @@ -38,8 +38,6 @@ public interface StatementPreparer { /** * Create a statement. * - * @param sql The SQL the statement to be created - * * @return the statement */ public Statement createStatement(); @@ -64,14 +62,15 @@ public interface StatementPreparer { public PreparedStatement prepareStatement(String sql, boolean isCallable); /** - * Get a prepared statement to use for inserting using JDBC3 - * {@link java.sql.PreparedStatement#getGeneratedKeys getGeneratedKeys} processing. - * - * @param sql - the SQL for the statement to be prepared - * @param autoGeneratedKeys - a flag indicating whether auto-generated keys should be returned; one of

    + * Prepare an INSERT statement, specifying how auto-generated (by the database) keys should be handled. Really this + * is a boolean, but JDBC opted to define it instead using 2 int constants:
      *
    • {@link PreparedStatement#RETURN_GENERATED_KEYS}
    • *
    • {@link PreparedStatement#NO_GENERATED_KEYS}
    • - * + *
    + * Generated keys are accessed afterwards via {@link java.sql.PreparedStatement#getGeneratedKeys} + * + * @param sql The INSERT SQL + * @param autoGeneratedKeys The autoGeneratedKeys flag * * @return the prepared statement * @@ -79,10 +78,9 @@ public interface StatementPreparer { */ public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys); - /** - * Get a prepared statement to use for inserting using JDBC3 - * {@link java.sql.PreparedStatement#getGeneratedKeys getGeneratedKeys} processing. + * Prepare an INSERT statement, specifying columns which are auto-generated values to be returned. + * Generated keys are accessed afterwards via {@link java.sql.PreparedStatement#getGeneratedKeys} * * @param sql - the SQL for the statement to be prepared * @param columnNames The name of the columns to be returned in the generated keys result set. diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/TypeInfo.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/TypeInfo.java similarity index 59% rename from hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/TypeInfo.java rename to hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/TypeInfo.java index c87f1b0e7d..f9546cbfcb 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/TypeInfo.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/TypeInfo.java @@ -21,15 +21,30 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.engine.jdbc.internal; +package org.hibernate.engine.jdbc.spi; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.LinkedHashSet; + +import org.jboss.logging.Logger; + +import org.hibernate.internal.CoreMessageLogger; +import org.hibernate.internal.util.collections.ArrayHelper; /** * Models type info extracted from {@link java.sql.DatabaseMetaData#getTypeInfo()} * * @author Steve Ebersole */ +@SuppressWarnings("UnusedDeclaration") public class TypeInfo { + private static final CoreMessageLogger LOG = Logger.getMessageLogger( + CoreMessageLogger.class, + TypeInfo.class.getName() + ); + private final String typeName; private final int jdbcTypeCode; private final String[] createParams; @@ -73,6 +88,64 @@ public class TypeInfo { this.nullability = nullability; } + /** + * Extract the type information from the JDBC driver's DatabaseMetaData + * + * @param metaData The JDBC metadata + * + * @return The extracted type info + */ + public static LinkedHashSet extractTypeInfo(DatabaseMetaData metaData) { + final LinkedHashSet typeInfoSet = new LinkedHashSet(); + try { + final ResultSet resultSet = metaData.getTypeInfo(); + try { + while ( resultSet.next() ) { + typeInfoSet.add( + new TypeInfo( + resultSet.getString( "TYPE_NAME" ), + resultSet.getInt( "DATA_TYPE" ), + interpretCreateParams( resultSet.getString( "CREATE_PARAMS" ) ), + resultSet.getBoolean( "UNSIGNED_ATTRIBUTE" ), + resultSet.getInt( "PRECISION" ), + resultSet.getShort( "MINIMUM_SCALE" ), + resultSet.getShort( "MAXIMUM_SCALE" ), + resultSet.getBoolean( "FIXED_PREC_SCALE" ), + resultSet.getString( "LITERAL_PREFIX" ), + resultSet.getString( "LITERAL_SUFFIX" ), + resultSet.getBoolean( "CASE_SENSITIVE" ), + TypeSearchability.interpret( resultSet.getShort( "SEARCHABLE" ) ), + TypeNullability.interpret( resultSet.getShort( "NULLABLE" ) ) + ) + ); + } + } + catch ( SQLException e ) { + LOG.unableToAccessTypeInfoResultSet( e.toString() ); + } + finally { + try { + resultSet.close(); + } + catch ( SQLException e ) { + LOG.unableToReleaseTypeInfoResultSet(); + } + } + } + catch ( SQLException e ) { + LOG.unableToRetrieveTypeInfoResultSet( e.toString() ); + } + + return typeInfoSet; + } + + private static String[] interpretCreateParams(String value) { + if ( value == null || value.length() == 0 ) { + return ArrayHelper.EMPTY_STRING_ARRAY; + } + return value.split( "," ); + } + public String getTypeName() { return typeName; } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/TypeNullability.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/TypeNullability.java similarity index 98% rename from hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/TypeNullability.java rename to hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/TypeNullability.java index cdbfb04e7c..5f639117de 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/TypeNullability.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/TypeNullability.java @@ -21,7 +21,7 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.engine.jdbc.internal; +package org.hibernate.engine.jdbc.spi; import java.sql.DatabaseMetaData; diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/TypeSearchability.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/TypeSearchability.java similarity index 98% rename from hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/TypeSearchability.java rename to hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/TypeSearchability.java index 96aef2e42d..1df1573a0f 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/TypeSearchability.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/TypeSearchability.java @@ -21,7 +21,7 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.engine.jdbc.internal; +package org.hibernate.engine.jdbc.spi; import java.sql.DatabaseMetaData; diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jndi/JndiException.java b/hibernate-core/src/main/java/org/hibernate/engine/jndi/JndiException.java index b7047ddbc1..9ab995ec4c 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jndi/JndiException.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jndi/JndiException.java @@ -30,7 +30,13 @@ import org.hibernate.HibernateException; * @author Steve Ebersole */ public class JndiException extends HibernateException { - public JndiException(String string, Throwable root) { - super( string, root ); + /** + * Constructs a JndiException + * + * @param message Message explaining the exception condition + * @param cause The underlying cause + */ + public JndiException(String message, Throwable cause) { + super( message, cause ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jndi/JndiNameException.java b/hibernate-core/src/main/java/org/hibernate/engine/jndi/JndiNameException.java index 6114c6dbfa..90a52148db 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jndi/JndiNameException.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jndi/JndiNameException.java @@ -31,7 +31,13 @@ import org.hibernate.HibernateException; * @author Steve Ebersole */ public class JndiNameException extends HibernateException { - public JndiNameException(String string, Throwable root) { - super( string, root ); + /** + * Constructs a JndiNameException + * + * @param message Message explaining the exception condition + * @param cause The underlying cause. + */ + public JndiNameException(String message, Throwable cause) { + super( message, cause ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jndi/internal/JndiServiceImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jndi/internal/JndiServiceImpl.java index 038ed40eb1..1e64bd4697 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jndi/internal/JndiServiceImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jndi/internal/JndiServiceImpl.java @@ -25,6 +25,8 @@ package org.hibernate.engine.jndi.internal; import java.util.Hashtable; import java.util.Map; +import java.util.Properties; +import java.util.Set; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.InvalidNameException; @@ -36,11 +38,11 @@ import javax.naming.event.NamespaceChangeListener; import org.jboss.logging.Logger; +import org.hibernate.cfg.Environment; +import org.hibernate.internal.CoreMessageLogger; import org.hibernate.engine.jndi.JndiException; import org.hibernate.engine.jndi.JndiNameException; import org.hibernate.engine.jndi.spi.JndiService; -import org.hibernate.internal.CoreMessageLogger; -import org.hibernate.internal.util.jndi.JndiHelper; /** * Standard implementation of JNDI services. @@ -48,19 +50,66 @@ import org.hibernate.internal.util.jndi.JndiHelper; * @author Steve Ebersole */ public class JndiServiceImpl implements JndiService { - - private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, JndiServiceImpl.class.getName()); + private static final CoreMessageLogger LOG = Logger.getMessageLogger( + CoreMessageLogger.class, + JndiServiceImpl.class.getName() + ); private final Hashtable initialContextSettings; + /** + * Constructs a JndiServiceImpl + * + * @param configurationValues Map of configuration settings, some of which apply to JNDI support. + */ public JndiServiceImpl(Map configurationValues) { - this.initialContextSettings = JndiHelper.extractJndiProperties( configurationValues ); + this.initialContextSettings = extractJndiProperties( configurationValues ); + } + + /** + * Given a hodgepodge of properties, extract out the ones relevant for JNDI interaction. + * + * @param configurationValues The map of config values + * + * @return The extracted JNDI specific properties. + */ + @SuppressWarnings({ "unchecked" }) + public static Properties extractJndiProperties(Map configurationValues) { + final Properties jndiProperties = new Properties(); + + for ( Map.Entry entry : (Set) configurationValues.entrySet() ) { + if ( !String.class.isInstance( entry.getKey() ) ) { + continue; + } + final String propertyName = (String) entry.getKey(); + final Object propertyValue = entry.getValue(); + if ( propertyName.startsWith( Environment.JNDI_PREFIX ) ) { + // write the IntialContextFactory class and provider url to the result only if they are + // non-null; this allows the environmental defaults (if any) to remain in effect + if ( Environment.JNDI_CLASS.equals( propertyName ) ) { + if ( propertyValue != null ) { + jndiProperties.put( Context.INITIAL_CONTEXT_FACTORY, propertyValue ); + } + } + else if ( Environment.JNDI_URL.equals( propertyName ) ) { + if ( propertyValue != null ) { + jndiProperties.put( Context.PROVIDER_URL, propertyValue ); + } + } + else { + final String passThruPropertyname = propertyName.substring( Environment.JNDI_PREFIX.length() + 1 ); + jndiProperties.put( passThruPropertyname, propertyValue ); + } + } + } + + return jndiProperties; } @Override public Object locate(String jndiName) { - InitialContext initialContext = buildInitialContext(); - Name name = parseName( jndiName, initialContext ); + final InitialContext initialContext = buildInitialContext(); + final Name name = parseName( jndiName, initialContext ); try { return initialContext.lookup( name ); } @@ -104,8 +153,8 @@ public class JndiServiceImpl implements JndiService { @Override public void bind(String jndiName, Object value) { - InitialContext initialContext = buildInitialContext(); - Name name = parseName( jndiName, initialContext ); + final InitialContext initialContext = buildInitialContext(); + final Name name = parseName( jndiName, initialContext ); try { bind( name, value, initialContext ); } @@ -172,8 +221,8 @@ public class JndiServiceImpl implements JndiService { @Override public void unbind(String jndiName) { - InitialContext initialContext = buildInitialContext(); - Name name = parseName( jndiName, initialContext ); + final InitialContext initialContext = buildInitialContext(); + final Name name = parseName( jndiName, initialContext ); try { initialContext.unbind( name ); } @@ -187,8 +236,8 @@ public class JndiServiceImpl implements JndiService { @Override public void addListener(String jndiName, NamespaceChangeListener listener) { - InitialContext initialContext = buildInitialContext(); - Name name = parseName( jndiName, initialContext ); + final InitialContext initialContext = buildInitialContext(); + final Name name = parseName( jndiName, initialContext ); try { ( (EventContext) initialContext ).addNamingListener( name, EventContext.OBJECT_SCOPE, listener ); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jndi/internal/JndiServiceInitiator.java b/hibernate-core/src/main/java/org/hibernate/engine/jndi/internal/JndiServiceInitiator.java index da10bba983..304691c62b 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jndi/internal/JndiServiceInitiator.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jndi/internal/JndiServiceInitiator.java @@ -35,6 +35,9 @@ import org.hibernate.service.spi.ServiceRegistryImplementor; * @author Steve Ebersole */ public class JndiServiceInitiator implements StandardServiceInitiator { + /** + * Singleton access + */ public static final JndiServiceInitiator INSTANCE = new JndiServiceInitiator(); @Override diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jndi/internal/package-info.java b/hibernate-core/src/main/java/org/hibernate/engine/jndi/internal/package-info.java new file mode 100644 index 0000000000..5e4df30b72 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/jndi/internal/package-info.java @@ -0,0 +1,4 @@ +/** + * Internal contracts defining the JNDI support within Hibernate + */ +package org.hibernate.engine.jndi.internal; diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jndi/package-info.java b/hibernate-core/src/main/java/org/hibernate/engine/jndi/package-info.java new file mode 100644 index 0000000000..4e5148287a --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/jndi/package-info.java @@ -0,0 +1,4 @@ +/** + * Support for JNDI within Hibernate + */ +package org.hibernate.engine.jndi; diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jndi/spi/package-info.java b/hibernate-core/src/main/java/org/hibernate/engine/jndi/spi/package-info.java new file mode 100644 index 0000000000..cb001f7500 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/jndi/spi/package-info.java @@ -0,0 +1,4 @@ +/** + * The SPI contracts for Hibernate JNDI support + */ +package org.hibernate.engine.jndi.spi; diff --git a/hibernate-core/src/main/java/org/hibernate/engine/transaction/synchronization/internal/SynchronizationCallbackCoordinatorImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/transaction/synchronization/internal/SynchronizationCallbackCoordinatorImpl.java index f16e9daec3..a02e83455b 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/transaction/synchronization/internal/SynchronizationCallbackCoordinatorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/transaction/synchronization/internal/SynchronizationCallbackCoordinatorImpl.java @@ -27,6 +27,7 @@ import javax.transaction.SystemException; import org.jboss.logging.Logger; +import org.hibernate.HibernateException; import org.hibernate.TransactionException; import org.hibernate.cfg.Settings; import org.hibernate.engine.transaction.internal.jta.JtaStatusHelper; @@ -56,9 +57,9 @@ public class SynchronizationCallbackCoordinatorImpl implements SynchronizationCa private AfterCompletionAction afterCompletionAction; private ExceptionMapper exceptionMapper; - private long registrationThreadId; + private volatile long registrationThreadId; private final int NO_STATUS = -1; - private int delayedCompletionHandlingStatus; + private volatile int delayedCompletionHandlingStatus; public SynchronizationCallbackCoordinatorImpl(TransactionCoordinator transactionCoordinator) { this.transactionCoordinator = transactionCoordinator; @@ -154,11 +155,12 @@ public class SynchronizationCallbackCoordinatorImpl implements SynchronizationCa if ( delayedCompletionHandlingStatus != NO_STATUS ) { doAfterCompletion( delayedCompletionHandlingStatus ); delayedCompletionHandlingStatus = NO_STATUS; + throw new HibernateException("Transaction was rolled back in a different thread!"); } } private void doAfterCompletion(int status) { - LOG.tracev( "Transaction after completion callback [status={0}]", status ); + LOG.tracev( "Transaction afterCompletion callback [status={0}]", status ); try { afterCompletionAction.doAction( transactionCoordinator, status ); diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultInitializeCollectionEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultInitializeCollectionEventListener.java index e849d790de..dc61155973 100755 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultInitializeCollectionEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultInitializeCollectionEventListener.java @@ -74,8 +74,10 @@ public class DefaultInitializeCollectionEventListener implements InitializeColle source ); - if ( foundInCache && traceEnabled ) { - LOG.trace( "Collection initialized from cache" ); + if ( foundInCache ) { + if ( traceEnabled ) { + LOG.trace( "Collection initialized from cache" ); + } } else { if ( traceEnabled ) { diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/util/JoinProcessor.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/util/JoinProcessor.java index 25eb27ee02..bc9e7e321c 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/util/JoinProcessor.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/util/JoinProcessor.java @@ -94,6 +94,8 @@ public class JoinProcessor implements SqlTokenTypes { return JoinType.INNER_JOIN; case RIGHT_OUTER: return JoinType.RIGHT_OUTER_JOIN; + case FULL: + return JoinType.FULL_JOIN; default: throw new AssertionFailure( "undefined join type " + astJoinType ); } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/util/jndi/JndiHelper.java b/hibernate-core/src/main/java/org/hibernate/internal/util/jndi/JndiHelper.java index a93b83df39..38b9faae19 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/util/jndi/JndiHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/util/jndi/JndiHelper.java @@ -23,17 +23,16 @@ */ package org.hibernate.internal.util.jndi; -import java.util.Hashtable; -import java.util.Map; -import java.util.Properties; -import java.util.Set; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.Name; import javax.naming.NameNotFoundException; import javax.naming.NamingException; +import java.util.Hashtable; +import java.util.Map; +import java.util.Properties; -import org.hibernate.cfg.Environment; +import org.hibernate.engine.jndi.internal.JndiServiceImpl; /** * Helper for dealing with JNDI. @@ -54,42 +53,12 @@ public final class JndiHelper { */ @SuppressWarnings({ "unchecked" }) public static Properties extractJndiProperties(Map configurationValues) { - final Properties jndiProperties = new Properties(); - - for ( Map.Entry entry : (Set) configurationValues.entrySet() ) { - if ( !String.class.isInstance( entry.getKey() ) ) { - continue; - } - final String propertyName = (String) entry.getKey(); - final Object propertyValue = entry.getValue(); - if ( propertyName.startsWith( Environment.JNDI_PREFIX ) ) { - // write the IntialContextFactory class and provider url to the result only if they are - // non-null; this allows the environmental defaults (if any) to remain in effect - if ( Environment.JNDI_CLASS.equals( propertyName ) ) { - if ( propertyValue != null ) { - jndiProperties.put( Context.INITIAL_CONTEXT_FACTORY, propertyValue ); - } - } - else if ( Environment.JNDI_URL.equals( propertyName ) ) { - if ( propertyValue != null ) { - jndiProperties.put( Context.PROVIDER_URL, propertyValue ); - } - } - else { - final String passThruPropertyname = propertyName.substring( Environment.JNDI_PREFIX.length() + 1 ); - jndiProperties.put( passThruPropertyname, propertyValue ); - } - } - } - - return jndiProperties; + return JndiServiceImpl.extractJndiProperties( configurationValues ); } public static InitialContext getInitialContext(Properties props) throws NamingException { - Hashtable hash = extractJndiProperties(props); - return hash.size()==0 ? - new InitialContext() : - new InitialContext(hash); + final Hashtable hash = extractJndiProperties( props ); + return hash.size() == 0 ? new InitialContext() : new InitialContext( hash ); } /** @@ -106,26 +75,26 @@ public final class JndiHelper { ctx.rebind(name, val); } catch (Exception e) { - Name n = ctx.getNameParser("").parse(name); + Name n = ctx.getNameParser( "" ).parse( name ); while ( n.size() > 1 ) { - String ctxName = n.get(0); + final String ctxName = n.get( 0 ); - Context subctx=null; + Context subctx = null; try { - subctx = (Context) ctx.lookup(ctxName); + subctx = (Context) ctx.lookup( ctxName ); } catch (NameNotFoundException ignore) { } - if (subctx!=null) { + if ( subctx != null ) { ctx = subctx; } else { - ctx = ctx.createSubcontext(ctxName); + ctx = ctx.createSubcontext( ctxName ); } - n = n.getSuffix(1); + n = n.getSuffix( 1 ); } - ctx.rebind(n, val); + ctx.rebind( n, val ); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/internal/AbstractJoinableAssociationImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/internal/AbstractJoinableAssociationImpl.java index 74c9c31ad0..acc41f7839 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/internal/AbstractJoinableAssociationImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/internal/AbstractJoinableAssociationImpl.java @@ -59,11 +59,8 @@ public abstract class AbstractJoinableAssociationImpl implements JoinableAssocia boolean hasRestriction, Map enabledFilters) throws MappingException { this.propertyPath = currentFetch.getPropertyPath(); - final OuterJoinLoadable ownerPersister = (OuterJoinLoadable) currentFetch.getOwner().retrieveFetchSourcePersister(); - final int propertyNumber = ownerPersister.getEntityMetamodel().getPropertyIndex( currentFetch.getOwnerPropertyName() ); - final boolean isNullable = ownerPersister.isSubclassPropertyNullable( propertyNumber ); if ( currentFetch.getFetchStrategy().getStyle() == FetchStyle.JOIN ) { - joinType = isNullable ? JoinType.LEFT_OUTER_JOIN : JoinType.INNER_JOIN; + joinType = currentFetch.isNullable() ? JoinType.LEFT_OUTER_JOIN : JoinType.INNER_JOIN; } else { joinType = JoinType.NONE; diff --git a/hibernate-core/src/main/java/org/hibernate/loader/internal/LoadQueryAliasResolutionContextImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/internal/LoadQueryAliasResolutionContextImpl.java index d829669a29..193a7e42f5 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/internal/LoadQueryAliasResolutionContextImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/internal/LoadQueryAliasResolutionContextImpl.java @@ -27,7 +27,6 @@ import java.util.HashMap; import java.util.Map; import org.hibernate.cfg.NotYetImplementedException; -import org.hibernate.engine.internal.JoinHelper; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.internal.util.StringHelper; import org.hibernate.loader.CollectionAliases; @@ -36,6 +35,8 @@ import org.hibernate.loader.EntityAliases; import org.hibernate.loader.GeneratedCollectionAliases; import org.hibernate.loader.plan.spi.CollectionReference; import org.hibernate.loader.plan.spi.CollectionReturn; +import org.hibernate.loader.plan.spi.CompositeElementGraph; +import org.hibernate.loader.plan.spi.CompositeIndexGraph; import org.hibernate.loader.plan.spi.EntityReference; import org.hibernate.loader.plan.spi.EntityReturn; import org.hibernate.loader.plan.spi.Fetch; @@ -46,7 +47,6 @@ import org.hibernate.loader.spi.LoadQueryAliasResolutionContext; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.Loadable; -import org.hibernate.persister.entity.OuterJoinLoadable; import org.hibernate.type.EntityType; /** @@ -214,9 +214,18 @@ public class LoadQueryAliasResolutionContextImpl implements LoadQueryAliasResolu if ( EntityReference.class.isInstance( currentFetch.getOwner() ) ) { lhsAlias = resolveEntityTableAlias( (EntityReference) currentFetch.getOwner() ); } - else { - throw new NotYetImplementedException( "Cannot determine LHS alias for a FetchOwner that is not an EntityReference yet." ); + else if ( CompositeElementGraph.class.isInstance( currentFetch.getOwner() ) ) { + CompositeElementGraph compositeElementGraph = (CompositeElementGraph) currentFetch.getOwner(); + lhsAlias = resolveCollectionTableAlias( compositeElementGraph.getCollectionReference() ); } + else if ( CompositeIndexGraph.class.isInstance( currentFetch.getOwner() ) ) { + CompositeIndexGraph compositeIndexGraph = (CompositeIndexGraph) currentFetch.getOwner(); + lhsAlias = resolveCollectionTableAlias( compositeIndexGraph.getCollectionReference() ); + } + else { + throw new NotYetImplementedException( "Cannot determine LHS alias for FetchOwner." ); + } + final String[] aliasedLhsColumnNames = StringHelper.qualify( lhsAlias, currentFetch.getColumnNames() ); final String rhsAlias; if ( EntityReference.class.isInstance( currentFetch ) ) { rhsAlias = resolveEntityTableAlias( (EntityReference) currentFetch ); @@ -229,15 +238,6 @@ public class LoadQueryAliasResolutionContextImpl implements LoadQueryAliasResolu } // TODO: can't this be found in CollectionAliases or EntityAliases? should be moved to LoadQueryAliasResolutionContextImpl - final OuterJoinLoadable fetchSourcePersister = (OuterJoinLoadable) currentFetch.getOwner().retrieveFetchSourcePersister(); - final int propertyNumber = fetchSourcePersister.getEntityMetamodel().getPropertyIndex( currentFetch.getOwnerPropertyName() ); - final String[] aliasedLhsColumnNames = JoinHelper.getAliasedLHSColumnNames( - joinableAssociation.getAssociationType(), - lhsAlias, - propertyNumber, - fetchSourcePersister, - sessionFactory - ); aliases = new JoinableAssociationAliasesImpl( lhsAlias, aliasedLhsColumnNames, rhsAlias ); aliasesByJoinableAssociation.put( joinableAssociation, aliases ); diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/LoadPlanBuildingHelper.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/LoadPlanBuildingHelper.java index 8a645e1e2c..ef71c92038 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/LoadPlanBuildingHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/LoadPlanBuildingHelper.java @@ -33,6 +33,7 @@ import org.hibernate.loader.plan.spi.FetchOwner; import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext; import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; import org.hibernate.persister.walking.spi.CompositionDefinition; +import org.hibernate.type.EntityType; /** * @author Steve Ebersole @@ -63,6 +64,7 @@ public class LoadPlanBuildingHelper { LockMode.NONE, // todo : for now fetchOwner, attributeDefinition.getName(), + (EntityType) attributeDefinition.getType(), fetchStrategy ); } @@ -73,7 +75,7 @@ public class LoadPlanBuildingHelper { LoadPlanBuildingContext loadPlanBuildingContext) { return new CompositeFetch( loadPlanBuildingContext.getSessionFactory(), - (AbstractFetchOwner) fetchOwner, + fetchOwner, attributeDefinition.getName() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractFetchOwner.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractFetchOwner.java index ffb5656f12..2e6293c1b5 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractFetchOwner.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractFetchOwner.java @@ -27,20 +27,24 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import org.hibernate.LockMode; +import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.loader.plan.internal.LoadPlanBuildingHelper; +import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext; +import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; +import org.hibernate.persister.walking.spi.CompositionDefinition; +import org.hibernate.type.Type; /** * @author Steve Ebersole + * @author Gail Badner */ public abstract class AbstractFetchOwner extends AbstractPlanNode implements FetchOwner { - private final LockMode lockMode; private List fetches; - public AbstractFetchOwner(SessionFactoryImplementor factory, LockMode lockMode) { + public AbstractFetchOwner(SessionFactoryImplementor factory) { super( factory ); - this.lockMode = lockMode; validate(); } @@ -50,11 +54,10 @@ public abstract class AbstractFetchOwner extends AbstractPlanNode implements Fet /** * A "copy" constructor. Used while making clones/copies of this. * - * @param original + * @param original - the original object to copy. */ protected AbstractFetchOwner(AbstractFetchOwner original, CopyContext copyContext) { super( original ); - this.lockMode = original.lockMode; validate(); copyContext.getReturnGraphVisitationStrategy().startingFetches( original ); @@ -62,6 +65,7 @@ public abstract class AbstractFetchOwner extends AbstractPlanNode implements Fet this.fetches = Collections.emptyList(); } else { + // TODO: don't think this is correct... List fetchesCopy = new ArrayList(); for ( Fetch fetch : fetches ) { fetchesCopy.add( fetch.makeCopy( copyContext, this ) ); @@ -71,11 +75,6 @@ public abstract class AbstractFetchOwner extends AbstractPlanNode implements Fet copyContext.getReturnGraphVisitationStrategy().finishingFetches( original ); } - public LockMode getLockMode() { - return lockMode; - } - - @Override public void addFetch(Fetch fetch) { if ( fetch.getOwner() != this ) { throw new IllegalArgumentException( "Fetch and owner did not match" ); @@ -92,4 +91,55 @@ public abstract class AbstractFetchOwner extends AbstractPlanNode implements Fet public Fetch[] getFetches() { return fetches == null ? NO_FETCHES : fetches.toArray( new Fetch[ fetches.size() ] ); } + + protected abstract FetchOwnerDelegate getFetchOwnerDelegate(); + + @Override + public boolean isNullable(Fetch fetch) { + return getFetchOwnerDelegate().isNullable( fetch ); + } + + @Override + public Type getType(Fetch fetch) { + return getFetchOwnerDelegate().getType( fetch ); + } + + @Override + public String[] getColumnNames(Fetch fetch) { + return getFetchOwnerDelegate().getColumnNames( fetch ); + } + + @Override + public CollectionFetch buildCollectionFetch( + AssociationAttributeDefinition attributeDefinition, + FetchStrategy fetchStrategy, + LoadPlanBuildingContext loadPlanBuildingContext) { + return LoadPlanBuildingHelper.buildStandardCollectionFetch( + this, + attributeDefinition, + fetchStrategy, + loadPlanBuildingContext + ); + } + + @Override + public EntityFetch buildEntityFetch( + AssociationAttributeDefinition attributeDefinition, + FetchStrategy fetchStrategy, + LoadPlanBuildingContext loadPlanBuildingContext) { + return LoadPlanBuildingHelper.buildStandardEntityFetch( + this, + attributeDefinition, + fetchStrategy, + loadPlanBuildingContext + ); + } + + @Override + public CompositeFetch buildCompositeFetch( + CompositionDefinition attributeDefinition, + LoadPlanBuildingContext loadPlanBuildingContext) { + return LoadPlanBuildingHelper.buildStandardCompositeFetch( this, attributeDefinition, loadPlanBuildingContext ); + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractSingularAttributeFetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractSingularAttributeFetch.java index ddb4b4491f..0b3689844d 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractSingularAttributeFetch.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractSingularAttributeFetch.java @@ -24,7 +24,6 @@ package org.hibernate.loader.plan.spi; import org.hibernate.HibernateException; -import org.hibernate.LockMode; import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -42,11 +41,10 @@ public abstract class AbstractSingularAttributeFetch extends AbstractFetchOwner public AbstractSingularAttributeFetch( SessionFactoryImplementor factory, - LockMode lockMode, FetchOwner owner, String ownerProperty, FetchStrategy fetchStrategy) { - super( factory, lockMode ); + super( factory ); this.owner = owner; this.ownerProperty = ownerProperty; this.fetchStrategy = fetchStrategy; @@ -77,6 +75,16 @@ public abstract class AbstractSingularAttributeFetch extends AbstractFetchOwner return ownerProperty; } + @Override + public boolean isNullable() { + return owner.isNullable( this ); + } + + @Override + public String[] getColumnNames() { + return owner.getColumnNames( this ); + } + @Override public FetchStrategy getFetchStrategy() { return fetchStrategy; diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionFetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionFetch.java index fef9cd1b65..242daf427c 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionFetch.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionFetch.java @@ -28,8 +28,10 @@ import java.sql.SQLException; import org.hibernate.LockMode; import org.hibernate.engine.FetchStrategy; +import org.hibernate.engine.internal.JoinHelper; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.loader.spi.ResultSetProcessingContext; +import org.hibernate.persister.entity.Joinable; import org.hibernate.type.CollectionType; /** @@ -74,6 +76,16 @@ public class CollectionFetch extends AbstractCollectionReference implements Fetc return getPropertyPath().getProperty(); } + @Override + public boolean isNullable() { + return true; + } + + @Override + public String[] getColumnNames() { + return getOwner().getColumnNames( this ); + } + @Override public FetchStrategy getFetchStrategy() { return fetchStrategy; diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeElementGraph.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeElementGraph.java index b1ddd79d30..e2d23540df 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeElementGraph.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeElementGraph.java @@ -1,29 +1,24 @@ package org.hibernate.loader.plan.spi; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - import org.hibernate.HibernateException; import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.loader.PropertyPath; -import org.hibernate.loader.plan.internal.LoadPlanBuildingHelper; import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext; import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.persister.collection.QueryableCollection; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; -import org.hibernate.persister.walking.spi.CompositionDefinition; +import org.hibernate.type.CompositeType; /** * @author Steve Ebersole */ -public class CompositeElementGraph extends AbstractPlanNode implements FetchableCollectionElement { +public class CompositeElementGraph extends AbstractFetchOwner implements FetchableCollectionElement { private final CollectionReference collectionReference; private final PropertyPath propertyPath; private final CollectionPersister collectionPersister; - - private List fetches; + private final FetchOwnerDelegate fetchOwnerDelegate; public CompositeElementGraph( SessionFactoryImplementor sessionFactory, @@ -34,39 +29,24 @@ public class CompositeElementGraph extends AbstractPlanNode implements Fetchable this.collectionReference = collectionReference; this.collectionPersister = collectionReference.getCollectionPersister(); this.propertyPath = collectionPath.append( "" ); + this.fetchOwnerDelegate = new CompositeFetchOwnerDelegate( + sessionFactory, + (CompositeType) collectionPersister.getElementType(), + ( (QueryableCollection) collectionPersister ).getElementColumnNames() + ); } public CompositeElementGraph(CompositeElementGraph original, CopyContext copyContext) { - super( original ); + super( original, copyContext ); this.collectionReference = original.collectionReference; this.collectionPersister = original.collectionPersister; this.propertyPath = original.propertyPath; - - copyContext.getReturnGraphVisitationStrategy().startingFetches( original ); - if ( fetches == null || fetches.size() == 0 ) { - this.fetches = Collections.emptyList(); - } - else { - List fetchesCopy = new ArrayList(); - for ( Fetch fetch : fetches ) { - fetchesCopy.add( fetch.makeCopy( copyContext, this ) ); - } - this.fetches = fetchesCopy; - } - copyContext.getReturnGraphVisitationStrategy().finishingFetches( original ); + this.fetchOwnerDelegate = original.fetchOwnerDelegate; } @Override - public void addFetch(Fetch fetch) { - if ( fetches == null ) { - fetches = new ArrayList(); - } - fetches.add( fetch ); - } - - @Override - public Fetch[] getFetches() { - return fetches == null ? NO_FETCHES : fetches.toArray( new Fetch[ fetches.size() ] ); + public CollectionReference getCollectionReference() { + return collectionReference; } @Override @@ -83,6 +63,16 @@ public class CompositeElementGraph extends AbstractPlanNode implements Fetchable return propertyPath; } + @Override + public CompositeElementGraph makeCopy(CopyContext copyContext) { + return new CompositeElementGraph( this, copyContext ); + } + + @Override + protected FetchOwnerDelegate getFetchOwnerDelegate() { + return fetchOwnerDelegate; + } + @Override public CollectionFetch buildCollectionFetch( AssociationAttributeDefinition attributeDefinition, @@ -90,29 +80,4 @@ public class CompositeElementGraph extends AbstractPlanNode implements Fetchable LoadPlanBuildingContext loadPlanBuildingContext) { throw new HibernateException( "Collection composite element cannot define collections" ); } - - @Override - public EntityFetch buildEntityFetch( - AssociationAttributeDefinition attributeDefinition, - FetchStrategy fetchStrategy, - LoadPlanBuildingContext loadPlanBuildingContext) { - return LoadPlanBuildingHelper.buildStandardEntityFetch( - this, - attributeDefinition, - fetchStrategy, - loadPlanBuildingContext - ); - } - - @Override - public CompositeFetch buildCompositeFetch( - CompositionDefinition attributeDefinition, - LoadPlanBuildingContext loadPlanBuildingContext) { - return LoadPlanBuildingHelper.buildStandardCompositeFetch( this, attributeDefinition, loadPlanBuildingContext ); - } - - @Override - public CompositeElementGraph makeCopy(CopyContext copyContext) { - return new CompositeElementGraph( this, copyContext ); - } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeFetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeFetch.java index 0d1a9fddd0..c1c043308c 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeFetch.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeFetch.java @@ -25,17 +25,17 @@ package org.hibernate.loader.plan.spi; import java.sql.ResultSet; import java.sql.SQLException; - -import org.hibernate.LockMode; import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.loader.plan.internal.LoadPlanBuildingHelper; import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext; import org.hibernate.loader.spi.ResultSetProcessingContext; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; import org.hibernate.persister.walking.spi.CompositionDefinition; +import org.hibernate.type.CompositeType; /** * @author Steve Ebersole @@ -43,15 +43,28 @@ import org.hibernate.persister.walking.spi.CompositionDefinition; public class CompositeFetch extends AbstractSingularAttributeFetch { public static final FetchStrategy FETCH_PLAN = new FetchStrategy( FetchTiming.IMMEDIATE, FetchStyle.JOIN ); + private final FetchOwnerDelegate delegate; + public CompositeFetch( SessionFactoryImplementor sessionFactory, FetchOwner owner, String ownerProperty) { - super( sessionFactory, LockMode.NONE, owner, ownerProperty, FETCH_PLAN ); + super( sessionFactory, owner, ownerProperty, FETCH_PLAN ); + this.delegate = new CompositeFetchOwnerDelegate( + sessionFactory, + (CompositeType) getOwner().getType( this ), + getOwner().getColumnNames( this ) + ); } public CompositeFetch(CompositeFetch original, CopyContext copyContext, FetchOwner fetchOwnerCopy) { super( original, copyContext, fetchOwnerCopy ); + this.delegate = original.getFetchOwnerDelegate(); + } + + @Override + protected FetchOwnerDelegate getFetchOwnerDelegate() { + return delegate; } @Override @@ -72,13 +85,18 @@ public class CompositeFetch extends AbstractSingularAttributeFetch { AssociationAttributeDefinition attributeDefinition, FetchStrategy fetchStrategy, LoadPlanBuildingContext loadPlanBuildingContext) { - return null; //To change body of implemented methods use File | Settings | File Templates. + return LoadPlanBuildingHelper.buildStandardEntityFetch( + this, + attributeDefinition, + fetchStrategy, + loadPlanBuildingContext + ); } @Override public CompositeFetch buildCompositeFetch( CompositionDefinition attributeDefinition, LoadPlanBuildingContext loadPlanBuildingContext) { - return null; //To change body of implemented methods use File | Settings | File Templates. + return LoadPlanBuildingHelper.buildStandardCompositeFetch( this, attributeDefinition, loadPlanBuildingContext ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeFetchOwnerDelegate.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeFetchOwnerDelegate.java new file mode 100644 index 0000000000..8c530eb5d2 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeFetchOwnerDelegate.java @@ -0,0 +1,98 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.loader.plan.spi; + +import java.util.Arrays; + +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.internal.util.collections.ArrayHelper; +import org.hibernate.persister.walking.spi.WalkingException; +import org.hibernate.type.CompositeType; +import org.hibernate.type.Type; + +/** + * @author Gail Badner + */ +public class CompositeFetchOwnerDelegate implements FetchOwnerDelegate { + private final SessionFactoryImplementor sessionFactory; + private final CompositeType compositeType; + private final String[] columnNames; + + public CompositeFetchOwnerDelegate( + SessionFactoryImplementor sessionFactory, + CompositeType compositeType, + String[] columnNames) { + this.sessionFactory = sessionFactory; + this.compositeType = compositeType; + this.columnNames = columnNames; + } + + @Override + public boolean isNullable(Fetch fetch) { + return compositeType.getPropertyNullability()[ determinePropertyIndex( fetch ) ]; + } + + @Override + public Type getType(Fetch fetch) { + return compositeType.getSubtypes()[ determinePropertyIndex( fetch ) ]; + } + + @Override + public String[] getColumnNames(Fetch fetch) { + // TODO: probably want to cache this + int begin = 0; + String[] subColumnNames = null; + for ( int i = 0; i < compositeType.getSubtypes().length; i++ ) { + final int columnSpan = compositeType.getSubtypes()[i].getColumnSpan( sessionFactory ); + subColumnNames = ArrayHelper.slice( columnNames, begin, columnSpan ); + if ( compositeType.getPropertyNames()[ i ].equals( fetch.getOwnerPropertyName() ) ) { + break; + } + begin += columnSpan; + } + return subColumnNames; + } + + private int determinePropertyIndex(Fetch fetch) { + // TODO: probably want to cache this + final String[] subAttributeNames = compositeType.getPropertyNames(); + int subAttributeIndex = -1; + for ( int i = 0; i < subAttributeNames.length ; i++ ) { + if ( subAttributeNames[ i ].equals( fetch.getOwnerPropertyName() ) ) { + subAttributeIndex = i; + break; + } + } + if ( subAttributeIndex == -1 ) { + throw new WalkingException( + String.format( + "Owner property [%s] not found in composite properties [%s]", + fetch.getOwnerPropertyName(), + Arrays.asList( subAttributeNames ) + ) + ); + } + return subAttributeIndex; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeIndexGraph.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeIndexGraph.java index bccc7b1cd7..f4f4eb7fe4 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeIndexGraph.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeIndexGraph.java @@ -1,29 +1,24 @@ package org.hibernate.loader.plan.spi; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - import org.hibernate.HibernateException; import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.loader.PropertyPath; -import org.hibernate.loader.plan.internal.LoadPlanBuildingHelper; import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext; import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.persister.collection.QueryableCollection; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; -import org.hibernate.persister.walking.spi.CompositionDefinition; +import org.hibernate.type.CompositeType; /** * @author Steve Ebersole */ -public class CompositeIndexGraph extends AbstractPlanNode implements FetchableCollectionIndex { +public class CompositeIndexGraph extends AbstractFetchOwner implements FetchableCollectionIndex { private final CollectionReference collectionReference; private final PropertyPath propertyPath; private final CollectionPersister collectionPersister; - - private List fetches; + private final FetchOwnerDelegate fetchOwnerDelegate; public CompositeIndexGraph( SessionFactoryImplementor sessionFactory, @@ -33,39 +28,19 @@ public class CompositeIndexGraph extends AbstractPlanNode implements FetchableCo this.collectionReference = collectionReference; this.collectionPersister = collectionReference.getCollectionPersister(); this.propertyPath = propertyPath.append( "" ); + this.fetchOwnerDelegate = new CompositeFetchOwnerDelegate( + sessionFactory, + (CompositeType) collectionPersister.getIndexType(), + ( (QueryableCollection) collectionPersister ).getIndexColumnNames() + ); } protected CompositeIndexGraph(CompositeIndexGraph original, CopyContext copyContext) { - super( original ); + super( original, copyContext ); this.collectionReference = original.collectionReference; this.collectionPersister = original.collectionPersister; this.propertyPath = original.propertyPath; - - copyContext.getReturnGraphVisitationStrategy().startingFetches( original ); - if ( fetches == null || fetches.size() == 0 ) { - this.fetches = Collections.emptyList(); - } - else { - List fetchesCopy = new ArrayList(); - for ( Fetch fetch : fetches ) { - fetchesCopy.add( fetch.makeCopy( copyContext, this ) ); - } - this.fetches = fetchesCopy; - } - copyContext.getReturnGraphVisitationStrategy().finishingFetches( original ); - } - - @Override - public void addFetch(Fetch fetch) { - if ( fetches == null ) { - fetches = new ArrayList(); - } - fetches.add( fetch ); - } - - @Override - public Fetch[] getFetches() { - return fetches == null ? NO_FETCHES : fetches.toArray( new Fetch[ fetches.size() ] ); + this.fetchOwnerDelegate = original.fetchOwnerDelegate; } @Override @@ -77,12 +52,25 @@ public class CompositeIndexGraph extends AbstractPlanNode implements FetchableCo return collectionPersister.getOwnerEntityPersister(); } + public CollectionReference getCollectionReference() { + return collectionReference; + } + @Override public PropertyPath getPropertyPath() { return propertyPath; } @Override + public CompositeIndexGraph makeCopy(CopyContext copyContext) { + return new CompositeIndexGraph( this, copyContext ); + } + + @Override + protected FetchOwnerDelegate getFetchOwnerDelegate() { + return fetchOwnerDelegate; + } + public CollectionFetch buildCollectionFetch( AssociationAttributeDefinition attributeDefinition, FetchStrategy fetchStrategy, @@ -90,28 +78,4 @@ public class CompositeIndexGraph extends AbstractPlanNode implements FetchableCo throw new HibernateException( "Composite index cannot define collections" ); } - @Override - public EntityFetch buildEntityFetch( - AssociationAttributeDefinition attributeDefinition, - FetchStrategy fetchStrategy, - LoadPlanBuildingContext loadPlanBuildingContext) { - return LoadPlanBuildingHelper.buildStandardEntityFetch( - this, - attributeDefinition, - fetchStrategy, - loadPlanBuildingContext - ); - } - - @Override - public CompositeFetch buildCompositeFetch( - CompositionDefinition attributeDefinition, - LoadPlanBuildingContext loadPlanBuildingContext) { - return LoadPlanBuildingHelper.buildStandardCompositeFetch( this, attributeDefinition, loadPlanBuildingContext ); - } - - @Override - public CompositeIndexGraph makeCopy(CopyContext copyContext) { - return new CompositeIndexGraph( this, copyContext ); - } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityElementGraph.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityElementGraph.java index d779a119ef..a3ccde17d7 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityElementGraph.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityElementGraph.java @@ -1,32 +1,23 @@ package org.hibernate.loader.plan.spi; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - import org.hibernate.LockMode; import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.loader.PropertyPath; -import org.hibernate.loader.plan.internal.LoadPlanBuildingHelper; -import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; -import org.hibernate.persister.walking.spi.CompositionDefinition; import org.hibernate.type.AssociationType; /** * @author Steve Ebersole */ -public class EntityElementGraph extends AbstractPlanNode implements FetchableCollectionElement, EntityReference { +public class EntityElementGraph extends AbstractFetchOwner implements FetchableCollectionElement, EntityReference { private final CollectionReference collectionReference; private final CollectionPersister collectionPersister; private final AssociationType elementType; private final EntityPersister elementPersister; private final PropertyPath propertyPath; - - private List fetches; + private final FetchOwnerDelegate fetchOwnerDelegate; private IdentifierDescription identifierDescription; @@ -40,30 +31,19 @@ public class EntityElementGraph extends AbstractPlanNode implements FetchableCol this.collectionPersister = collectionReference.getCollectionPersister(); this.elementType = (AssociationType) collectionPersister.getElementType(); this.elementPersister = (EntityPersister) this.elementType.getAssociatedJoinable( sessionFactory() ); - this.propertyPath = collectionPath.append( "" ); + this.propertyPath = collectionPath; + this.fetchOwnerDelegate = new EntityFetchOwnerDelegate( elementPersister ); } public EntityElementGraph(EntityElementGraph original, CopyContext copyContext) { - super( original ); + super( original, copyContext ); this.collectionReference = original.collectionReference; this.collectionPersister = original.collectionReference.getCollectionPersister(); this.elementType = original.elementType; this.elementPersister = original.elementPersister; this.propertyPath = original.propertyPath; - - copyContext.getReturnGraphVisitationStrategy().startingFetches( original ); - if ( fetches == null || fetches.size() == 0 ) { - this.fetches = Collections.emptyList(); - } - else { - List fetchesCopy = new ArrayList(); - for ( Fetch fetch : fetches ) { - fetchesCopy.add( fetch.makeCopy( copyContext, this ) ); - } - this.fetches = fetchesCopy; - } - copyContext.getReturnGraphVisitationStrategy().finishingFetches( original ); + this.fetchOwnerDelegate = original.fetchOwnerDelegate; } @Override @@ -86,19 +66,6 @@ public class EntityElementGraph extends AbstractPlanNode implements FetchableCol return identifierDescription; } - @Override - public void addFetch(Fetch fetch) { - if ( fetches == null ) { - fetches = new ArrayList(); - } - fetches.add( fetch ); - } - - @Override - public Fetch[] getFetches() { - return fetches == null ? NO_FETCHES : fetches.toArray( new Fetch[ fetches.size() ] ); - } - @Override public void validateFetchPlan(FetchStrategy fetchStrategy) { } @@ -113,39 +80,6 @@ public class EntityElementGraph extends AbstractPlanNode implements FetchableCol return propertyPath; } - @Override - public CollectionFetch buildCollectionFetch( - AssociationAttributeDefinition attributeDefinition, - FetchStrategy fetchStrategy, - LoadPlanBuildingContext loadPlanBuildingContext) { - return LoadPlanBuildingHelper.buildStandardCollectionFetch( - this, - attributeDefinition, - fetchStrategy, - loadPlanBuildingContext - ); - } - - @Override - public EntityFetch buildEntityFetch( - AssociationAttributeDefinition attributeDefinition, - FetchStrategy fetchStrategy, - LoadPlanBuildingContext loadPlanBuildingContext) { - return LoadPlanBuildingHelper.buildStandardEntityFetch( - this, - attributeDefinition, - fetchStrategy, - loadPlanBuildingContext - ); - } - - @Override - public CompositeFetch buildCompositeFetch( - CompositionDefinition attributeDefinition, - LoadPlanBuildingContext loadPlanBuildingContext) { - return LoadPlanBuildingHelper.buildStandardCompositeFetch( this, attributeDefinition, loadPlanBuildingContext ); - } - @Override public void injectIdentifierDescription(IdentifierDescription identifierDescription) { this.identifierDescription = identifierDescription; @@ -156,8 +90,18 @@ public class EntityElementGraph extends AbstractPlanNode implements FetchableCol return new EntityElementGraph( this, copyContext ); } + @Override + public CollectionReference getCollectionReference() { + return collectionReference; + } + @Override public String toString() { return "EntityElementGraph(collection=" + collectionPersister.getRole() + ", type=" + elementPersister.getEntityName() + ")"; } + + @Override + protected FetchOwnerDelegate getFetchOwnerDelegate() { + return fetchOwnerDelegate; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityFetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityFetch.java index 444e80cdc1..9842166561 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityFetch.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityFetch.java @@ -32,21 +32,19 @@ import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.loader.plan.internal.LoadPlanBuildingHelper; -import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext; import org.hibernate.loader.spi.ResultSetProcessingContext; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; -import org.hibernate.persister.walking.spi.CompositionDefinition; import org.hibernate.type.EntityType; /** * @author Steve Ebersole */ -public class EntityFetch extends AbstractSingularAttributeFetch implements EntityReference { +public class EntityFetch extends AbstractSingularAttributeFetch implements EntityReference, Fetch { private final EntityType associationType; private final EntityPersister persister; + private final LockMode lockMode; + private final FetchOwnerDelegate fetchOwnerDelegate; private IdentifierDescription identifierDescription; @@ -55,11 +53,14 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit LockMode lockMode, FetchOwner owner, String ownerProperty, + EntityType entityType, FetchStrategy fetchStrategy) { - super( sessionFactory, lockMode, owner, ownerProperty, fetchStrategy ); + super( sessionFactory, owner, ownerProperty, fetchStrategy ); - this.associationType = (EntityType) owner.retrieveFetchSourcePersister().getPropertyType( ownerProperty ); + this.associationType = entityType; this.persister = sessionFactory.getEntityPersister( associationType.getAssociatedEntityName() ); + this.lockMode = lockMode; + this.fetchOwnerDelegate = new EntityFetchOwnerDelegate( persister ); } /** @@ -72,6 +73,8 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit super( original, copyContext, fetchOwnerCopy ); this.associationType = original.associationType; this.persister = original.persister; + this.lockMode = original.lockMode; + this.fetchOwnerDelegate = original.fetchOwnerDelegate; } public EntityType getAssociationType() { @@ -93,45 +96,16 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit return identifierDescription; } + @Override + public LockMode getLockMode() { + return lockMode; + } + @Override public EntityPersister retrieveFetchSourcePersister() { return persister; } - - @Override - public CollectionFetch buildCollectionFetch( - AssociationAttributeDefinition attributeDefinition, - FetchStrategy fetchStrategy, - LoadPlanBuildingContext loadPlanBuildingContext) { - return LoadPlanBuildingHelper.buildStandardCollectionFetch( - this, - attributeDefinition, - fetchStrategy, - loadPlanBuildingContext - ); - } - - @Override - public EntityFetch buildEntityFetch( - AssociationAttributeDefinition attributeDefinition, - FetchStrategy fetchStrategy, - LoadPlanBuildingContext loadPlanBuildingContext) { - return LoadPlanBuildingHelper.buildStandardEntityFetch( - this, - attributeDefinition, - fetchStrategy, - loadPlanBuildingContext - ); - } - - @Override - public CompositeFetch buildCompositeFetch( - CompositionDefinition attributeDefinition, - LoadPlanBuildingContext loadPlanBuildingContext) { - return LoadPlanBuildingHelper.buildStandardCompositeFetch( this, attributeDefinition, loadPlanBuildingContext ); - } - @Override public void injectIdentifierDescription(IdentifierDescription identifierDescription) { this.identifierDescription = identifierDescription; @@ -267,4 +241,9 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit copyContext.getReturnGraphVisitationStrategy().finishingEntityFetch( this ); return copy; } + + @Override + protected FetchOwnerDelegate getFetchOwnerDelegate() { + return fetchOwnerDelegate; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityFetchOwnerDelegate.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityFetchOwnerDelegate.java new file mode 100644 index 0000000000..7ef801df07 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityFetchOwnerDelegate.java @@ -0,0 +1,72 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.loader.plan.spi; + +import org.hibernate.engine.internal.JoinHelper; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.entity.OuterJoinLoadable; +import org.hibernate.type.AssociationType; +import org.hibernate.type.Type; + +/** + * @author Gail Badner + */ +public class EntityFetchOwnerDelegate implements FetchOwnerDelegate { + private final EntityPersister entityPersister; + + public EntityFetchOwnerDelegate(EntityPersister entityPersister) { + this.entityPersister = entityPersister; + } + + @Override + public boolean isNullable(Fetch fetch) { + return entityPersister.getPropertyNullability()[ determinePropertyIndex( fetch ) ]; + } + + @Override + public Type getType(Fetch fetch) { + return entityPersister.getPropertyTypes()[ determinePropertyIndex( fetch ) ]; + } + + @Override + public String[] getColumnNames(Fetch fetch) { + final OuterJoinLoadable outerJoinLoadable = (OuterJoinLoadable) entityPersister; + Type fetchType = getType( fetch ); + if ( fetchType.isAssociationType() ) { + return JoinHelper.getLHSColumnNames( + (AssociationType) fetchType, + determinePropertyIndex( fetch ), + outerJoinLoadable, + outerJoinLoadable.getFactory() + ); + } + else { + return outerJoinLoadable.getPropertyColumnNames( determinePropertyIndex( fetch ) ); + } + } + + private int determinePropertyIndex(Fetch fetch) { + return entityPersister.getEntityMetamodel().getPropertyIndex( fetch.getOwnerPropertyName() ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityIndexGraph.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityIndexGraph.java index ed12d31a99..384b8e98d4 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityIndexGraph.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityIndexGraph.java @@ -23,33 +23,24 @@ */ package org.hibernate.loader.plan.spi; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - import org.hibernate.LockMode; import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.loader.PropertyPath; -import org.hibernate.loader.plan.internal.LoadPlanBuildingHelper; -import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; -import org.hibernate.persister.walking.spi.CompositionDefinition; import org.hibernate.type.AssociationType; /** * @author Steve Ebersole */ -public class EntityIndexGraph extends AbstractPlanNode implements FetchableCollectionIndex, EntityReference { +public class EntityIndexGraph extends AbstractFetchOwner implements FetchableCollectionIndex, EntityReference { private final CollectionReference collectionReference; private final CollectionPersister collectionPersister; private final AssociationType indexType; private final EntityPersister indexPersister; private final PropertyPath propertyPath; - - private List fetches; + private final FetchOwnerDelegate fetchOwnerDelegate; private IdentifierDescription identifierDescription; @@ -63,28 +54,17 @@ public class EntityIndexGraph extends AbstractPlanNode implements FetchableColle this.indexType = (AssociationType) collectionPersister.getIndexType(); this.indexPersister = (EntityPersister) this.indexType.getAssociatedJoinable( sessionFactory() ); this.propertyPath = collectionPath.append( "" ); // todo : do we want the part? + this.fetchOwnerDelegate = new EntityFetchOwnerDelegate( indexPersister ); } public EntityIndexGraph(EntityIndexGraph original, CopyContext copyContext) { - super( original ); + super( original, copyContext ); this.collectionReference = original.collectionReference; this.collectionPersister = original.collectionReference.getCollectionPersister(); this.indexType = original.indexType; this.indexPersister = original.indexPersister; this.propertyPath = original.propertyPath; - - copyContext.getReturnGraphVisitationStrategy().startingFetches( original ); - if ( fetches == null || fetches.size() == 0 ) { - this.fetches = Collections.emptyList(); - } - else { - List fetchesCopy = new ArrayList(); - for ( Fetch fetch : fetches ) { - fetchesCopy.add( fetch.makeCopy( copyContext, this ) ); - } - this.fetches = fetchesCopy; - } - copyContext.getReturnGraphVisitationStrategy().finishingFetches( original ); + this.fetchOwnerDelegate = original.fetchOwnerDelegate; } @Override @@ -107,19 +87,6 @@ public class EntityIndexGraph extends AbstractPlanNode implements FetchableColle return identifierDescription; } - @Override - public void addFetch(Fetch fetch) { - if ( fetches == null ) { - fetches = new ArrayList(); - } - fetches.add( fetch ); - } - - @Override - public Fetch[] getFetches() { - return fetches == null ? NO_FETCHES : fetches.toArray( new Fetch[ fetches.size() ] ); - } - @Override public void validateFetchPlan(FetchStrategy fetchStrategy) { } @@ -134,39 +101,6 @@ public class EntityIndexGraph extends AbstractPlanNode implements FetchableColle return propertyPath; } - @Override - public CollectionFetch buildCollectionFetch( - AssociationAttributeDefinition attributeDefinition, - FetchStrategy fetchStrategy, - LoadPlanBuildingContext loadPlanBuildingContext) { - return LoadPlanBuildingHelper.buildStandardCollectionFetch( - this, - attributeDefinition, - fetchStrategy, - loadPlanBuildingContext - ); - } - - @Override - public EntityFetch buildEntityFetch( - AssociationAttributeDefinition attributeDefinition, - FetchStrategy fetchStrategy, - LoadPlanBuildingContext loadPlanBuildingContext) { - return LoadPlanBuildingHelper.buildStandardEntityFetch( - this, - attributeDefinition, - fetchStrategy, - loadPlanBuildingContext - ); - } - - @Override - public CompositeFetch buildCompositeFetch( - CompositionDefinition attributeDefinition, - LoadPlanBuildingContext loadPlanBuildingContext) { - return LoadPlanBuildingHelper.buildStandardCompositeFetch( this, attributeDefinition, loadPlanBuildingContext ); - } - @Override public void injectIdentifierDescription(IdentifierDescription identifierDescription) { this.identifierDescription = identifierDescription; @@ -176,4 +110,9 @@ public class EntityIndexGraph extends AbstractPlanNode implements FetchableColle public EntityIndexGraph makeCopy(CopyContext copyContext) { return new EntityIndexGraph( this, copyContext ); } + + @Override + protected FetchOwnerDelegate getFetchOwnerDelegate() { + return fetchOwnerDelegate; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReturn.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReturn.java index 2d3bb88b52..7680e4a30f 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReturn.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReturn.java @@ -32,12 +32,8 @@ import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.loader.PropertyPath; -import org.hibernate.loader.plan.internal.LoadPlanBuildingHelper; -import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext; import org.hibernate.loader.spi.ResultSetProcessingContext; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; -import org.hibernate.persister.walking.spi.CompositionDefinition; import static org.hibernate.loader.spi.ResultSetProcessingContext.IdentifierResolutionContext; @@ -50,24 +46,32 @@ public class EntityReturn extends AbstractFetchOwner implements Return, EntityRe private final PropertyPath propertyPath = new PropertyPath(); // its a root + private final LockMode lockMode; + + private final FetchOwnerDelegate fetchOwnerDelegate; + private IdentifierDescription identifierDescription; public EntityReturn( SessionFactoryImplementor sessionFactory, LockMode lockMode, String entityName) { - super( sessionFactory, lockMode ); - + super( sessionFactory ); this.persister = sessionFactory.getEntityPersister( entityName ); + this.lockMode = lockMode; + this.fetchOwnerDelegate = new EntityFetchOwnerDelegate( persister ); } protected EntityReturn(EntityReturn original, CopyContext copyContext) { super( original, copyContext ); this.persister = original.persister; + this.lockMode = original.lockMode; + this.fetchOwnerDelegate = original.fetchOwnerDelegate; } + @Override public LockMode getLockMode() { - return super.getLockMode(); + return lockMode; } @Override @@ -99,42 +103,9 @@ public class EntityReturn extends AbstractFetchOwner implements Return, EntityRe return propertyPath; } - @Override - public CollectionFetch buildCollectionFetch( - AssociationAttributeDefinition attributeDefinition, - FetchStrategy fetchStrategy, - LoadPlanBuildingContext loadPlanBuildingContext) { - return LoadPlanBuildingHelper.buildStandardCollectionFetch( - this, - attributeDefinition, - fetchStrategy, - loadPlanBuildingContext - ); - } - - @Override - public EntityFetch buildEntityFetch( - AssociationAttributeDefinition attributeDefinition, - FetchStrategy fetchStrategy, - LoadPlanBuildingContext loadPlanBuildingContext) { - return LoadPlanBuildingHelper.buildStandardEntityFetch( - this, - attributeDefinition, - fetchStrategy, - loadPlanBuildingContext - ); - } - - @Override - public CompositeFetch buildCompositeFetch( - CompositionDefinition attributeDefinition, - LoadPlanBuildingContext loadPlanBuildingContext) { - return LoadPlanBuildingHelper.buildStandardCompositeFetch( this, attributeDefinition, loadPlanBuildingContext ); - } - @Override public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException { - EntityKey entityKey = context.getDictatedRootEntityKey(); + EntityKey entityKey = getEntityKeyFromContext( context ); if ( entityKey != null ) { context.getIdentifierResolutionContext( this ).registerEntityKey( entityKey ); return; @@ -147,6 +118,19 @@ public class EntityReturn extends AbstractFetchOwner implements Return, EntityRe } } + private EntityKey getEntityKeyFromContext(ResultSetProcessingContext context) { + if ( context.getDictatedRootEntityKey() != null ) { + return context.getDictatedRootEntityKey(); + } + else if ( context.getQueryParameters().getOptionalId() != null ) { + return context.getSession().generateEntityKey( + context.getQueryParameters().getOptionalId(), + getEntityPersister() + ); + } + return null; + } + @Override public void resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException { final IdentifierResolutionContext identifierResolutionContext = context.getIdentifierResolutionContext( this ); @@ -194,4 +178,9 @@ public class EntityReturn extends AbstractFetchOwner implements Return, EntityRe public EntityReturn makeCopy(CopyContext copyContext) { return new EntityReturn( this, copyContext ); } + + @Override + protected FetchOwnerDelegate getFetchOwnerDelegate() { + return fetchOwnerDelegate; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/Fetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/Fetch.java index ab344a17f2..9d34ff51ad 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/Fetch.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/Fetch.java @@ -52,6 +52,10 @@ public interface Fetch extends CopyableFetch { */ public String getOwnerPropertyName(); + public boolean isNullable(); + + public String[] getColumnNames(); + public FetchStrategy getFetchStrategy(); /** diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/FetchOwner.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/FetchOwner.java index 102180bdb1..9558e987d3 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/FetchOwner.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/FetchOwner.java @@ -29,6 +29,7 @@ import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; import org.hibernate.persister.walking.spi.CompositionDefinition; +import org.hibernate.type.Type; /** * Contract for owners of fetches. Any non-scalar return could be a fetch owner. @@ -56,6 +57,12 @@ public interface FetchOwner { */ public Fetch[] getFetches(); + public Type getType(Fetch fetch); + + public boolean isNullable(Fetch fetch); + + public String[] getColumnNames(Fetch fetch); + /** * Is the asserted plan valid from this owner to a fetch? * diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/FetchOwnerDelegate.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/FetchOwnerDelegate.java new file mode 100644 index 0000000000..523891b3ef --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/FetchOwnerDelegate.java @@ -0,0 +1,38 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.loader.plan.spi; + +import org.hibernate.type.Type; + +/** + * @author Gail Badner + */ +public interface FetchOwnerDelegate { + + public boolean isNullable(Fetch fetch); + + public Type getType(Fetch fetch); + + public String[] getColumnNames(Fetch fetch); +} \ No newline at end of file diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/FetchableCollectionElement.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/FetchableCollectionElement.java index 5b18a01029..c4f090f9e5 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/FetchableCollectionElement.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/FetchableCollectionElement.java @@ -23,10 +23,14 @@ */ package org.hibernate.loader.plan.spi; +import org.hibernate.persister.collection.CollectionPersister; + /** * @author Steve Ebersole */ public interface FetchableCollectionElement extends FetchOwner, CopyableReturn { @Override public FetchableCollectionElement makeCopy(CopyContext copyContext); + + public CollectionReference getCollectionReference(); } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/build/AbstractLoadPlanBuilderStrategy.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/build/AbstractLoadPlanBuilderStrategy.java index e6968905d1..d7c12c762d 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/build/AbstractLoadPlanBuilderStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/build/AbstractLoadPlanBuilderStrategy.java @@ -37,6 +37,7 @@ import org.jboss.logging.MDC; import org.hibernate.HibernateException; import org.hibernate.LockMode; +import org.hibernate.cfg.NotYetImplementedException; import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.EntityKey; @@ -44,19 +45,24 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.internal.util.StringHelper; import org.hibernate.loader.PropertyPath; import org.hibernate.loader.plan.internal.LoadPlanBuildingHelper; +import org.hibernate.loader.plan.spi.AbstractSingularAttributeFetch; import org.hibernate.loader.plan.spi.CollectionFetch; import org.hibernate.loader.plan.spi.CollectionReference; import org.hibernate.loader.plan.spi.CollectionReturn; +import org.hibernate.loader.plan.spi.CompositeElementGraph; import org.hibernate.loader.plan.spi.CompositeFetch; +import org.hibernate.loader.plan.spi.CompositeFetchOwnerDelegate; import org.hibernate.loader.plan.spi.EntityFetch; import org.hibernate.loader.plan.spi.EntityReference; import org.hibernate.loader.plan.spi.EntityReturn; import org.hibernate.loader.plan.spi.Fetch; import org.hibernate.loader.plan.spi.FetchOwner; +import org.hibernate.loader.plan.spi.FetchOwnerDelegate; import org.hibernate.loader.plan.spi.IdentifierDescription; import org.hibernate.loader.plan.spi.Return; import org.hibernate.loader.spi.ResultSetProcessingContext; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.entity.Loadable; import org.hibernate.persister.spi.HydratedCompoundValueHandler; import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; import org.hibernate.persister.walking.spi.AttributeDefinition; @@ -64,9 +70,11 @@ import org.hibernate.persister.walking.spi.CollectionDefinition; import org.hibernate.persister.walking.spi.CollectionElementDefinition; import org.hibernate.persister.walking.spi.CollectionIndexDefinition; import org.hibernate.persister.walking.spi.CompositionDefinition; +import org.hibernate.persister.walking.spi.CompositionElementDefinition; import org.hibernate.persister.walking.spi.EntityDefinition; import org.hibernate.persister.walking.spi.EntityIdentifierDefinition; import org.hibernate.persister.walking.spi.WalkingException; +import org.hibernate.type.CompositeType; import org.hibernate.type.Type; import static org.hibernate.loader.spi.ResultSetProcessingContext.IdentifierResolutionContext; @@ -190,7 +198,12 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder final FetchOwner identifierAttributeCollector; if ( entityIdentifierDefinition.isEncapsulated() ) { - identifierAttributeCollector = new EncapsulatedIdentifierAttributeCollector( entityReference ); + if ( entityIdentifierDefinition.getEntityDefinition().getEntityPersister().getIdentifierType().isComponentType() ) { + identifierAttributeCollector = new EncapsulatedCompositeIdentifierAttributeCollector( entityReference ); + } + else { + identifierAttributeCollector = new EncapsulatedIdentifierAttributeCollector( entityReference ); + } } else { identifierAttributeCollector = new NonEncapsulatedIdentifierAttributeCollector( entityReference ); @@ -305,6 +318,35 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder // - the element graph pushed while starting would be popped in finishing/Entity/finishingComposite } + @Override + public void startingCompositeElement(CompositionElementDefinition compositeElementDefinition) { + System.out.println( + String.format( + "%s Starting composite collection element for (%s)", + StringHelper.repeat( ">>", fetchOwnerStack.size() ), + compositeElementDefinition.getCollectionDefinition().getCollectionPersister().getRole() + ) + ); + } + + @Override + public void finishingCompositeElement(CompositionElementDefinition compositeElementDefinition) { + // pop the current fetch owner, and make sure what we just popped represents this composition + final FetchOwner poppedFetchOwner = popFromStack(); + + if ( ! CompositeElementGraph.class.isInstance( poppedFetchOwner ) ) { + throw new WalkingException( "Mismatched FetchOwner from stack on pop" ); + } + + // NOTE : not much else we can really check here atm since on the walking spi side we do not have path + + log.tracef( + "%s Finished composite element for : %s", + StringHelper.repeat( "<<", fetchOwnerStack.size() ), + compositeElementDefinition.getCollectionDefinition().getCollectionPersister().getRole() + ); + } + @Override public void finishingCollection(CollectionDefinition collectionDefinition) { // pop the current fetch owner, and make sure what we just popped represents this collection @@ -413,7 +455,7 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder ); } - if ( FetchOwner.class.isInstance( associationFetch ) ) { + if ( FetchOwner.class.isInstance( associationFetch) ) { pushToStack( (FetchOwner) associationFetch ); } @@ -487,15 +529,13 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder implements FetchOwner, EntityReference, FetchStackAware { protected final EntityReference entityReference; - private final PropertyPath propertyPath; - protected final List identifierFetches = new ArrayList(); - protected final Map fetchToHydratedStateExtractorMap - = new HashMap(); + protected final List identifierFetches = new ArrayList(); + protected final Map fetchToHydratedStateExtractorMap + = new HashMap(); public AbstractIdentifierAttributeCollector(EntityReference entityReference) { this.entityReference = entityReference; - this.propertyPath = ( (FetchOwner) entityReference ).getPropertyPath().append( "" ); } @Override @@ -513,6 +553,11 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder return entityReference.getEntityPersister(); } + @Override + public boolean isNullable(Fetch fetch) { + return false; + } + @Override public IdentifierDescription getIdentifierDescription() { return entityReference.getIdentifierDescription(); @@ -533,7 +578,7 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder LoadPlanBuildingContext loadPlanBuildingContext) { // we have a key-many-to-one // - // IMPL NOTE: we pass ourselves as the FetchOwner which will route the fetch back throw our #addFetch + // IMPL NOTE: we pass ourselves as the FetchOwner which will route the fetch back through our #addFetch // impl. We collect them there and later build the IdentifierDescription final EntityFetch fetch = LoadPlanBuildingHelper.buildStandardEntityFetch( this, @@ -551,7 +596,7 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder CompositionDefinition attributeDefinition, LoadPlanBuildingContext loadPlanBuildingContext) { // nested composition. Unusual, but not disallowed. // - // IMPL NOTE: we pass ourselves as the FetchOwner which will route the fetch back throw our #addFetch + // IMPL NOTE: we pass ourselves as the FetchOwner which will route the fetch back through our #addFetch // impl. We collect them there and later build the IdentifierDescription return LoadPlanBuildingHelper.buildStandardCompositeFetch( this, @@ -570,7 +615,7 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder @Override public void addFetch(Fetch fetch) { - identifierFetches.add( (EntityFetch) fetch ); + identifierFetches.add( (AbstractSingularAttributeFetch) fetch ); } @Override @@ -588,11 +633,6 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder return ( (FetchOwner) entityReference ).retrieveFetchSourcePersister(); } - @Override - public PropertyPath getPropertyPath() { - return propertyPath; - } - @Override public void injectIdentifierDescription(IdentifierDescription identifierDescription) { throw new WalkingException( @@ -601,44 +641,144 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder } } - protected static class EncapsulatedIdentifierAttributeCollector extends AbstractIdentifierAttributeCollector { - public EncapsulatedIdentifierAttributeCollector(EntityReference entityReference) { - super( entityReference ); - } + protected static abstract class AbstractCompositeIdentifierAttributeCollector extends AbstractIdentifierAttributeCollector { - @Override - protected IdentifierDescription buildIdentifierDescription() { - return new IdentifierDescriptionImpl( - entityReference, - identifierFetches.toArray( new EntityFetch[ identifierFetches.size() ] ), - null - ); + public AbstractCompositeIdentifierAttributeCollector(EntityReference entityReference) { + super( entityReference ); } } - protected static class NonEncapsulatedIdentifierAttributeCollector extends AbstractIdentifierAttributeCollector { - public NonEncapsulatedIdentifierAttributeCollector(EntityReference entityReference) { + protected static class EncapsulatedIdentifierAttributeCollector extends AbstractIdentifierAttributeCollector { + private final PropertyPath propertyPath; + + public EncapsulatedIdentifierAttributeCollector(EntityReference entityReference) { super( entityReference ); + this.propertyPath = ( (FetchOwner) entityReference ).getPropertyPath(); } @Override protected IdentifierDescription buildIdentifierDescription() { return new IdentifierDescriptionImpl( entityReference, - identifierFetches.toArray( new EntityFetch[ identifierFetches.size() ] ), + identifierFetches.toArray( new AbstractSingularAttributeFetch[ identifierFetches.size() ] ), + null + ); + } + + @Override + public Type getType(Fetch fetch) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public String[] getColumnNames(Fetch fetch) { + return ( (Loadable) entityReference.getEntityPersister() ).getIdentifierColumnNames(); + } + + @Override + public PropertyPath getPropertyPath() { + return propertyPath; + } + } + + protected static class EncapsulatedCompositeIdentifierAttributeCollector extends AbstractCompositeIdentifierAttributeCollector { + private final PropertyPath propertyPath; + + public EncapsulatedCompositeIdentifierAttributeCollector(EntityReference entityReference) { + super( entityReference ); + this.propertyPath = ( (FetchOwner) entityReference ).getPropertyPath(); + } + + @Override + protected IdentifierDescription buildIdentifierDescription() { + return new IdentifierDescriptionImpl( + entityReference, + identifierFetches.toArray( new AbstractSingularAttributeFetch[ identifierFetches.size() ] ), + null + ); + } + + @Override + public PropertyPath getPropertyPath() { + return propertyPath; + } + + @Override + public Type getType(Fetch fetch) { + if ( !fetch.getOwnerPropertyName().equals( entityReference.getEntityPersister().getIdentifierPropertyName() ) ) { + throw new IllegalArgumentException( + String.format( + "Fetch owner property name [%s] is not the same as the identifier property name [%s].", + fetch.getOwnerPropertyName(), + entityReference.getEntityPersister().getIdentifierPropertyName() + ) + ); + } + return entityReference.getEntityPersister().getIdentifierType(); + } + + @Override + public String[] getColumnNames(Fetch fetch) { + return ( (Loadable) entityReference.getEntityPersister() ).getIdentifierColumnNames(); + } + } + + protected static class NonEncapsulatedIdentifierAttributeCollector extends AbstractCompositeIdentifierAttributeCollector { + private final PropertyPath propertyPath; + private final FetchOwnerDelegate fetchOwnerDelegate; + + public NonEncapsulatedIdentifierAttributeCollector(EntityReference entityReference) { + super( entityReference ); + this.propertyPath = ( (FetchOwner) entityReference ).getPropertyPath().append( "" ); + this.fetchOwnerDelegate = new CompositeFetchOwnerDelegate( + entityReference.getEntityPersister().getFactory(), + (CompositeType) entityReference.getEntityPersister().getIdentifierType(), + ( (Loadable) entityReference.getEntityPersister() ).getIdentifierColumnNames() + ); + } + + @Override + protected IdentifierDescription buildIdentifierDescription() { + return new IdentifierDescriptionImpl( + entityReference, + identifierFetches.toArray( new AbstractSingularAttributeFetch[ identifierFetches.size() ] ), fetchToHydratedStateExtractorMap ); } + + @Override + public PropertyPath getPropertyPath() { + return propertyPath; + } + + + public Type getType(Fetch fetch) { + if ( !fetch.getOwnerPropertyName().equals( entityReference.getEntityPersister().getIdentifierPropertyName() ) ) { + throw new IllegalArgumentException( + String.format( + "Fetch owner property name [%s] is not the same as the identifier property name [%s].", + fetch.getOwnerPropertyName(), + entityReference.getEntityPersister().getIdentifierPropertyName() + ) + ); + } + return fetchOwnerDelegate.getType( fetch ); + } + + public String[] getColumnNames(Fetch fetch) { + return fetchOwnerDelegate.getColumnNames( fetch ); + } } private static class IdentifierDescriptionImpl implements IdentifierDescription { private final EntityReference entityReference; - private final EntityFetch[] identifierFetches; - private final Map fetchToHydratedStateExtractorMap; + private final AbstractSingularAttributeFetch[] identifierFetches; + private final Map fetchToHydratedStateExtractorMap; private IdentifierDescriptionImpl( - EntityReference entityReference, EntityFetch[] identifierFetches, - Map fetchToHydratedStateExtractorMap) { + EntityReference entityReference, + AbstractSingularAttributeFetch[] identifierFetches, + Map fetchToHydratedStateExtractorMap) { this.entityReference = entityReference; this.identifierFetches = identifierFetches; this.fetchToHydratedStateExtractorMap = fetchToHydratedStateExtractorMap; @@ -656,24 +796,28 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder final Object ownerIdentifierHydratedState = ownerIdentifierResolutionContext.getHydratedForm(); if ( ownerIdentifierHydratedState != null ) { - for ( EntityFetch fetch : identifierFetches ) { - final IdentifierResolutionContext identifierResolutionContext = - context.getIdentifierResolutionContext( fetch ); - // if the identifier was already hydrated, nothing to do - if ( identifierResolutionContext.getHydratedForm() != null ) { - continue; - } + for ( AbstractSingularAttributeFetch fetch : identifierFetches ) { + if ( fetch instanceof EntityFetch ) { + final IdentifierResolutionContext identifierResolutionContext = + context.getIdentifierResolutionContext( (EntityFetch) fetch ); + // if the identifier was already hydrated, nothing to do + if ( identifierResolutionContext.getHydratedForm() != null ) { + continue; + } + // try to extract the sub-hydrated value from the owners tuple array + if ( fetchToHydratedStateExtractorMap != null && ownerIdentifierHydratedState != null ) { + Serializable extracted = (Serializable) fetchToHydratedStateExtractorMap.get( fetch ) + .extract( ownerIdentifierHydratedState ); + identifierResolutionContext.registerHydratedForm( extracted ); + continue; + } - // try to extract the sub-hydrated value from the owners tuple array - if ( fetchToHydratedStateExtractorMap != null && ownerIdentifierHydratedState != null ) { - Serializable extracted = (Serializable) fetchToHydratedStateExtractorMap.get( fetch ) - .extract( ownerIdentifierHydratedState ); - identifierResolutionContext.registerHydratedForm( extracted ); - continue; + // if we can't, then read from result set + fetch.hydrate( resultSet, context ); + } + else { + throw new NotYetImplementedException( "Cannot hydrate identifier Fetch that is not an EntityFetch" ); } - - // if we can't, then read from result set - fetch.hydrate( resultSet, context ); } return; } @@ -689,15 +833,8 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder @Override public EntityKey resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException { - for ( EntityFetch fetch : identifierFetches ) { - final IdentifierResolutionContext identifierResolutionContext = - context.getIdentifierResolutionContext( fetch ); - if ( identifierResolutionContext.getEntityKey() != null ) { - continue; - } - - EntityKey fetchKey = fetch.resolveInIdentifier( resultSet, context ); - identifierResolutionContext.registerEntityKey( fetchKey ); + for ( AbstractSingularAttributeFetch fetch : identifierFetches ) { + resolveIdentifierFetch( resultSet, context, fetch ); } final IdentifierResolutionContext ownerIdentifierResolutionContext = @@ -710,6 +847,28 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder } } + private static void resolveIdentifierFetch( + ResultSet resultSet, + ResultSetProcessingContext context, + AbstractSingularAttributeFetch fetch) throws SQLException { + if ( fetch instanceof EntityFetch ) { + EntityFetch entityFetch = (EntityFetch) fetch; + final IdentifierResolutionContext identifierResolutionContext = + context.getIdentifierResolutionContext( entityFetch ); + if ( identifierResolutionContext.getEntityKey() != null ) { + return; + } + + EntityKey fetchKey = entityFetch.resolveInIdentifier( resultSet, context ); + identifierResolutionContext.registerEntityKey( fetchKey ); + } + else if ( fetch instanceof CompositeFetch ) { + for ( Fetch subFetch : fetch.getFetches() ) { + resolveIdentifierFetch( resultSet, context, (AbstractSingularAttributeFetch) subFetch ); + } + } + } + public static class MDCStack { private ArrayDeque pathStack = new ArrayDeque(); diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/ManyToOne.java b/hibernate-core/src/main/java/org/hibernate/mapping/ManyToOne.java index 9736b5bf51..49cf99a548 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/ManyToOne.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/ManyToOne.java @@ -45,7 +45,8 @@ public class ManyToOne extends ToOne { public Type getType() throws MappingException { return getMappings().getTypeResolver().getTypeFactory().manyToOne( - getReferencedEntityName(), + getReferencedEntityName(), + referenceToPrimaryKey, getReferencedPropertyName(), isLazy(), isUnwrapProxy(), diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/OneToMany.java b/hibernate-core/src/main/java/org/hibernate/mapping/OneToMany.java index 80287e3a8c..f7e4a00ff9 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/OneToMany.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/OneToMany.java @@ -46,7 +46,8 @@ public class OneToMany implements Value { private EntityType getEntityType() { return mappings.getTypeResolver().getTypeFactory().manyToOne( - getReferencedEntityName(), + getReferencedEntityName(), + true, null, false, false, diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/OneToOne.java b/hibernate-core/src/main/java/org/hibernate/mapping/OneToOne.java index ca8e196db6..596cfe4ebf 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/OneToOne.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/OneToOne.java @@ -69,7 +69,8 @@ public class OneToOne extends ToOne { if ( getColumnIterator().hasNext() ) { return getMappings().getTypeResolver().getTypeFactory().specialOneToOne( getReferencedEntityName(), - foreignKeyType, + foreignKeyType, + referenceToPrimaryKey, referencedPropertyName, isLazy(), isUnwrapProxy(), @@ -80,7 +81,8 @@ public class OneToOne extends ToOne { else { return getMappings().getTypeResolver().getTypeFactory().oneToOne( getReferencedEntityName(), - foreignKeyType, + foreignKeyType, + referenceToPrimaryKey, referencedPropertyName, isLazy(), isUnwrapProxy(), diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/ToOne.java b/hibernate-core/src/main/java/org/hibernate/mapping/ToOne.java index fc74daa855..79ef969cfc 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/ToOne.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/ToOne.java @@ -40,6 +40,7 @@ public abstract class ToOne extends SimpleValue implements Fetchable { private String referencedEntityName; private boolean lazy = true; protected boolean unwrapProxy; + protected boolean referenceToPrimaryKey = true; protected ToOne(Mappings mappings, Table table) { super( mappings, table ); @@ -110,5 +111,13 @@ public abstract class ToOne extends SimpleValue implements Fetchable { public void setUnwrapProxy(boolean unwrapProxy) { this.unwrapProxy = unwrapProxy; } + + public boolean isReferenceToPrimaryKey() { + return referenceToPrimaryKey; + } + + public void setReferenceToPrimaryKey(boolean referenceToPrimaryKey) { + this.referenceToPrimaryKey = referenceToPrimaryKey; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/Metadata.java b/hibernate-core/src/main/java/org/hibernate/metamodel/Metadata.java index b8b3047926..43924bccae 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/Metadata.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/Metadata.java @@ -35,6 +35,7 @@ import org.hibernate.SessionFactory; import org.hibernate.boot.registry.StandardServiceRegistry; import org.hibernate.cache.spi.access.AccessType; import org.hibernate.cfg.NamingStrategy; +import org.hibernate.cfg.annotations.NamedEntityGraphDefinition; import org.hibernate.engine.ResultSetMappingDefinition; import org.hibernate.engine.spi.FilterDefinition; import org.hibernate.engine.spi.NamedQueryDefinition; @@ -115,6 +116,8 @@ public interface Metadata { public Iterable getNamedQueryDefinitions(); + public Map getNamedEntityGraphMap(); + public Iterable getNamedNativeQueryDefinitions(); public Map getResultSetMappingDefinitions(); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetadataImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetadataImpl.java index 55c8b0feb2..95691436cd 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetadataImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetadataImpl.java @@ -47,6 +47,7 @@ import org.hibernate.boot.spi.CacheRegionDefinition; import org.hibernate.cache.spi.access.AccessType; import org.hibernate.cfg.NamingStrategy; import org.hibernate.cfg.ObjectNameNormalizer; +import org.hibernate.cfg.annotations.NamedEntityGraphDefinition; import org.hibernate.dialect.Dialect; import org.hibernate.engine.ResultSetMappingDefinition; import org.hibernate.engine.jdbc.spi.JdbcServices; @@ -134,7 +135,7 @@ public class MetadataImpl implements MetadataImplementor, Serializable { private final MappingDefaults mappingDefaults; private final ObjectNameNormalizer nameNormalizer; - private Map typeDefinitionMap = new HashMap(); + private final Map typeDefinitionMap = new HashMap(); private Map filterDefinitionMap = new HashMap(); private Map entityBindingMap = new HashMap(); @@ -145,6 +146,7 @@ public class MetadataImpl implements MetadataImplementor, Serializable { private Map namedQueryDefs = new HashMap(); private Map namedNativeQueryDefs = new HashMap(); private Map resultSetMappings = new HashMap(); + private final Map namedEntityGraphMap = new HashMap( ); private boolean globallyQuotedIdentifiers = false; @@ -375,6 +377,19 @@ public class MetadataImpl implements MetadataImplementor, Serializable { return typeDefinitionMap.get( registrationKey ); } + @Override + public void addNamedEntityGraph(NamedEntityGraphDefinition definition) { + final String name = definition.getRegisteredName(); + final NamedEntityGraphDefinition previous = namedEntityGraphMap.put( name, definition ); + if ( previous != null ) { + throw new DuplicateMappingException( "NamedEntityGraph", name ); + } + } + + @Override + public Map getNamedEntityGraphMap() { + return namedEntityGraphMap; + } // filter definitions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/MetadataImplementor.java b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/MetadataImplementor.java index e3786a8115..745f6edc91 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/MetadataImplementor.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/MetadataImplementor.java @@ -24,6 +24,7 @@ package org.hibernate.metamodel.spi; import org.hibernate.cfg.ObjectNameNormalizer; +import org.hibernate.cfg.annotations.NamedEntityGraphDefinition; import org.hibernate.engine.ResultSetMappingDefinition; import org.hibernate.engine.spi.FilterDefinition; import org.hibernate.engine.spi.Mapping; @@ -69,6 +70,8 @@ public interface MetadataImplementor extends Metadata, BindingContext, Mapping { public void addNamedNativeQuery(NamedSQLQueryDefinition def); + public void addNamedEntityGraph(NamedEntityGraphDefinition def); + public void addNamedQuery(NamedQueryDefinition def); public void addResultSetMapping(ResultSetMappingDefinition resultSetMappingDefinition); diff --git a/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java index 2295336ee3..35af40967c 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java @@ -101,10 +101,14 @@ import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.Loadable; import org.hibernate.persister.entity.PropertyMapping; import org.hibernate.persister.entity.Queryable; +import org.hibernate.persister.walking.internal.CompositionSingularSubAttributesHelper; +import org.hibernate.persister.walking.spi.AttributeDefinition; +import org.hibernate.persister.walking.spi.AttributeSource; import org.hibernate.persister.walking.spi.CollectionDefinition; import org.hibernate.persister.walking.spi.CollectionElementDefinition; import org.hibernate.persister.walking.spi.CollectionIndexDefinition; import org.hibernate.persister.walking.spi.CompositionDefinition; +import org.hibernate.persister.walking.spi.CompositionElementDefinition; import org.hibernate.persister.walking.spi.EntityDefinition; import org.hibernate.pretty.MessageHelper; import org.hibernate.sql.Alias; @@ -2342,7 +2346,6 @@ public abstract class AbstractCollectionPersister public abstract FilterAliasGenerator getFilterAliasGenerator(final String rootAlias); - // ColectionDefinition impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @Override @@ -2408,12 +2411,42 @@ public abstract class AbstractCollectionPersister } @Override - public CompositionDefinition toCompositeDefinition() { + public CompositionElementDefinition toCompositeElementDefinition() { + final String propertyName = role.substring( entityName.length() + 1 ); + final int propertyIndex = ownerPersister.getEntityMetamodel().getPropertyIndex( propertyName ); + if ( ! getType().isComponentType() ) { throw new IllegalStateException( "Cannot treat entity collection element type as composite" ); } - // todo : implement - throw new NotYetImplementedException(); + + return new CompositionElementDefinition() { + @Override + public String getName() { + return ""; + } + + @Override + public Type getType() { + return getElementType(); + } + + @Override + public AttributeSource getSource() { + // TODO: what if this is a collection w/in an encapsulated composition attribute? + // should return the encapsulated composition attribute instead??? + return getOwnerEntityPersister(); + } + + @Override + public Iterable getAttributes() { + return CompositionSingularSubAttributesHelper.getCompositionElementSubAttributes( this ); + } + + @Override + public CollectionDefinition getCollectionDefinition() { + return AbstractCollectionPersister.this; + } + }; } }; } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index 0895ab57da..cad3fbd0bd 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -62,7 +62,6 @@ import org.hibernate.cache.spi.entry.ReferenceCacheEntryImpl; import org.hibernate.cache.spi.entry.StandardCacheEntryImpl; import org.hibernate.cache.spi.entry.StructuredCacheEntry; import org.hibernate.cache.spi.entry.UnstructuredCacheEntry; -import org.hibernate.cfg.NotYetImplementedException; import org.hibernate.dialect.lock.LockingStrategy; import org.hibernate.engine.OptimisticLockStyle; import org.hibernate.engine.internal.StatefulPersistenceContext; @@ -120,12 +119,9 @@ import org.hibernate.metamodel.spi.relational.DerivedValue; import org.hibernate.metamodel.spi.relational.Table; import org.hibernate.metamodel.spi.relational.TableSpecification; import org.hibernate.metamodel.spi.relational.Value; +import org.hibernate.persister.walking.internal.EntityIdentifierDefinitionHelper; import org.hibernate.persister.walking.spi.AttributeDefinition; -import org.hibernate.persister.walking.spi.AttributeSource; -import org.hibernate.persister.walking.spi.EncapsulatedEntityIdentifierDefinition; -import org.hibernate.persister.walking.spi.EntityDefinition; import org.hibernate.persister.walking.spi.EntityIdentifierDefinition; -import org.hibernate.persister.walking.spi.NonEncapsulatedEntityIdentifierDefinition; import org.hibernate.pretty.MessageHelper; import org.hibernate.property.BackrefPropertyAccessor; import org.hibernate.sql.Alias; @@ -5290,80 +5286,20 @@ public abstract class AbstractEntityPersister final Type idType = getIdentifierType(); if ( !idType.isComponentType() ) { - entityIdentifierDefinition = buildEncapsulatedIdentifierDefinition(); + entityIdentifierDefinition = + EntityIdentifierDefinitionHelper.buildSimpleEncapsulatedIdentifierDefinition( this ); return; } final CompositeType cidType = (CompositeType) idType; if ( !cidType.isEmbedded() ) { - entityIdentifierDefinition = buildEncapsulatedIdentifierDefinition(); + entityIdentifierDefinition = + EntityIdentifierDefinitionHelper.buildEncapsulatedCompositeIdentifierDefinition( this ); return; } - entityIdentifierDefinition = new NonEncapsulatedEntityIdentifierDefinition() { - @Override - public Iterable getAttributes() { - // todo : implement - throw new NotYetImplementedException(); - } - - @Override - public Class getSeparateIdentifierMappingClass() { - // todo : implement - throw new NotYetImplementedException(); - } - - @Override - public boolean isEncapsulated() { - return false; - } - - @Override - public EntityDefinition getEntityDefinition() { - return AbstractEntityPersister.this; - } - }; - } - - private EntityIdentifierDefinition buildEncapsulatedIdentifierDefinition() { - final AttributeDefinition simpleIdentifierAttributeAdapter = new AttributeDefinition() { - @Override - public String getName() { - return entityMetamodel.getIdentifierProperty().getName(); - } - - @Override - public Type getType() { - return entityMetamodel.getIdentifierProperty().getType(); - } - - @Override - public AttributeSource getSource() { - return AbstractEntityPersister.this; - } - - @Override - public String toString() { - return ""; - } - }; - - return new EncapsulatedEntityIdentifierDefinition() { - @Override - public AttributeDefinition getAttributeDefinition() { - return simpleIdentifierAttributeAdapter; - } - - @Override - public boolean isEncapsulated() { - return true; - } - - @Override - public EntityDefinition getEntityDefinition() { - return AbstractEntityPersister.this; - } - }; + entityIdentifierDefinition = + EntityIdentifierDefinitionHelper.buildNonEncapsulatedCompositeIdentifierDefinition( this ); } private void collectAttributeDefinitions() { diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/internal/CompositionSingularSubAttributesHelper.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/internal/CompositionSingularSubAttributesHelper.java new file mode 100644 index 0000000000..8cac9860c0 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/internal/CompositionSingularSubAttributesHelper.java @@ -0,0 +1,212 @@ +package org.hibernate.persister.walking.internal; + +import java.util.Iterator; + +import org.hibernate.engine.FetchStrategy; +import org.hibernate.engine.FetchStyle; +import org.hibernate.engine.FetchTiming; +import org.hibernate.engine.spi.CascadeStyle; +import org.hibernate.engine.spi.CascadeStyles; +import org.hibernate.engine.spi.LoadQueryInfluencers; +import org.hibernate.internal.util.collections.ArrayHelper; +import org.hibernate.loader.PropertyPath; +import org.hibernate.persister.collection.QueryableCollection; +import org.hibernate.persister.entity.AbstractEntityPersister; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.entity.OuterJoinLoadable; +import org.hibernate.persister.spi.HydratedCompoundValueHandler; +import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; +import org.hibernate.persister.walking.spi.AssociationKey; +import org.hibernate.persister.walking.spi.AttributeDefinition; +import org.hibernate.persister.walking.spi.AttributeSource; +import org.hibernate.persister.walking.spi.CollectionDefinition; +import org.hibernate.persister.walking.spi.CompositionDefinition; +import org.hibernate.persister.walking.spi.CompositionElementDefinition; +import org.hibernate.persister.walking.spi.EntityDefinition; +import org.hibernate.persister.walking.spi.WalkingException; +import org.hibernate.type.AssociationType; +import org.hibernate.type.CompositeType; +import org.hibernate.type.Type; + +/** + * @author Gail Badner + */ +public class CompositionSingularSubAttributesHelper { + + public static Iterable getIdentifierSubAttributes( + final AbstractEntityPersister entityPersister) { + return getSingularSubAttributes( + entityPersister, + entityPersister, + (CompositeType) entityPersister.getIdentifierType(), + entityPersister.getTableName(), + entityPersister.getRootTableIdentifierColumnNames() + ); + } + + public static Iterable getCompositionElementSubAttributes( + CompositionElementDefinition compositionElementDefinition) { + final QueryableCollection collectionPersister = + (QueryableCollection) compositionElementDefinition.getCollectionDefinition().getCollectionPersister(); + return getSingularSubAttributes( + compositionElementDefinition.getSource(), + (OuterJoinLoadable) collectionPersister.getOwnerEntityPersister(), + (CompositeType) collectionPersister.getElementType(), + collectionPersister.getTableName(), + collectionPersister.getElementColumnNames() + ); + } + + private static Iterable getSingularSubAttributes( + final AttributeSource source, + final OuterJoinLoadable ownerEntityPersister, + final CompositeType compositeType, + final String lhsTableName, + final String[] lhsColumns) { + return new Iterable() { + @Override + public Iterator iterator() { + return new Iterator() { + private final int numberOfAttributes = compositeType.getSubtypes().length; + private int currentSubAttributeNumber = 0; + private int currentColumnPosition = 0; + + @Override + public boolean hasNext() { + return currentSubAttributeNumber < numberOfAttributes; + } + + @Override + public AttributeDefinition next() { + final int subAttributeNumber = currentSubAttributeNumber; + currentSubAttributeNumber++; + + final String name = compositeType.getPropertyNames()[subAttributeNumber]; + final Type type = compositeType.getSubtypes()[subAttributeNumber]; + + final int columnPosition = currentColumnPosition; + final int columnSpan = type.getColumnSpan( ownerEntityPersister.getFactory() ); + final String[] subAttributeLhsColumns = ArrayHelper.slice( lhsColumns, columnPosition, columnSpan ); + + currentColumnPosition += columnSpan; + + if ( type.isAssociationType() ) { + final AssociationType aType = (AssociationType) type; + return new AssociationAttributeDefinition() { + @Override + public AssociationKey getAssociationKey() { + /* TODO: is this always correct? */ + //return new AssociationKey( + // joinable.getTableName(), + // JoinHelper.getRHSColumnNames( aType, getEntityPersister().getFactory() ) + //); + return new AssociationKey( + lhsTableName, + subAttributeLhsColumns + ); + } + + @Override + public boolean isCollection() { + return false; + } + + @Override + public EntityDefinition toEntityDefinition() { + return (EntityPersister) aType.getAssociatedJoinable( ownerEntityPersister.getFactory() ); + } + + @Override + public CollectionDefinition toCollectionDefinition() { + throw new WalkingException( "A collection cannot be mapped to a composite ID sub-attribute." ); + } + + @Override + public FetchStrategy determineFetchPlan(LoadQueryInfluencers loadQueryInfluencers, PropertyPath propertyPath) { + return new FetchStrategy( FetchTiming.IMMEDIATE, FetchStyle.JOIN ); + } + + @Override + public CascadeStyle determineCascadeStyle() { + return CascadeStyles.NONE; + } + + @Override + public HydratedCompoundValueHandler getHydratedCompoundValueExtractor() { + return null; + } + + @Override + public String getName() { + return name; + } + + @Override + public Type getType() { + return type; + } + + @Override + public AttributeSource getSource() { + return source; + } + }; + } + else if ( type.isComponentType() ) { + return new CompositionDefinition() { + @Override + public String getName() { + return name; + } + + @Override + public Type getType() { + return type; + } + + @Override + public AttributeSource getSource() { + return this; + } + + @Override + public Iterable getAttributes() { + return CompositionSingularSubAttributesHelper.getSingularSubAttributes( + this, + ownerEntityPersister, + (CompositeType) type, + lhsTableName, + subAttributeLhsColumns + ); + } + }; + } + else { + return new AttributeDefinition() { + @Override + public String getName() { + return name; + } + + @Override + public Type getType() { + return type; + } + + @Override + public AttributeSource getSource() { + return source; + } + }; + } + } + + @Override + public void remove() { + throw new UnsupportedOperationException( "Remove operation not supported here" ); + } + }; + } + }; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/internal/EntityIdentifierDefinitionHelper.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/internal/EntityIdentifierDefinitionHelper.java new file mode 100644 index 0000000000..bec4320dc2 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/internal/EntityIdentifierDefinitionHelper.java @@ -0,0 +1,153 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.persister.walking.internal; + +import org.hibernate.persister.entity.AbstractEntityPersister; +import org.hibernate.persister.walking.spi.AttributeDefinition; +import org.hibernate.persister.walking.spi.AttributeSource; +import org.hibernate.persister.walking.spi.CompositionDefinition; +import org.hibernate.persister.walking.spi.EncapsulatedEntityIdentifierDefinition; +import org.hibernate.persister.walking.spi.EntityDefinition; +import org.hibernate.persister.walking.spi.EntityIdentifierDefinition; +import org.hibernate.persister.walking.spi.NonEncapsulatedEntityIdentifierDefinition; +import org.hibernate.type.Type; + +/** + * @author Gail Badner + */ +public class EntityIdentifierDefinitionHelper { + + public static EntityIdentifierDefinition buildSimpleEncapsulatedIdentifierDefinition(final AbstractEntityPersister entityPersister) { + return new EncapsulatedEntityIdentifierDefinition() { + @Override + public AttributeDefinition getAttributeDefinition() { + return new AttributeDefinitionAdapter( entityPersister); + } + + @Override + public boolean isEncapsulated() { + return true; + } + + @Override + public EntityDefinition getEntityDefinition() { + return entityPersister; + } + }; + } + + public static EntityIdentifierDefinition buildEncapsulatedCompositeIdentifierDefinition( + final AbstractEntityPersister entityPersister) { + + return new EncapsulatedEntityIdentifierDefinition() { + @Override + public AttributeDefinition getAttributeDefinition() { + return new CompositionDefinitionAdapter( entityPersister ); + } + + @Override + public boolean isEncapsulated() { + return true; + } + + @Override + public EntityDefinition getEntityDefinition() { + return entityPersister; + } + }; + } + + public static EntityIdentifierDefinition buildNonEncapsulatedCompositeIdentifierDefinition(final AbstractEntityPersister entityPersister) { + return new NonEncapsulatedEntityIdentifierDefinition() { + @Override + public Iterable getAttributes() { + return CompositionSingularSubAttributesHelper.getIdentifierSubAttributes( entityPersister ); + } + + @Override + public Class getSeparateIdentifierMappingClass() { + return entityPersister.getEntityMetamodel().getIdentifierProperty().getType().getReturnedClass(); + } + + @Override + public boolean isEncapsulated() { + return false; + } + + @Override + public EntityDefinition getEntityDefinition() { + return entityPersister; + } + }; + } + + private static class AttributeDefinitionAdapter implements AttributeDefinition { + private final AbstractEntityPersister entityPersister; + + AttributeDefinitionAdapter(AbstractEntityPersister entityPersister) { + this.entityPersister = entityPersister; + } + + @Override + public String getName() { + return entityPersister.getEntityMetamodel().getIdentifierProperty().getName(); + } + + @Override + public Type getType() { + return entityPersister.getEntityMetamodel().getIdentifierProperty().getType(); + } + + @Override + public AttributeSource getSource() { + return entityPersister; + } + + @Override + public String toString() { + return ""; + } + + protected AbstractEntityPersister getEntityPersister() { + return entityPersister; + } + } + + private static class CompositionDefinitionAdapter extends AttributeDefinitionAdapter implements CompositionDefinition { + + CompositionDefinitionAdapter(AbstractEntityPersister entityPersister) { + super( entityPersister ); + } + + @Override + public String toString() { + return ""; + } + + @Override + public Iterable getAttributes() { + return CompositionSingularSubAttributesHelper.getIdentifierSubAttributes( getEntityPersister() ); + } + } +} \ No newline at end of file diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/internal/Helper.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/internal/FetchStrategyHelper.java similarity index 99% rename from hibernate-core/src/main/java/org/hibernate/persister/walking/internal/Helper.java rename to hibernate-core/src/main/java/org/hibernate/persister/walking/internal/FetchStrategyHelper.java index 29eb9f5dbe..7d6cf84335 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/walking/internal/Helper.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/internal/FetchStrategyHelper.java @@ -42,7 +42,7 @@ import org.hibernate.type.AssociationType; /** * @author Steve Ebersole */ -public class Helper { +public class FetchStrategyHelper { /** * Determine the fetch-style (if one) explicitly set for this association via fetch profiles. *

    diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationVisitationStrategy.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationVisitationStrategy.java index 285ec22270..6bf7d9a156 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationVisitationStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationVisitationStrategy.java @@ -55,6 +55,9 @@ public interface AssociationVisitationStrategy { public void startingComposite(CompositionDefinition compositionDefinition); public void finishingComposite(CompositionDefinition compositionDefinition); + public void startingCompositeElement(CompositionElementDefinition compositionElementDefinition); + public void finishingCompositeElement(CompositionElementDefinition compositionElementDefinition); + public boolean startingAttribute(AttributeDefinition attributeDefinition); public void finishingAttribute(AttributeDefinition attributeDefinition); } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CollectionElementDefinition.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CollectionElementDefinition.java index b42f8b0ddc..ff138f76f4 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CollectionElementDefinition.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CollectionElementDefinition.java @@ -35,5 +35,5 @@ public interface CollectionElementDefinition { public EntityDefinition toEntityDefinition(); - public CompositionDefinition toCompositeDefinition(); + public CompositionElementDefinition toCompositeElementDefinition(); } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CompositionElementDefinition.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CompositionElementDefinition.java new file mode 100644 index 0000000000..cd073881c8 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CompositionElementDefinition.java @@ -0,0 +1,31 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.persister.walking.spi; + +/** + * @author Gail Badner + */ +public interface CompositionElementDefinition extends CompositionDefinition{ + public CollectionDefinition getCollectionDefinition(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/MetadataDrivenModelGraphVisitor.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/MetadataDrivenModelGraphVisitor.java index 8a9ceb77a9..ffdccb2b99 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/MetadataDrivenModelGraphVisitor.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/MetadataDrivenModelGraphVisitor.java @@ -114,10 +114,10 @@ public class MetadataDrivenModelGraphVisitor { log.debug( "Visiting attribute path : " + subPath.getFullPath() ); final boolean continueWalk; - if ( attributeDefinition.getType().isAssociationType() ) { - continueWalk = - ! isDuplicateAssociation( ( (AssociationAttributeDefinition) attributeDefinition ).getAssociationKey() ) && - strategy.startingAttribute( attributeDefinition ); + if ( attributeDefinition.getType().isAssociationType() && + isDuplicateAssociationKey( ( (AssociationAttributeDefinition) attributeDefinition ).getAssociationKey() ) ) { + log.debug( "Property path deemed to be circular : " + subPath.getFullPath() ); + continueWalk = false; } else { continueWalk = strategy.startingAttribute( attributeDefinition ); @@ -143,6 +143,8 @@ public class MetadataDrivenModelGraphVisitor { private void visitAssociation(AssociationAttributeDefinition attribute) { // todo : do "too deep" checks; but see note about adding depth to PropertyPath + addAssociationKey( attribute.getAssociationKey() ); + if ( attribute.isCollection() ) { visitCollectionDefinition( attribute.toCollectionDefinition() ); } @@ -200,7 +202,7 @@ public class MetadataDrivenModelGraphVisitor { strategy.startingCollectionElements( elementDefinition ); if ( elementDefinition.getType().isComponentType() ) { - visitCompositeDefinition( elementDefinition.toCompositeDefinition() ); + visitCompositeElementDefinition( elementDefinition.toCompositeElementDefinition() ); } else if ( elementDefinition.getType().isEntityType() ) { visitEntityDefinition( elementDefinition.toEntityDefinition() ); @@ -209,18 +211,37 @@ public class MetadataDrivenModelGraphVisitor { strategy.finishingCollectionElements( elementDefinition ); } + private void visitCompositeElementDefinition(CompositionElementDefinition compositionElementDefinition) { + strategy.startingCompositeElement( compositionElementDefinition ); + + visitAttributes( compositionElementDefinition ); + + strategy.finishingCompositeElement( compositionElementDefinition ); + } private final Set visitedAssociationKeys = new HashSet(); - protected boolean isDuplicateAssociation(AssociationKey associationKey) { - boolean isDuplicate = !visitedAssociationKeys.add( associationKey ); - if ( isDuplicate ) { - log.debug( "Property path deemed to be circular : " + currentPropertyPath.getFullPath() ); - return true; - } - else { - return false; + /** + * Add association key to indicate the association is being visited. + * @param associationKey - the association key. + * @throws WalkingException if the association with the specified association key + * has already been visited. + */ + protected void addAssociationKey(AssociationKey associationKey) { + if ( ! visitedAssociationKeys.add( associationKey ) ) { + throw new WalkingException( + String.format( "Association has already been visited: %s", associationKey ) + ); } } + /** + * Has an association with the specified key been visited already? + * @param associationKey - the association key. + * @return true, if the association with the specified association key has already been visited; + * false, otherwise. + */ + protected boolean isDuplicateAssociationKey(AssociationKey associationKey) { + return visitedAssociationKeys.contains( associationKey ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositeBasedAttribute.java b/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositeBasedAttribute.java index a225b8a05f..9dbcbc7558 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositeBasedAttribute.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositeBasedAttribute.java @@ -39,7 +39,7 @@ public abstract class AbstractCompositeBasedAttribute private final int ownerAttributeNumber; public AbstractCompositeBasedAttribute( - AbstractCompositionDefinition source, + AbstractCompositionAttribute source, SessionFactoryImplementor sessionFactory, int attributeNumber, String attributeName, @@ -55,7 +55,7 @@ public abstract class AbstractCompositeBasedAttribute } @Override - public AbstractCompositionDefinition getSource() { - return (AbstractCompositionDefinition) super.getSource(); + public AbstractCompositionAttribute getSource() { + return (AbstractCompositionAttribute) super.getSource(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositionDefinition.java b/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositionAttribute.java similarity index 64% rename from hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositionDefinition.java rename to hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositionAttribute.java index 287c032ce8..6da786f81e 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositionDefinition.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositionAttribute.java @@ -48,9 +48,9 @@ import static org.hibernate.engine.internal.JoinHelper.getRHSColumnNames; /** * @author Steve Ebersole */ -public abstract class AbstractCompositionDefinition extends AbstractNonIdentifierAttribute implements +public abstract class AbstractCompositionAttribute extends AbstractNonIdentifierAttribute implements CompositionDefinition { - protected AbstractCompositionDefinition( + protected AbstractCompositionAttribute( AttributeSource source, SessionFactoryImplementor sessionFactory, int attributeNumber, @@ -72,21 +72,21 @@ public abstract class AbstractCompositionDefinition extends AbstractNonIdentifie public Iterator iterator() { return new Iterator() { private final int numberOfAttributes = getType().getSubtypes().length; - private int currentAttributeNumber = 0; + private int currentSubAttributeNumber = 0; private int currentColumnPosition = 0; @Override public boolean hasNext() { - return currentAttributeNumber < numberOfAttributes; + return currentSubAttributeNumber < numberOfAttributes; } @Override public AttributeDefinition next() { - final int attributeNumber = currentAttributeNumber; - currentAttributeNumber++; + final int subAttributeNumber = currentSubAttributeNumber; + currentSubAttributeNumber++; - final String name = getType().getPropertyNames()[attributeNumber]; - final Type type = getType().getSubtypes()[attributeNumber]; + final String name = getType().getPropertyNames()[subAttributeNumber]; + final Type type = getType().getSubtypes()[subAttributeNumber]; int columnPosition = currentColumnPosition; currentColumnPosition += type.getColumnSpan( sessionFactory() ); @@ -101,13 +101,13 @@ public abstract class AbstractCompositionDefinition extends AbstractNonIdentifie getLHSTableName( aType, attributeNumber(), - (OuterJoinLoadable) joinable + (OuterJoinLoadable) locateOwningPersister() ), getLHSColumnNames( aType, attributeNumber(), columnPosition, - (OuterJoinLoadable) joinable, + (OuterJoinLoadable) locateOwningPersister(), sessionFactory() ) ); @@ -120,63 +120,63 @@ public abstract class AbstractCompositionDefinition extends AbstractNonIdentifie } return new CompositeBasedAssociationAttribute( - AbstractCompositionDefinition.this, + AbstractCompositionAttribute.this, sessionFactory(), - currentAttributeNumber, + subAttributeNumber, name, (AssociationType) type, new BaselineAttributeInformation.Builder() - .setInsertable( AbstractCompositionDefinition.this.isInsertable() ) - .setUpdateable( AbstractCompositionDefinition.this.isUpdateable() ) - .setInsertGenerated( AbstractCompositionDefinition.this.isInsertGenerated() ) - .setUpdateGenerated( AbstractCompositionDefinition.this.isUpdateGenerated() ) - .setNullable( getType().getPropertyNullability()[currentAttributeNumber] ) + .setInsertable( AbstractCompositionAttribute.this.isInsertable() ) + .setUpdateable( AbstractCompositionAttribute.this.isUpdateable() ) + .setInsertGenerated( AbstractCompositionAttribute.this.isInsertGenerated() ) + .setUpdateGenerated( AbstractCompositionAttribute.this.isUpdateGenerated() ) + .setNullable( getType().getPropertyNullability()[subAttributeNumber] ) .setDirtyCheckable( true ) - .setVersionable( AbstractCompositionDefinition.this.isVersionable() ) - .setCascadeStyle( getType().getCascadeStyle( currentAttributeNumber ) ) - .setFetchMode( getType().getFetchMode( currentAttributeNumber ) ) + .setVersionable( AbstractCompositionAttribute.this.isVersionable() ) + .setCascadeStyle( getType().getCascadeStyle( subAttributeNumber ) ) + .setFetchMode( getType().getFetchMode( subAttributeNumber ) ) .createInformation(), - AbstractCompositionDefinition.this.attributeNumber(), + AbstractCompositionAttribute.this.attributeNumber(), associationKey ); } else if ( type.isComponentType() ) { return new CompositionBasedCompositionAttribute( - AbstractCompositionDefinition.this, + AbstractCompositionAttribute.this, sessionFactory(), - currentAttributeNumber, + subAttributeNumber, name, (CompositeType) type, new BaselineAttributeInformation.Builder() - .setInsertable( AbstractCompositionDefinition.this.isInsertable() ) - .setUpdateable( AbstractCompositionDefinition.this.isUpdateable() ) - .setInsertGenerated( AbstractCompositionDefinition.this.isInsertGenerated() ) - .setUpdateGenerated( AbstractCompositionDefinition.this.isUpdateGenerated() ) - .setNullable( getType().getPropertyNullability()[currentAttributeNumber] ) + .setInsertable( AbstractCompositionAttribute.this.isInsertable() ) + .setUpdateable( AbstractCompositionAttribute.this.isUpdateable() ) + .setInsertGenerated( AbstractCompositionAttribute.this.isInsertGenerated() ) + .setUpdateGenerated( AbstractCompositionAttribute.this.isUpdateGenerated() ) + .setNullable( getType().getPropertyNullability()[subAttributeNumber] ) .setDirtyCheckable( true ) - .setVersionable( AbstractCompositionDefinition.this.isVersionable() ) - .setCascadeStyle( getType().getCascadeStyle( currentAttributeNumber ) ) - .setFetchMode( getType().getFetchMode( currentAttributeNumber ) ) + .setVersionable( AbstractCompositionAttribute.this.isVersionable() ) + .setCascadeStyle( getType().getCascadeStyle( subAttributeNumber ) ) + .setFetchMode( getType().getFetchMode( subAttributeNumber ) ) .createInformation() ); } else { return new CompositeBasedBasicAttribute( - AbstractCompositionDefinition.this, + AbstractCompositionAttribute.this, sessionFactory(), - currentAttributeNumber, + subAttributeNumber, name, type, new BaselineAttributeInformation.Builder() - .setInsertable( AbstractCompositionDefinition.this.isInsertable() ) - .setUpdateable( AbstractCompositionDefinition.this.isUpdateable() ) - .setInsertGenerated( AbstractCompositionDefinition.this.isInsertGenerated() ) - .setUpdateGenerated( AbstractCompositionDefinition.this.isUpdateGenerated() ) - .setNullable( getType().getPropertyNullability()[currentAttributeNumber] ) + .setInsertable( AbstractCompositionAttribute.this.isInsertable() ) + .setUpdateable( AbstractCompositionAttribute.this.isUpdateable() ) + .setInsertGenerated( AbstractCompositionAttribute.this.isInsertGenerated() ) + .setUpdateGenerated( AbstractCompositionAttribute.this.isUpdateGenerated() ) + .setNullable( getType().getPropertyNullability()[subAttributeNumber] ) .setDirtyCheckable( true ) - .setVersionable( AbstractCompositionDefinition.this.isVersionable() ) - .setCascadeStyle( getType().getCascadeStyle( currentAttributeNumber ) ) - .setFetchMode( getType().getFetchMode( currentAttributeNumber ) ) + .setVersionable( AbstractCompositionAttribute.this.isVersionable() ) + .setCascadeStyle( getType().getCascadeStyle( subAttributeNumber ) ) + .setFetchMode( getType().getFetchMode( subAttributeNumber ) ) .createInformation() ); } @@ -196,7 +196,7 @@ public abstract class AbstractCompositionDefinition extends AbstractNonIdentifie return ( (EntityDefinition) getSource() ).getEntityPersister(); } else { - return ( (AbstractCompositionDefinition) getSource() ).locateOwningPersister(); + return ( (AbstractCompositionAttribute) getSource() ).locateOwningPersister(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositeBasedAssociationAttribute.java b/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositeBasedAssociationAttribute.java index 320599b5c7..72eb5eac0b 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositeBasedAssociationAttribute.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositeBasedAssociationAttribute.java @@ -35,7 +35,7 @@ import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.Joinable; import org.hibernate.persister.spi.HydratedCompoundValueHandler; -import org.hibernate.persister.walking.internal.Helper; +import org.hibernate.persister.walking.internal.FetchStrategyHelper; import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; import org.hibernate.persister.walking.spi.AssociationKey; import org.hibernate.persister.walking.spi.CollectionDefinition; @@ -55,7 +55,7 @@ public class CompositeBasedAssociationAttribute private Joinable joinable; public CompositeBasedAssociationAttribute( - AbstractCompositionDefinition source, + AbstractCompositionAttribute source, SessionFactoryImplementor factory, int attributeNumber, String attributeName, @@ -130,7 +130,7 @@ public class CompositeBasedAssociationAttribute EntityPersister owningPersister, PropertyPath propertyPath, int ownerAttributeNumber) { - return Helper.determineFetchStyleByProfile( + return FetchStrategyHelper.determineFetchStyleByProfile( loadQueryInfluencers, owningPersister, propertyPath, @@ -139,11 +139,11 @@ public class CompositeBasedAssociationAttribute } protected FetchStyle determineFetchStyleByMetadata(FetchMode fetchMode, AssociationType type) { - return Helper.determineFetchStyleByMetadata( fetchMode, type, sessionFactory() ); + return FetchStrategyHelper.determineFetchStyleByMetadata( fetchMode, type, sessionFactory() ); } private FetchTiming determineFetchTiming(FetchStyle style) { - return Helper.determineFetchTiming( style, getType(), sessionFactory() ); + return FetchStrategyHelper.determineFetchTiming( style, getType(), sessionFactory() ); } private EntityPersister locateOwningPersister() { diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositionBasedCompositionAttribute.java b/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositionBasedCompositionAttribute.java index 7beee25984..4a154f130d 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositionBasedCompositionAttribute.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositionBasedCompositionAttribute.java @@ -32,7 +32,7 @@ import org.hibernate.type.CompositeType; * @author Steve Ebersole */ public class CompositionBasedCompositionAttribute - extends AbstractCompositionDefinition + extends AbstractCompositionAttribute implements CompositionDefinition { public CompositionBasedCompositionAttribute( CompositionDefinition source, diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedAssociationAttribute.java b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedAssociationAttribute.java index 5e1d31ed40..1954302738 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedAssociationAttribute.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedAssociationAttribute.java @@ -34,7 +34,7 @@ import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.Joinable; import org.hibernate.persister.entity.OuterJoinLoadable; import org.hibernate.persister.spi.HydratedCompoundValueHandler; -import org.hibernate.persister.walking.internal.Helper; +import org.hibernate.persister.walking.internal.FetchStrategyHelper; import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; import org.hibernate.persister.walking.spi.AssociationKey; import org.hibernate.persister.walking.spi.CollectionDefinition; @@ -129,22 +129,22 @@ public class EntityBasedAssociationAttribute public FetchStrategy determineFetchPlan(LoadQueryInfluencers loadQueryInfluencers, PropertyPath propertyPath) { final EntityPersister owningPersister = getSource().getEntityPersister(); - FetchStyle style = Helper.determineFetchStyleByProfile( + FetchStyle style = FetchStrategyHelper.determineFetchStyleByProfile( loadQueryInfluencers, owningPersister, propertyPath, attributeNumber() ); if ( style == null ) { - style = Helper.determineFetchStyleByMetadata( - ((OuterJoinLoadable) getSource().getEntityPersister()).getFetchMode( attributeNumber() ), + style = FetchStrategyHelper.determineFetchStyleByMetadata( + ( (OuterJoinLoadable) getSource().getEntityPersister() ).getFetchMode( attributeNumber() ), getType(), sessionFactory() ); } return new FetchStrategy( - Helper.determineFetchTiming( style, getType(), sessionFactory() ), + FetchStrategyHelper.determineFetchTiming( style, getType(), sessionFactory() ), style ); } diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedCompositionAttribute.java b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedCompositionAttribute.java index e283cae94b..66a7242a5f 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedCompositionAttribute.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedCompositionAttribute.java @@ -25,7 +25,7 @@ package org.hibernate.tuple.entity; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.tuple.component.AbstractCompositionDefinition; +import org.hibernate.tuple.component.AbstractCompositionAttribute; import org.hibernate.persister.walking.spi.CompositionDefinition; import org.hibernate.tuple.BaselineAttributeInformation; import org.hibernate.type.CompositeType; @@ -34,7 +34,7 @@ import org.hibernate.type.CompositeType; * @author Steve Ebersole */ public class EntityBasedCompositionAttribute - extends AbstractCompositionDefinition + extends AbstractCompositionAttribute implements CompositionDefinition { public EntityBasedCompositionAttribute( diff --git a/hibernate-core/src/main/java/org/hibernate/type/EntityType.java b/hibernate-core/src/main/java/org/hibernate/type/EntityType.java old mode 100755 new mode 100644 index a4bd059dee..0c91ee2584 --- a/hibernate-core/src/main/java/org/hibernate/type/EntityType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/EntityType.java @@ -23,6 +23,7 @@ */ package org.hibernate.type; + import java.io.Serializable; import java.sql.ResultSet; import java.sql.SQLException; @@ -34,7 +35,11 @@ import org.hibernate.Filter; import org.hibernate.HibernateException; import org.hibernate.MappingException; import org.hibernate.engine.internal.ForeignKeys; -import org.hibernate.engine.spi.*; +import org.hibernate.engine.spi.EntityUniqueKey; +import org.hibernate.engine.spi.Mapping; +import org.hibernate.engine.spi.PersistenceContext; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.internal.util.ReflectHelper; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.Joinable; @@ -53,6 +58,7 @@ public abstract class EntityType extends AbstractType implements AssociationType protected final String uniqueKeyPropertyName; private final boolean eager; private final boolean unwrapProxy; + private final boolean referenceToPrimaryKey; private transient Class returnedClass; @@ -67,29 +73,46 @@ public abstract class EntityType extends AbstractType implements AssociationType * @param unwrapProxy Is unwrapping of proxies allowed for this association; unwrapping * says to return the "implementation target" of lazy prooxies; typically only possible * with lazy="no-proxy". + * + * @deprecated Use {@link #EntityType(org.hibernate.type.TypeFactory.TypeScope, String, boolean, String, boolean, boolean)} instead. + * See Jira issue: HHH-7771 */ + @Deprecated protected EntityType( TypeFactory.TypeScope scope, String entityName, String uniqueKeyPropertyName, boolean eager, boolean unwrapProxy) { - this(scope, entityName, uniqueKeyPropertyName, eager, unwrapProxy, null); + this( scope, entityName, uniqueKeyPropertyName == null, uniqueKeyPropertyName, eager, unwrapProxy ); } + /** + * Constructs the requested entity type mapping. + * + * @param scope The type scope + * @param entityName The name of the associated entity. + * @param referenceToPrimaryKey True if association references a primary key. + * @param uniqueKeyPropertyName The property-ref name, or null if we + * reference the PK of the associated entity. + * @param eager Is eager fetching enabled. + * @param unwrapProxy Is unwrapping of proxies allowed for this association; unwrapping + * says to return the "implementation target" of lazy prooxies; typically only possible + * with lazy="no-proxy". + */ protected EntityType( TypeFactory.TypeScope scope, String entityName, + boolean referenceToPrimaryKey, String uniqueKeyPropertyName, boolean eager, - boolean unwrapProxy, - Class returnedClass) { + boolean unwrapProxy) { this.scope = scope; this.associatedEntityName = entityName; this.uniqueKeyPropertyName = uniqueKeyPropertyName; this.eager = eager; this.unwrapProxy = unwrapProxy; - this.returnedClass = returnedClass; + this.referenceToPrimaryKey = referenceToPrimaryKey; } @@ -146,11 +169,13 @@ public abstract class EntityType extends AbstractType implements AssociationType * @return True if this association reference the PK of the associated entity. */ public boolean isReferenceToPrimaryKey() { - return uniqueKeyPropertyName==null; + return referenceToPrimaryKey; } public String getRHSUniqueKeyPropertyName() { - return uniqueKeyPropertyName; + // Return null if this type references a PK. This is important for + // associations' use of mappedBy referring to a derived ID. + return referenceToPrimaryKey ? null : uniqueKeyPropertyName; } public String getLHSPropertyName() { @@ -211,7 +236,7 @@ public abstract class EntityType extends AbstractType implements AssociationType private Class determineAssociatedEntityClass() { final String entityName = getAssociatedEntityName(); try { - return ReflectHelper.classForName(entityName); + return ReflectHelper.classForName( entityName ); } catch ( ClassNotFoundException cnfe ) { return this.scope.resolveFactory().getEntityPersister(entityName). @@ -314,7 +339,7 @@ public abstract class EntityType extends AbstractType implements AssociationType } final Serializable id; - if (x instanceof HibernateProxy) { + if (x instanceof HibernateProxy ) { id = ( (HibernateProxy) x ).getHibernateLazyInitializer().getIdentifier(); } else { @@ -397,14 +422,15 @@ public abstract class EntityType extends AbstractType implements AssociationType if ( isNull( owner, session ) ) { return null; //EARLY EXIT! } - if ( isReferenceToPrimaryKey() ) { return resolveIdentifier( (Serializable) value, session ); } - else { + else if ( uniqueKeyPropertyName != null ) { return loadByUniqueKey( getAssociatedEntityName(), uniqueKeyPropertyName, value, session ); } } + + return null; } public Type getSemiResolvedType(SessionFactoryImplementor factory) { @@ -412,7 +438,7 @@ public abstract class EntityType extends AbstractType implements AssociationType } protected final Object getIdentifier(Object value, SessionImplementor session) throws HibernateException { - if ( isReferenceToPrimaryKey() ) { + if ( isReferenceToPrimaryKey() || uniqueKeyPropertyName == null) { return ForeignKeys.getEntityIdentifierIfNotUnsaved( getAssociatedEntityName(), value, session ); //tolerates nulls } else if ( value == null ) { @@ -516,7 +542,7 @@ public abstract class EntityType extends AbstractType implements AssociationType * or unique key property name. */ public final Type getIdentifierOrUniqueKeyType(Mapping factory) throws MappingException { - if ( isReferenceToPrimaryKey() ) { + if ( isReferenceToPrimaryKey() || uniqueKeyPropertyName == null ) { return getIdentifierType(factory); } else { @@ -538,7 +564,7 @@ public abstract class EntityType extends AbstractType implements AssociationType */ public final String getIdentifierOrUniqueKeyPropertyName(Mapping factory) throws MappingException { - if ( isReferenceToPrimaryKey() ) { + if ( isReferenceToPrimaryKey() || uniqueKeyPropertyName == null ) { return factory.getIdentifierPropertyName( getAssociatedEntityName() ); } else { diff --git a/hibernate-core/src/main/java/org/hibernate/type/ManyToOneType.java b/hibernate-core/src/main/java/org/hibernate/type/ManyToOneType.java index 22e7e33a13..9e16f42d93 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/ManyToOneType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/ManyToOneType.java @@ -67,9 +67,15 @@ public class ManyToOneType extends EntityType { * @param lazy Should the association be handled lazily */ public ManyToOneType(TypeFactory.TypeScope scope, String referencedEntityName, boolean lazy) { - this( scope, referencedEntityName, null, lazy, true, false, false ); + this( scope, referencedEntityName, true, null, lazy, true, false, false ); } + + /** + * @deprecated Use {@link #ManyToOneType(TypeFactory.TypeScope, String, boolean, String, boolean, boolean, boolean, boolean ) } instead. + * See Jira issue: HHH-7771 + */ + @Deprecated public ManyToOneType( TypeFactory.TypeScope scope, String referencedEntityName, @@ -78,20 +84,18 @@ public class ManyToOneType extends EntityType { boolean unwrapProxy, boolean ignoreNotFound, boolean isLogicalOneToOne) { - super( scope, referencedEntityName, uniqueKeyPropertyName, !lazy, unwrapProxy ); - this.ignoreNotFound = ignoreNotFound; - this.isLogicalOneToOne = isLogicalOneToOne; + this( scope, referencedEntityName, uniqueKeyPropertyName == null, uniqueKeyPropertyName, lazy, unwrapProxy, ignoreNotFound, isLogicalOneToOne ); } public ManyToOneType( TypeFactory.TypeScope scope, String referencedEntityName, + boolean referenceToPrimaryKey, String uniqueKeyPropertyName, boolean lazy, boolean unwrapProxy, boolean ignoreNotFound, - boolean isLogicalOneToOne, - Class returnedClass) { - super( scope, referencedEntityName, uniqueKeyPropertyName, !lazy, unwrapProxy, returnedClass ); + boolean isLogicalOneToOne) { + super( scope, referencedEntityName, referenceToPrimaryKey, uniqueKeyPropertyName, !lazy, unwrapProxy ); this.ignoreNotFound = ignoreNotFound; this.isLogicalOneToOne = isLogicalOneToOne; } diff --git a/hibernate-core/src/main/java/org/hibernate/type/OneToOneType.java b/hibernate-core/src/main/java/org/hibernate/type/OneToOneType.java index 4c16fd0679..9783148b13 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/OneToOneType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/OneToOneType.java @@ -47,6 +47,12 @@ public class OneToOneType extends EntityType { private final String propertyName; private final String entityName; + /** + * @deprecated Use {@link #OneToOneType(TypeFactory.TypeScope, String, ForeignKeyDirection, boolean, String, boolean, boolean, String, String)} + * instead. + * See Jira issue: HHH-7771 + */ + @Deprecated public OneToOneType( TypeFactory.TypeScope scope, String referencedEntityName, @@ -56,23 +62,20 @@ public class OneToOneType extends EntityType { boolean unwrapProxy, String entityName, String propertyName) { - super( scope, referencedEntityName, uniqueKeyPropertyName, !lazy, unwrapProxy ); - this.foreignKeyType = foreignKeyType; - this.propertyName = propertyName; - this.entityName = entityName; + this( scope, referencedEntityName, foreignKeyType, uniqueKeyPropertyName == null, uniqueKeyPropertyName, lazy, unwrapProxy, entityName, propertyName ); } public OneToOneType( TypeFactory.TypeScope scope, String referencedEntityName, ForeignKeyDirection foreignKeyType, + boolean referenceToPrimaryKey, String uniqueKeyPropertyName, boolean lazy, boolean unwrapProxy, String entityName, - String propertyName, - Class returnedClass) { - super( scope, referencedEntityName, uniqueKeyPropertyName, !lazy, unwrapProxy, returnedClass ); + String propertyName) { + super( scope, referencedEntityName, referenceToPrimaryKey, uniqueKeyPropertyName, !lazy, unwrapProxy ); this.foreignKeyType = foreignKeyType; this.propertyName = propertyName; this.entityName = entityName; diff --git a/hibernate-core/src/main/java/org/hibernate/type/SpecialOneToOneType.java b/hibernate-core/src/main/java/org/hibernate/type/SpecialOneToOneType.java old mode 100755 new mode 100644 index b97ff8a002..8fd9d6e8ce --- a/hibernate-core/src/main/java/org/hibernate/type/SpecialOneToOneType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/SpecialOneToOneType.java @@ -43,6 +43,10 @@ import org.hibernate.metamodel.spi.relational.Size; */ public class SpecialOneToOneType extends OneToOneType { + /** + * @deprecated Use {@link #SpecialOneToOneType(org.hibernate.type.TypeFactory.TypeScope, String, ForeignKeyDirection, boolean, String, boolean, boolean, String, String)} instead. + */ + @Deprecated public SpecialOneToOneType( TypeFactory.TypeScope scope, String referencedEntityName, @@ -52,10 +56,24 @@ public class SpecialOneToOneType extends OneToOneType { boolean unwrapProxy, String entityName, String propertyName) { + this( scope, referencedEntityName, foreignKeyType, uniqueKeyPropertyName == null, uniqueKeyPropertyName, lazy, unwrapProxy, entityName, propertyName ); + } + + public SpecialOneToOneType( + TypeFactory.TypeScope scope, + String referencedEntityName, + ForeignKeyDirection foreignKeyType, + boolean referenceToPrimaryKey, + String uniqueKeyPropertyName, + boolean lazy, + boolean unwrapProxy, + String entityName, + String propertyName) { super( scope, referencedEntityName, - foreignKeyType, + foreignKeyType, + referenceToPrimaryKey, uniqueKeyPropertyName, lazy, unwrapProxy, diff --git a/hibernate-core/src/main/java/org/hibernate/type/TypeFactory.java b/hibernate-core/src/main/java/org/hibernate/type/TypeFactory.java index b2606eb69c..2f81dcd8ba 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/TypeFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/type/TypeFactory.java @@ -27,8 +27,6 @@ import java.io.Serializable; import java.util.Comparator; import java.util.Properties; -import org.jboss.logging.Logger; - import org.hibernate.HibernateException; import org.hibernate.MappingException; import org.hibernate.classic.Lifecycle; @@ -39,6 +37,7 @@ import org.hibernate.tuple.component.ComponentMetamodel; import org.hibernate.usertype.CompositeUserType; import org.hibernate.usertype.ParameterizedType; import org.hibernate.usertype.UserType; +import org.jboss.logging.Logger; /** * Used internally to build instances of {@link Type}, specifically it builds instances of @@ -208,6 +207,11 @@ public final class TypeFactory implements Serializable { // one-to-one type builders ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + /** + * @deprecated Use {@link #oneToOne(String, ForeignKeyDirection, String, boolean, boolean, String, String, boolean)} instead. + * See Jira issue: HHH-7771 + */ + @Deprecated public EntityType oneToOne( String persistentClass, ForeignKeyDirection foreignKeyType, @@ -216,10 +220,28 @@ public final class TypeFactory implements Serializable { boolean unwrapProxy, String entityName, String propertyName) { - return new OneToOneType( typeScope, persistentClass, foreignKeyType, uniqueKeyPropertyName, - lazy, unwrapProxy, entityName, propertyName ); + return oneToOne( persistentClass, foreignKeyType, uniqueKeyPropertyName == null, uniqueKeyPropertyName, lazy, unwrapProxy, entityName, + propertyName ); } + + public EntityType oneToOne( + String persistentClass, + ForeignKeyDirection foreignKeyType, + boolean referenceToPrimaryKey, + String uniqueKeyPropertyName, + boolean lazy, + boolean unwrapProxy, + String entityName, + String propertyName) { + return new OneToOneType( typeScope, persistentClass, foreignKeyType, referenceToPrimaryKey, + uniqueKeyPropertyName, lazy, unwrapProxy, entityName, propertyName ); + } + + /** + * @deprecated Use {@link #specialOneToOne(String, ForeignKeyDirection, String, boolean, boolean, String, String, boolean)} instead. + */ + @Deprecated public EntityType specialOneToOne( String persistentClass, ForeignKeyDirection foreignKeyType, @@ -228,34 +250,21 @@ public final class TypeFactory implements Serializable { boolean unwrapProxy, String entityName, String propertyName) { - return new SpecialOneToOneType( typeScope, persistentClass, foreignKeyType, uniqueKeyPropertyName, - lazy, unwrapProxy, entityName, propertyName ); - } - - public EntityType oneToOne( - String persistentClass, - ForeignKeyDirection foreignKeyType, - String uniqueKeyPropertyName, - boolean lazy, - boolean unwrapProxy, - String entityName, - String propertyName, - Class returnedClass) { - return new OneToOneType( typeScope, persistentClass, foreignKeyType, uniqueKeyPropertyName, - lazy, unwrapProxy, entityName, propertyName, returnedClass ); + return specialOneToOne( persistentClass, foreignKeyType, uniqueKeyPropertyName == null, uniqueKeyPropertyName, lazy, unwrapProxy, + entityName, propertyName ); } public EntityType specialOneToOne( String persistentClass, ForeignKeyDirection foreignKeyType, + boolean referenceToPrimaryKey, String uniqueKeyPropertyName, boolean lazy, boolean unwrapProxy, String entityName, - String propertyName, - Class returnedClass) { - return new SpecialOneToOneType( typeScope, persistentClass, foreignKeyType, uniqueKeyPropertyName, - lazy, unwrapProxy, entityName, propertyName, returnedClass ); + String propertyName) { + return new SpecialOneToOneType( typeScope, persistentClass, foreignKeyType, referenceToPrimaryKey, + uniqueKeyPropertyName, lazy, unwrapProxy, entityName, propertyName ); } @@ -269,6 +278,11 @@ public final class TypeFactory implements Serializable { return new ManyToOneType( typeScope, persistentClass, lazy ); } + /** + * @deprecated Use {@link #manyToOne(String, boolean, String, boolean, boolean, boolean, boolean)} instead. + * See Jira issue: HHH-7771 + */ + @Deprecated public EntityType manyToOne( String persistentClass, String uniqueKeyPropertyName, @@ -276,9 +290,23 @@ public final class TypeFactory implements Serializable { boolean unwrapProxy, boolean ignoreNotFound, boolean isLogicalOneToOne) { + return manyToOne( persistentClass, uniqueKeyPropertyName == null, uniqueKeyPropertyName, lazy, unwrapProxy, ignoreNotFound, + isLogicalOneToOne ); + } + + + public EntityType manyToOne( + String persistentClass, + boolean referenceToPrimaryKey, + String uniqueKeyPropertyName, + boolean lazy, + boolean unwrapProxy, + boolean ignoreNotFound, + boolean isLogicalOneToOne) { return new ManyToOneType( typeScope, persistentClass, + referenceToPrimaryKey, uniqueKeyPropertyName, lazy, unwrapProxy, @@ -287,26 +315,6 @@ public final class TypeFactory implements Serializable { ); } - public EntityType manyToOne( - String persistentClass, - String uniqueKeyPropertyName, - boolean lazy, - boolean unwrapProxy, - boolean ignoreNotFound, - boolean isLogicalOneToOne, - Class returnedClass) { - return new ManyToOneType( - typeScope, - persistentClass, - uniqueKeyPropertyName, - lazy, - unwrapProxy, - ignoreNotFound, - isLogicalOneToOne , - returnedClass - ); - } - // collection type builders ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public CollectionType array(String role, String propertyRef, Class elementClass) { diff --git a/hibernate-core/src/test/java/org/hibernate/loader/EncapsulatedCompositeAttributeResultSetProcessorTest.java b/hibernate-core/src/test/java/org/hibernate/loader/EncapsulatedCompositeAttributeResultSetProcessorTest.java new file mode 100644 index 0000000000..ca0cd1aeab --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/loader/EncapsulatedCompositeAttributeResultSetProcessorTest.java @@ -0,0 +1,352 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.loader; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import javax.persistence.Column; +import javax.persistence.ElementCollection; +import javax.persistence.Embeddable; +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.ManyToOne; + +import org.junit.Test; + +import org.hibernate.Session; +import org.hibernate.engine.spi.LoadQueryInfluencers; +import org.hibernate.engine.spi.QueryParameters; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.jdbc.Work; +import org.hibernate.loader.internal.EntityLoadQueryBuilderImpl; +import org.hibernate.loader.internal.LoadQueryAliasResolutionContextImpl; +import org.hibernate.loader.internal.ResultSetProcessorImpl; +import org.hibernate.loader.plan.internal.SingleRootReturnLoadPlanBuilderStrategy; +import org.hibernate.loader.plan.spi.LoadPlan; +import org.hibernate.loader.plan.spi.build.LoadPlanBuilder; +import org.hibernate.loader.spi.LoadQueryAliasResolutionContext; +import org.hibernate.loader.spi.NamedParameterContext; +import org.hibernate.loader.spi.NoOpLoadPlanAdvisor; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.test.component.cascading.toone.PersonalInfo; +import org.hibernate.testing.FailureExpected; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.hibernate.testing.junit4.ExtraAssertions; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; + +/** + * @author Gail Badner + */ +public class EncapsulatedCompositeAttributeResultSetProcessorTest extends BaseCoreFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Person.class, Customer.class }; + } + + @Test + public void testSimpleNestedCompositeAttributeProcessing() throws Exception { + // create some test data + Session session = openSession(); + session.beginTransaction(); + Person person = new Person(); + person.id = 1; + person.name = "Joe Blow"; + person.address = new Address(); + person.address.address1 = "1313 Mockingbird Lane"; + person.address.city = "Pleasantville"; + person.address.country = "USA"; + AddressType addressType = new AddressType(); + addressType.typeName = "snail mail"; + person.address.type = addressType; + session.save( person ); + session.getTransaction().commit(); + session.close(); + + session = openSession(); + session.beginTransaction(); + Person personGotten = (Person) session.get( Person.class, person.id ); + assertEquals( person.id, personGotten.id ); + assertEquals( person.address.address1, personGotten.address.address1 ); + assertEquals( person.address.city, personGotten.address.city ); + assertEquals( person.address.country, personGotten.address.country ); + assertEquals( person.address.type.typeName, personGotten.address.type.typeName ); + session.getTransaction().commit(); + session.close(); + + List results = getResults( sessionFactory().getEntityPersister( Person.class.getName() ) ); + assertEquals( 1, results.size() ); + Object result = results.get( 0 ); + assertNotNull( result ); + + Person personWork = ExtraAssertions.assertTyping( Person.class, result ); + assertEquals( person.id, personWork.id ); + assertEquals( person.address.address1, personWork.address.address1 ); + assertEquals( person.address.city, personWork.address.city ); + assertEquals( person.address.country, personWork.address.country ); + assertEquals( person.address.type.typeName, personGotten.address.type.typeName ); + + // clean up test data + session = openSession(); + session.beginTransaction(); + session.createQuery( "delete Person" ).executeUpdate(); + session.getTransaction().commit(); + session.close(); + } + + @Test + public void testNestedCompositeElementCollectionProcessing() throws Exception { + // create some test data + Session session = openSession(); + session.beginTransaction(); + Person person = new Person(); + person.id = 1; + person.name = "Joe Blow"; + session.save( person ); + Customer customer = new Customer(); + customer.id = 1L; + Investment investment1 = new Investment(); + investment1.description = "stock"; + investment1.date = new Date(); + investment1.monetaryAmount = new MonetaryAmount(); + investment1.monetaryAmount.currency = MonetaryAmount.CurrencyCode.USD; + investment1.monetaryAmount.amount = BigDecimal.valueOf( 1234, 2 ); + investment1.performedBy = person; + Investment investment2 = new Investment(); + investment2.description = "bond"; + investment2.date = new Date(); + investment2.monetaryAmount = new MonetaryAmount(); + investment2.monetaryAmount.currency = MonetaryAmount.CurrencyCode.EUR; + investment2.monetaryAmount.amount = BigDecimal.valueOf( 98176, 1 ); + customer.investments.add( investment1 ); + customer.investments.add( investment2 ); + session.save( customer ); + session.getTransaction().commit(); + session.close(); + + session = openSession(); + session.beginTransaction(); + Customer customerGotten = (Customer) session.get( Customer.class, customer.id ); + assertEquals( customer.id, customerGotten.id ); + session.getTransaction().commit(); + session.close(); + + List results = getResults( sessionFactory().getEntityPersister( Customer.class.getName() ) ); + + assertEquals( 2, results.size() ); + assertSame( results.get( 0 ), results.get( 1 ) ); + Object result = results.get( 0 ); + assertNotNull( result ); + + Customer customerWork = ExtraAssertions.assertTyping( Customer.class, result ); + + // clean up test data + session = openSession(); + session.beginTransaction(); + session.delete( customerWork.investments.get( 0 ).performedBy ); + session.delete( customerWork ); + session.getTransaction().commit(); + session.close(); + } + + private List getResults(EntityPersister entityPersister ) { + final SingleRootReturnLoadPlanBuilderStrategy strategy = new SingleRootReturnLoadPlanBuilderStrategy( + sessionFactory(), + LoadQueryInfluencers.NONE + ); + final LoadPlan plan = LoadPlanBuilder.buildRootEntityLoadPlan( strategy, entityPersister ); + final LoadQueryAliasResolutionContext aliasResolutionContext = + new LoadQueryAliasResolutionContextImpl( + sessionFactory(), + 0, + Collections.singletonMap( plan.getReturns().get( 0 ), new String[] { "abc" } ) + ); + final EntityLoadQueryBuilderImpl queryBuilder = new EntityLoadQueryBuilderImpl( + LoadQueryInfluencers.NONE, + plan + ); + final String sql = queryBuilder.generateSql( 1, sessionFactory(), aliasResolutionContext ); + + final ResultSetProcessorImpl resultSetProcessor = new ResultSetProcessorImpl( plan ); + final List results = new ArrayList(); + + final Session workSession = openSession(); + workSession.beginTransaction(); + workSession.doWork( + new Work() { + @Override + public void execute(Connection connection) throws SQLException { + PreparedStatement ps = connection.prepareStatement( sql ); + ps.setInt( 1, 1 ); + ResultSet resultSet = ps.executeQuery(); + results.addAll( + resultSetProcessor.extractResults( + NoOpLoadPlanAdvisor.INSTANCE, + resultSet, + (SessionImplementor) workSession, + new QueryParameters(), + new NamedParameterContext() { + @Override + public int[] getNamedParameterLocations(String name) { + return new int[0]; + } + }, + aliasResolutionContext, + true, + false, + null, + null + ) + ); + resultSet.close(); + ps.close(); + } + } + ); + workSession.getTransaction().commit(); + workSession.close(); + return results; + } + + @Entity( name = "Person" ) + public static class Person implements Serializable { + @Id + Integer id; + String name; + + @Embedded + Address address; + } + + @Embeddable + public static class Address implements Serializable { + String address1; + String city; + String country; + AddressType type; + } + + @Embeddable + public static class AddressType { + String typeName; + } + + @Entity( name = "Customer" ) + public static class Customer { + private Long id; + private List investments = new ArrayList(); + + @Id + public Long getId() { + return id; + } + public void setId(Long id) { + this.id = id; + } + + @ElementCollection(fetch = FetchType.EAGER) + public List getInvestments() { + return investments; + } + public void setInvestments(List investments) { + this.investments = investments; + } + } + + @Embeddable + public static class Investment { + private MonetaryAmount monetaryAmount; + private String description; + private Date date; + private Person performedBy; + + @Embedded + public MonetaryAmount getMonetaryAmount() { + return monetaryAmount; + } + public void setMonetaryAmount(MonetaryAmount monetaryAmount) { + this.monetaryAmount = monetaryAmount; + } + public String getDescription() { + return description; + } + public void setDescription(String description) { + this.description = description; + } + public Date getDate() { + return date; + } + public void setDate(Date date) { + this.date = date; + } + @ManyToOne + public Person getPerformedBy() { + return performedBy; + } + public void setPerformedBy(Person performedBy) { + this.performedBy = performedBy; + } + } + + @Embeddable + public static class MonetaryAmount { + public static enum CurrencyCode { + USD, + EUR + } + private BigDecimal amount; + @Column(length = 3) + @Enumerated(EnumType.STRING) + private CurrencyCode currency; + + public BigDecimal getAmount() { + return amount; + } + public void setAmount(BigDecimal amount) { + this.amount = amount; + } + + public CurrencyCode getCurrency() { + return currency; + } + public void setCurrency(CurrencyCode currency) { + this.currency = currency; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/loader/EncapsulatedCompositeIdResultSetProcessorTest.java b/hibernate-core/src/test/java/org/hibernate/loader/EncapsulatedCompositeIdResultSetProcessorTest.java new file mode 100644 index 0000000000..2482fcf4ca --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/loader/EncapsulatedCompositeIdResultSetProcessorTest.java @@ -0,0 +1,434 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.loader; + +import java.io.Serializable; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import javax.persistence.Embeddable; +import javax.persistence.EmbeddedId; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.ManyToOne; + +import org.junit.Test; + +import org.hibernate.LockOptions; +import org.hibernate.Session; +import org.hibernate.engine.spi.LoadQueryInfluencers; +import org.hibernate.engine.spi.QueryParameters; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.jdbc.Work; +import org.hibernate.loader.internal.EntityLoadQueryBuilderImpl; +import org.hibernate.loader.internal.LoadQueryAliasResolutionContextImpl; +import org.hibernate.loader.internal.ResultSetProcessorImpl; +import org.hibernate.loader.plan.internal.SingleRootReturnLoadPlanBuilderStrategy; +import org.hibernate.loader.plan.spi.LoadPlan; +import org.hibernate.loader.plan.spi.build.LoadPlanBuilder; +import org.hibernate.loader.spi.LoadQueryAliasResolutionContext; +import org.hibernate.loader.spi.NamedParameterContext; +import org.hibernate.loader.spi.NoOpLoadPlanAdvisor; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.hibernate.testing.junit4.ExtraAssertions; +import org.hibernate.type.Type; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +/** + * @author Gail Badner + */ +public class EncapsulatedCompositeIdResultSetProcessorTest extends BaseCoreFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Parent.class, CardField.class, Card.class }; + } + + @Test + public void testSimpleCompositeId() throws Exception { + + // create some test data + Session session = openSession(); + session.beginTransaction(); + Parent parent = new Parent(); + parent.id = new ParentPK(); + parent.id.firstName = "Joe"; + parent.id.lastName = "Blow"; + session.save( parent ); + session.getTransaction().commit(); + session.close(); + + session = openSession(); + session.beginTransaction(); + Parent parentGotten = (Parent) session.get( Parent.class, parent.id ); + assertEquals( parent, parentGotten ); + session.getTransaction().commit(); + session.close(); + + final List results = getResults( + sessionFactory().getEntityPersister( Parent.class.getName() ), + new Callback() { + @Override + public void bind(PreparedStatement ps) throws SQLException { + ps.setString( 1, "Joe" ); + ps.setString( 2, "Blow" ); + } + + @Override + public QueryParameters getQueryParameters() { + return new QueryParameters(); + } + + } + ); + assertEquals( 1, results.size() ); + Object result = results.get( 0 ); + assertNotNull( result ); + + Parent parentWork = ExtraAssertions.assertTyping( Parent.class, result ); + assertEquals( parent, parentWork ); + + // clean up test data + session = openSession(); + session.beginTransaction(); + session.createQuery( "delete Parent" ).executeUpdate(); + session.getTransaction().commit(); + session.close(); + } + + @Test + public void testCompositeIdWithKeyManyToOne() throws Exception { + final String cardId = "ace-of-spades"; + + // create some test data + Session session = openSession(); + session.beginTransaction(); + Card card = new Card( cardId ); + final CardField cardField = new CardField( card, 1 ); + session.persist( card ); + session.persist( cardField ); + session.getTransaction().commit(); + session.close(); + + session = openSession(); + session.beginTransaction(); + Card cardProxy = (Card) session.load( Card.class, cardId ); + final CardFieldPK cardFieldPK = new CardFieldPK( cardProxy, 1 ); + CardField cardFieldGotten = (CardField) session.get( CardField.class, cardFieldPK ); + + //assertEquals( card, cardGotten ); + session.getTransaction().commit(); + session.close(); + + final EntityPersister entityPersister = sessionFactory().getEntityPersister( CardField.class.getName() ); + + final List results = getResults( + entityPersister, + new Callback() { + @Override + public void bind(PreparedStatement ps) throws SQLException { + ps.setString( 1, cardField.primaryKey.card.id ); + ps.setInt( 2, cardField.primaryKey.fieldNumber ); + } + + @Override + public QueryParameters getQueryParameters() { + QueryParameters qp = new QueryParameters(); + qp.setPositionalParameterTypes( new Type[] { entityPersister.getIdentifierType() } ); + qp.setPositionalParameterValues( new Object[] { cardFieldPK } ); + qp.setOptionalObject( null ); + qp.setOptionalEntityName( entityPersister.getEntityName() ); + qp.setOptionalId( cardFieldPK ); + qp.setLockOptions( LockOptions.NONE ); + return qp; + } + + } + ); + assertEquals( 1, results.size() ); + Object result = results.get( 0 ); + assertNotNull( result ); + + CardField cardFieldWork = ExtraAssertions.assertTyping( CardField.class, result ); + assertEquals( cardFieldGotten, cardFieldWork ); + + // clean up test data + session = openSession(); + session.beginTransaction(); + session.createQuery( "delete CardField" ).executeUpdate(); + session.createQuery( "delete Card" ).executeUpdate(); + session.getTransaction().commit(); + session.close(); + } + + private List getResults(final EntityPersister entityPersister, final Callback callback) { + final SingleRootReturnLoadPlanBuilderStrategy strategy = new SingleRootReturnLoadPlanBuilderStrategy( + sessionFactory(), + LoadQueryInfluencers.NONE + ); + final LoadPlan plan = LoadPlanBuilder.buildRootEntityLoadPlan( strategy, entityPersister ); + final LoadQueryAliasResolutionContext aliasResolutionContext = + new LoadQueryAliasResolutionContextImpl( + sessionFactory(), + 0, + Collections.singletonMap( plan.getReturns().get( 0 ), new String[] { "abc" } ) + ); + final EntityLoadQueryBuilderImpl queryBuilder = new EntityLoadQueryBuilderImpl( + LoadQueryInfluencers.NONE, + plan + ); + final String sql = queryBuilder.generateSql( 1, sessionFactory(), aliasResolutionContext ); + + final ResultSetProcessorImpl resultSetProcessor = new ResultSetProcessorImpl( plan ); + final List results = new ArrayList(); + + final Session workSession = openSession(); + workSession.beginTransaction(); + workSession.doWork( + new Work() { + @Override + public void execute(Connection connection) throws SQLException { + PreparedStatement ps = connection.prepareStatement( sql ); + callback.bind( ps ); + ResultSet resultSet = ps.executeQuery(); + //callback.beforeExtractResults( workSession ); + results.addAll( + resultSetProcessor.extractResults( + NoOpLoadPlanAdvisor.INSTANCE, + resultSet, + (SessionImplementor) workSession, + callback.getQueryParameters(), + new NamedParameterContext() { + @Override + public int[] getNamedParameterLocations(String name) { + return new int[0]; + } + }, + aliasResolutionContext, + true, + false, + null, + null + ) + ); + resultSet.close(); + ps.close(); + } + } + ); + workSession.getTransaction().commit(); + workSession.close(); + + return results; + } + + + private interface Callback { + void bind(PreparedStatement ps) throws SQLException; + QueryParameters getQueryParameters (); + } + + @Entity ( name = "Parent" ) + public static class Parent { + @EmbeddedId + public ParentPK id; + + public boolean equals(Object o) { + if ( this == o ) return true; + if ( !( o instanceof Parent ) ) return false; + + final Parent parent = (Parent) o; + + if ( !id.equals( parent.id ) ) return false; + + return true; + } + + public int hashCode() { + return id.hashCode(); + } + } + + @Embeddable + public static class ParentPK implements Serializable { + private String firstName; + private String lastName; + + public boolean equals(Object o) { + if ( this == o ) return true; + if ( !( o instanceof ParentPK ) ) return false; + + final ParentPK parentPk = (ParentPK) o; + + if ( !firstName.equals( parentPk.firstName ) ) return false; + if ( !lastName.equals( parentPk.lastName ) ) return false; + + return true; + } + + public int hashCode() { + int result; + result = firstName.hashCode(); + result = 29 * result + lastName.hashCode(); + return result; + } + } + + @Entity ( name = "CardField" ) + public static class CardField implements Serializable { + + @EmbeddedId + private CardFieldPK primaryKey; + + CardField(Card card, int fieldNumber) { + this.primaryKey = new CardFieldPK(card, fieldNumber); + } + + CardField() { + } + + public CardFieldPK getPrimaryKey() { + return primaryKey; + } + + public void setPrimaryKey(CardFieldPK primaryKey) { + this.primaryKey = primaryKey; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + + CardField cardField = (CardField) o; + + if ( primaryKey != null ? !primaryKey.equals( cardField.primaryKey ) : cardField.primaryKey != null ) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + return primaryKey != null ? primaryKey.hashCode() : 0; + } + } + + @Embeddable + public static class CardFieldPK implements Serializable { + @ManyToOne(optional = false) + private Card card; + + private int fieldNumber; + + public CardFieldPK(Card card, int fieldNumber) { + this.card = card; + this.fieldNumber = fieldNumber; + } + + CardFieldPK() { + } + + public Card getCard() { + return card; + } + + public void setCard(Card card) { + this.card = card; + } + + public int getFieldNumber() { + return fieldNumber; + } + + public void setFieldNumber(int fieldNumber) { + this.fieldNumber = fieldNumber; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + + CardFieldPK that = (CardFieldPK) o; + + if ( fieldNumber != that.fieldNumber ) { + return false; + } + if ( card != null ? !card.equals( that.card ) : that.card != null ) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + int result = card != null ? card.hashCode() : 0; + result = 31 * result + fieldNumber; + return result; + } + } + + @Entity ( name = "Card" ) + public static class Card implements Serializable { + @Id + private String id; + + public Card(String id) { + this(); + this.id = id; + } + + Card() { + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/loader/EntityWithCollectionResultSetProcessorTest.java b/hibernate-core/src/test/java/org/hibernate/loader/EntityWithNonLazyCollectionResultSetProcessorTest.java similarity index 98% rename from hibernate-core/src/test/java/org/hibernate/loader/EntityWithCollectionResultSetProcessorTest.java rename to hibernate-core/src/test/java/org/hibernate/loader/EntityWithNonLazyCollectionResultSetProcessorTest.java index ff9d0680f8..ce85f482fd 100644 --- a/hibernate-core/src/test/java/org/hibernate/loader/EntityWithCollectionResultSetProcessorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/loader/EntityWithNonLazyCollectionResultSetProcessorTest.java @@ -69,7 +69,7 @@ import static org.junit.Assert.assertTrue; * @author Gail Badner */ @FailureExpectedWithNewMetamodel -public class EntityWithCollectionResultSetProcessorTest extends BaseCoreFunctionalTestCase { +public class EntityWithNonLazyCollectionResultSetProcessorTest extends BaseCoreFunctionalTestCase { @Override protected Class[] getAnnotatedClasses() { diff --git a/hibernate-core/src/test/java/org/hibernate/loader/EntityWithNonLazyOneToManyListResultSetProcessorTest.java b/hibernate-core/src/test/java/org/hibernate/loader/EntityWithNonLazyOneToManyListResultSetProcessorTest.java new file mode 100644 index 0000000000..abd4dadf7f --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/loader/EntityWithNonLazyOneToManyListResultSetProcessorTest.java @@ -0,0 +1,215 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.loader; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; + +import org.junit.Test; + +import org.hibernate.Hibernate; +import org.hibernate.Session; +import org.hibernate.engine.spi.LoadQueryInfluencers; +import org.hibernate.engine.spi.QueryParameters; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.jdbc.Work; +import org.hibernate.loader.internal.EntityLoadQueryBuilderImpl; +import org.hibernate.loader.internal.LoadQueryAliasResolutionContextImpl; +import org.hibernate.loader.internal.ResultSetProcessorImpl; +import org.hibernate.loader.plan.internal.SingleRootReturnLoadPlanBuilderStrategy; +import org.hibernate.loader.plan.spi.LoadPlan; +import org.hibernate.loader.plan.spi.build.LoadPlanBuilder; +import org.hibernate.loader.spi.LoadQueryAliasResolutionContext; +import org.hibernate.loader.spi.NamedParameterContext; +import org.hibernate.loader.spi.NoOpLoadPlanAdvisor; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.hibernate.testing.junit4.ExtraAssertions; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +/** + * @author Gail Badner + */ +public class EntityWithNonLazyOneToManyListResultSetProcessorTest extends BaseCoreFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Poster.class, Message.class }; + } + + @Test + public void testEntityWithList() throws Exception { + final EntityPersister entityPersister = sessionFactory().getEntityPersister( Poster.class.getName() ); + + // create some test data + Session session = openSession(); + session.beginTransaction(); + Poster poster = new Poster(); + poster.pid = 0; + poster.name = "John Doe"; + Message message1 = new Message(); + message1.mid = 1; + message1.msgTxt = "Howdy!"; + message1.poster = poster; + poster.messages.add( message1 ); + Message message2 = new Message(); + message2.mid = 2; + message2.msgTxt = "Bye!"; + message2.poster = poster; + poster.messages.add( message2 ); + session.save( poster ); + session.getTransaction().commit(); + session.close(); + + session = openSession(); + session.beginTransaction(); + Poster posterGotten = (Poster) session.get( Poster.class, poster.pid ); + assertEquals( 0, posterGotten.pid.intValue() ); + assertEquals( poster.name, posterGotten.name ); + assertTrue( Hibernate.isInitialized( posterGotten.messages ) ); + assertEquals( 2, posterGotten.messages.size() ); + assertEquals( message1.msgTxt, posterGotten.messages.get( 0 ).msgTxt ); + assertEquals( message2.msgTxt, posterGotten.messages.get( 1 ).msgTxt ); + assertSame( posterGotten, posterGotten.messages.get( 0 ).poster ); + assertSame( posterGotten, posterGotten.messages.get( 1 ).poster ); + session.getTransaction().commit(); + session.close(); + + { + final SingleRootReturnLoadPlanBuilderStrategy strategy = new SingleRootReturnLoadPlanBuilderStrategy( + sessionFactory(), + LoadQueryInfluencers.NONE + ); + final LoadPlan plan = LoadPlanBuilder.buildRootEntityLoadPlan( strategy, entityPersister ); + final LoadQueryAliasResolutionContext aliasResolutionContext = + new LoadQueryAliasResolutionContextImpl( + sessionFactory(), + 0, + Collections.singletonMap( plan.getReturns().get( 0 ), new String[] { "abc" } ) + ); + final EntityLoadQueryBuilderImpl queryBuilder = new EntityLoadQueryBuilderImpl( + LoadQueryInfluencers.NONE, + plan + ); + final String sql = queryBuilder.generateSql( 1, sessionFactory(), aliasResolutionContext ); + + final ResultSetProcessorImpl resultSetProcessor = new ResultSetProcessorImpl( plan ); + final List results = new ArrayList(); + + final Session workSession = openSession(); + workSession.beginTransaction(); + workSession.doWork( + new Work() { + @Override + public void execute(Connection connection) throws SQLException { + PreparedStatement ps = connection.prepareStatement( sql ); + ps.setInt( 1, 0 ); + ResultSet resultSet = ps.executeQuery(); + results.addAll( + resultSetProcessor.extractResults( + NoOpLoadPlanAdvisor.INSTANCE, + resultSet, + (SessionImplementor) workSession, + new QueryParameters(), + new NamedParameterContext() { + @Override + public int[] getNamedParameterLocations(String name) { + return new int[0]; + } + }, + aliasResolutionContext, + true, + false, + null, + null + ) + ); + resultSet.close(); + ps.close(); + } + } + ); + assertEquals( 2, results.size() ); + Object result1 = results.get( 0 ); + assertNotNull( result1 ); + assertSame( result1, results.get( 1 ) ); + + Poster workPoster = ExtraAssertions.assertTyping( Poster.class, result1 ); + assertEquals( 0, workPoster.pid.intValue() ); + assertEquals( poster.name, workPoster.name ); + assertTrue( Hibernate.isInitialized( workPoster.messages ) ); + assertEquals( 2, workPoster.messages.size() ); + assertTrue( Hibernate.isInitialized( posterGotten.messages ) ); + assertEquals( 2, workPoster.messages.size() ); + assertEquals( message1.msgTxt, workPoster.messages.get( 0 ).msgTxt ); + assertEquals( message2.msgTxt, workPoster.messages.get( 1 ).msgTxt ); + assertSame( workPoster, workPoster.messages.get( 0 ).poster ); + assertSame( workPoster, workPoster.messages.get( 1 ).poster ); + workSession.getTransaction().commit(); + workSession.close(); + } + + // clean up test data + session = openSession(); + session.beginTransaction(); + session.delete( poster ); + session.getTransaction().commit(); + session.close(); + } + + @Entity( name = "Message" ) + public static class Message { + @Id + private Integer mid; + private String msgTxt; + @ManyToOne + @JoinColumn + private Poster poster; + } + + @Entity( name = "Poster" ) + public static class Poster { + @Id + private Integer pid; + private String name; + @OneToMany(mappedBy = "poster", fetch = FetchType.EAGER, cascade = CascadeType.ALL ) + private List messages = new ArrayList(); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/loader/EntityWithOneToManyResultSetProcessorTest.java b/hibernate-core/src/test/java/org/hibernate/loader/EntityWithNonLazyOneToManySetResultSetProcessorTest.java similarity index 98% rename from hibernate-core/src/test/java/org/hibernate/loader/EntityWithOneToManyResultSetProcessorTest.java rename to hibernate-core/src/test/java/org/hibernate/loader/EntityWithNonLazyOneToManySetResultSetProcessorTest.java index a6351cfad0..cdaf9d76f4 100644 --- a/hibernate-core/src/test/java/org/hibernate/loader/EntityWithOneToManyResultSetProcessorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/loader/EntityWithNonLazyOneToManySetResultSetProcessorTest.java @@ -72,7 +72,7 @@ import static org.junit.Assert.fail; * @author Gail Badner */ @FailureExpectedWithNewMetamodel -public class EntityWithOneToManyResultSetProcessorTest extends BaseCoreFunctionalTestCase { +public class EntityWithNonLazyOneToManySetResultSetProcessorTest extends BaseCoreFunctionalTestCase { @Override protected Class[] getAnnotatedClasses() { diff --git a/hibernate-core/src/test/java/org/hibernate/persister/walking/BasicWalkingTest.java b/hibernate-core/src/test/java/org/hibernate/persister/walking/BasicWalkingTest.java index da8bc7f961..411a42f714 100644 --- a/hibernate-core/src/test/java/org/hibernate/persister/walking/BasicWalkingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/persister/walking/BasicWalkingTest.java @@ -38,6 +38,7 @@ import org.hibernate.persister.walking.spi.CollectionDefinition; import org.hibernate.persister.walking.spi.CollectionElementDefinition; import org.hibernate.persister.walking.spi.CollectionIndexDefinition; import org.hibernate.persister.walking.spi.CompositionDefinition; +import org.hibernate.persister.walking.spi.CompositionElementDefinition; import org.hibernate.persister.walking.spi.EntityDefinition; import org.hibernate.persister.walking.spi.EntityIdentifierDefinition; import org.hibernate.persister.walking.spi.MetadataDrivenModelGraphVisitor; @@ -168,6 +169,28 @@ public class BasicWalkingTest extends BaseCoreFunctionalTestCase { ); } + @Override + public void startingCompositeElement(CompositionElementDefinition compositionElementDefinition) { + System.out.println( + String.format( + "%s Starting composite (%s)", + StringHelper.repeat( ">>", ++depth ), + compositionElementDefinition.toString() + ) + ); + } + + @Override + public void finishingCompositeElement(CompositionElementDefinition compositionElementDefinition) { + System.out.println( + String.format( + "%s Finishing composite (%s)", + StringHelper.repeat( ">>", depth-- ), + compositionElementDefinition.toString() + ) + ); + } + @Override public boolean startingAttribute(AttributeDefinition attributeDefinition) { System.out.println( diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/OneToOneWithDerivedIdentityTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/OneToOneWithDerivedIdentityTest.java index baf99d62ee..f1a33cc8da 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/OneToOneWithDerivedIdentityTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/OneToOneWithDerivedIdentityTest.java @@ -26,6 +26,9 @@ package org.hibernate.test.annotations.derivedidentities.bidirectional; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import java.util.List; + +import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.testing.FailureExpected; import org.hibernate.testing.TestForIssue; @@ -35,6 +38,7 @@ import org.junit.Test; @FailureExpected(jiraKey = "HHH-5695") public class OneToOneWithDerivedIdentityTest extends BaseCoreFunctionalTestCase { @Test + @TestForIssue(jiraKey = "HHH-5695") public void testInsertFooAndBarWithDerivedId() { Session s = openSession(); s.beginTransaction(); @@ -81,11 +85,50 @@ public class OneToOneWithDerivedIdentityTest extends BaseCoreFunctionalTestCase s.close(); } + @Test + @TestForIssue(jiraKey = "HHH-6813") + // Regression test utilizing multiple types of queries. + public void testCase() { + Session s = openSession(); + s.getTransaction().begin(); + + Person p = new Person(); + p.setName( "Alfio" ); + PersonInfo pi = new PersonInfo(); + pi.setId( p ); + pi.setInfo( "Some information" ); + s.persist( p ); + s.persist( pi ); + + s.getTransaction().commit(); + s.clear(); + + s.getTransaction().begin(); + + Query q = s.getNamedQuery( "PersonQuery" ); + List persons = q.list(); + assertEquals( persons.size(), 1 ); + assertEquals( persons.get( 0 ).getName(), "Alfio" ); + + s.getTransaction().commit(); + s.clear(); + + s.getTransaction().begin(); + + p = (Person) s.get( Person.class, persons.get( 0 ).getId() ); + assertEquals( p.getName(), "Alfio" ); + + s.getTransaction().commit(); + s.close(); + } + @Override protected Class[] getAnnotatedClasses() { return new Class[] { Foo.class, - Bar.class + Bar.class, + Person.class, + PersonInfo.class }; } diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/Person.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/Person.java new file mode 100644 index 0000000000..77a0727489 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/Person.java @@ -0,0 +1,77 @@ +package org.hibernate.test.annotations.derivedidentities.bidirectional; + +import java.io.Serializable; +import javax.persistence.Basic; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.NamedQuery; +import javax.persistence.OneToOne; + +@Entity +@NamedQuery(name="PersonQuery", query="SELECT p FROM Person p") +public class Person + implements Serializable +{ + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue(strategy=GenerationType.AUTO) + private Integer id; + + @Basic + private String name; + + @OneToOne(mappedBy="id") + private PersonInfo personInfo; + + public Integer getId() + { + return this.id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public int hashCode() + { + int hash = 0; + hash += (this.id != null ? this.id.hashCode() : 0); + return hash; + } + + public boolean equals(Object object) + { + if (!(object instanceof Person)) { + return false; + } + Person other = (Person)object; + + return ((this.id != null) || (other.id == null)) && ((this.id == null) || (this.id.equals(other.id))); + } + + public String toString() + { + return "nogroup.hibertest.Person[ id=" + this.id + " ]"; + } + + public PersonInfo getPersonInfo() + { + return this.personInfo; + } + + public void setPersonInfo(PersonInfo personInfo) + { + this.personInfo = personInfo; + } +} \ No newline at end of file diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/PersonInfo.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/PersonInfo.java new file mode 100644 index 0000000000..08a1ff9714 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/PersonInfo.java @@ -0,0 +1,60 @@ +package org.hibernate.test.annotations.derivedidentities.bidirectional; + +import java.io.Serializable; +import javax.persistence.Basic; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.OneToOne; + +@Entity +public class PersonInfo + implements Serializable +{ + private static final long serialVersionUID = 1L; + + @Id + @OneToOne + private Person id; + + @Basic + private String info; + + public Person getId() + { + return this.id; + } + + public void setId(Person id) { + this.id = id; + } + + public String getInfo() { + return this.info; + } + + public void setInfo(String info) { + this.info = info; + } + + public int hashCode() + { + int hash = 0; + hash += (this.id != null ? this.id.hashCode() : 0); + return hash; + } + + public boolean equals(Object object) + { + if (!(object instanceof PersonInfo)) { + return false; + } + PersonInfo other = (PersonInfo)object; + + return ((this.id != null) || (other.id == null)) && ((this.id == null) || (this.id.equals(other.id))); + } + + public String toString() + { + return "nogroup.hibertest.PersonInfo[ id=" + this.id + " ]"; + } +} \ No newline at end of file diff --git a/hibernate-core/src/test/java/org/hibernate/test/common/BasicTestingJdbcServiceImpl.java b/hibernate-core/src/test/java/org/hibernate/test/common/BasicTestingJdbcServiceImpl.java index 1a25bcf661..3047a053e5 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/common/BasicTestingJdbcServiceImpl.java +++ b/hibernate-core/src/test/java/org/hibernate/test/common/BasicTestingJdbcServiceImpl.java @@ -38,7 +38,7 @@ import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.jdbc.env.spi.LobCreatorBuilder; import org.hibernate.engine.jdbc.env.spi.QualifiedObjectNameSupport; import org.hibernate.engine.jdbc.internal.ResultSetWrapperImpl; -import org.hibernate.engine.jdbc.internal.TypeInfo; +import org.hibernate.engine.jdbc.spi.TypeInfo; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.jdbc.spi.ResultSetWrapper; import org.hibernate.engine.jdbc.spi.SqlExceptionHelper; diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java index a172a9afb1..5ff73da5fb 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java @@ -1007,7 +1007,8 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil sessionFactory, settings, configurationValues, - serviceRegistry.getService( ConfigurationService.class ).getSettings() + serviceRegistry.getService( ConfigurationService.class ).getSettings(), + metadata ); } else { diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/scan/spi/AbstractScannerImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/scan/spi/AbstractScannerImpl.java index da5af94f25..3d2c441370 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/scan/spi/AbstractScannerImpl.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/scan/spi/AbstractScannerImpl.java @@ -68,7 +68,7 @@ public abstract class AbstractScannerImpl implements Scanner { if ( persistenceUnit.getPersistenceUnitRootUrl() != null ) { final ArchiveDescriptor descriptor = buildArchiveDescriptor( persistenceUnit.getPersistenceUnitRootUrl(), true, scanOptions ); - final ArchiveContext context = buildArchiveContext( persistenceUnit, false, resultCollector ); + final ArchiveContext context = buildArchiveContext( persistenceUnit, true, resultCollector ); descriptor.visitArchive( context ); } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/AbstractGraphNode.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/AbstractGraphNode.java index ff760bd2f1..e30c4f88fb 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/AbstractGraphNode.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/AbstractGraphNode.java @@ -162,42 +162,42 @@ public abstract class AbstractGraphNode implements GraphNodeImplementor { } @SuppressWarnings("unchecked") - public Subgraph addSubgraph(Attribute attribute) { + public SubgraphImpl addSubgraph(Attribute attribute) { return addAttribute( attribute ).makeSubgraph(); } @SuppressWarnings("unchecked") - public Subgraph addSubgraph(Attribute attribute, Class type) { + public SubgraphImpl addSubgraph(Attribute attribute, Class type) { return addAttribute( attribute ).makeSubgraph( type ); } @SuppressWarnings("unchecked") - public Subgraph addSubgraph(String attributeName) { + public SubgraphImpl addSubgraph(String attributeName) { return addAttribute( attributeName ).makeSubgraph(); } @SuppressWarnings("unchecked") - public Subgraph addSubgraph(String attributeName, Class type) { + public SubgraphImpl addSubgraph(String attributeName, Class type) { return addAttribute( attributeName ).makeSubgraph( type ); } @SuppressWarnings("unchecked") - public Subgraph addKeySubgraph(Attribute attribute) { + public SubgraphImpl addKeySubgraph(Attribute attribute) { return addAttribute( attribute ).makeKeySubgraph(); } @SuppressWarnings("unchecked") - public Subgraph addKeySubgraph(Attribute attribute, Class type) { + public SubgraphImpl addKeySubgraph(Attribute attribute, Class type) { return addAttribute( attribute ).makeKeySubgraph( type ); } @SuppressWarnings("unchecked") - public Subgraph addKeySubgraph(String attributeName) { + public SubgraphImpl addKeySubgraph(String attributeName) { return addAttribute( attributeName ).makeKeySubgraph(); } @SuppressWarnings("unchecked") - public Subgraph addKeySubgraph(String attributeName, Class type) { + public SubgraphImpl addKeySubgraph(String attributeName, Class type) { return addAttribute( attributeName ).makeKeySubgraph( type ); } } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/AttributeNodeImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/AttributeNodeImpl.java index b399468217..d25a2526c2 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/AttributeNodeImpl.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/AttributeNodeImpl.java @@ -108,16 +108,16 @@ public class AttributeNodeImpl implements AttributeNode, AttributeNodeImpl } @SuppressWarnings("unchecked") - public Subgraph makeSubgraph() { - return (Subgraph) internalMakeSubgraph( null ); + public SubgraphImpl makeSubgraph() { + return (SubgraphImpl) internalMakeSubgraph( null ); } @SuppressWarnings("unchecked") - public Subgraph makeSubgraph(Class type) { - return (Subgraph) internalMakeSubgraph( type ); + public SubgraphImpl makeSubgraph(Class type) { + return (SubgraphImpl) internalMakeSubgraph( type ); } - private Subgraph internalMakeSubgraph(Class type) { + private SubgraphImpl internalMakeSubgraph(Class type) { if ( attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.BASIC || attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.EMBEDDED ) { throw new IllegalArgumentException( @@ -193,12 +193,17 @@ public class AttributeNodeImpl implements AttributeNode, AttributeNodeImpl return type.isAssignableFrom( entityPersister.getMappedClass() ); } - public Subgraph makeKeySubgraph() { - return (SubgraphImpl) makeKeySubgraph( null ); + @SuppressWarnings("unchecked") + public SubgraphImpl makeKeySubgraph() { + return (SubgraphImpl) internalMakeKeySubgraph( null ); } @SuppressWarnings("unchecked") - public Subgraph makeKeySubgraph(Class type) { + public SubgraphImpl makeKeySubgraph(Class type) { + return (SubgraphImpl) internalMakeKeySubgraph( type ); + } + + public SubgraphImpl internalMakeKeySubgraph(Class type) { if ( ! attribute.isCollection() ) { throw new IllegalArgumentException( String.format( "Non-collection attribute [%s] cannot be target of key subgraph", getAttributeName() ) diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/EntityGraphImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/EntityGraphImpl.java index 4189b9144d..c41ed06b65 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/EntityGraphImpl.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/EntityGraphImpl.java @@ -83,42 +83,42 @@ public class EntityGraphImpl extends AbstractGraphNode implements EntityGr } @Override - public Subgraph addSubgraph(Attribute attribute) { + public SubgraphImpl addSubgraph(Attribute attribute) { return super.addSubgraph( attribute ); } @Override - public Subgraph addSubgraph(Attribute attribute, Class type) { + public SubgraphImpl addSubgraph(Attribute attribute, Class type) { return super.addSubgraph( attribute, type ); } @Override - public Subgraph addSubgraph(String attributeName) { + public SubgraphImpl addSubgraph(String attributeName) { return super.addSubgraph( attributeName ); } @Override - public Subgraph addSubgraph(String attributeName, Class type) { + public SubgraphImpl addSubgraph(String attributeName, Class type) { return super.addSubgraph( attributeName, type ); } @Override - public Subgraph addKeySubgraph(Attribute attribute) { + public SubgraphImpl addKeySubgraph(Attribute attribute) { return super.addKeySubgraph( attribute ); } @Override - public Subgraph addKeySubgraph(Attribute attribute, Class type) { + public SubgraphImpl addKeySubgraph(Attribute attribute, Class type) { return super.addKeySubgraph( attribute, type ); } @Override - public Subgraph addKeySubgraph(String attributeName) { + public SubgraphImpl addKeySubgraph(String attributeName) { return super.addKeySubgraph( attributeName ); } @Override - public Subgraph addKeySubgraph(String attributeName, Class type) { + public SubgraphImpl addKeySubgraph(String attributeName, Class type) { return super.addKeySubgraph( attributeName, type ); } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/SubgraphImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/SubgraphImpl.java index 1f206ff32f..f52c01dba1 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/SubgraphImpl.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/SubgraphImpl.java @@ -68,42 +68,42 @@ public class SubgraphImpl extends AbstractGraphNode implements Subgraph } @Override - public Subgraph addSubgraph(Attribute attribute) { + public SubgraphImpl addSubgraph(Attribute attribute) { return super.addSubgraph( attribute ); } @Override - public Subgraph addSubgraph(Attribute attribute, Class type) { + public SubgraphImpl addSubgraph(Attribute attribute, Class type) { return super.addSubgraph( attribute, type ); } @Override - public Subgraph addSubgraph(String attributeName) { + public SubgraphImpl addSubgraph(String attributeName) { return super.addSubgraph( attributeName ); } @Override - public Subgraph addSubgraph(String attributeName, Class type) { + public SubgraphImpl addSubgraph(String attributeName, Class type) { return super.addSubgraph( attributeName, type ); } @Override - public Subgraph addKeySubgraph(Attribute attribute) { + public SubgraphImpl addKeySubgraph(Attribute attribute) { return super.addKeySubgraph( attribute ); } @Override - public Subgraph addKeySubgraph(Attribute attribute, Class type) { + public SubgraphImpl addKeySubgraph(Attribute attribute, Class type) { return super.addKeySubgraph( attribute, type ); } @Override - public Subgraph addKeySubgraph(String attributeName) { + public SubgraphImpl addKeySubgraph(String attributeName) { return super.addKeySubgraph( attributeName ); } @Override - public Subgraph addKeySubgraph(String attributeName, Class type) { + public SubgraphImpl addKeySubgraph(String attributeName, Class type) { return super.addKeySubgraph( attributeName, type ); } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/advisor/AdviceHelper.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/advisor/AdviceHelper.java index 297912b513..3f7bcab15b 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/advisor/AdviceHelper.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/advisor/AdviceHelper.java @@ -29,11 +29,13 @@ import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.jpa.graph.spi.AttributeNodeImplementor; +import org.hibernate.jpa.internal.metamodel.Helper; import org.hibernate.loader.plan.spi.CollectionFetch; import org.hibernate.loader.plan.spi.CompositeFetch; import org.hibernate.loader.plan.spi.EntityFetch; import org.hibernate.loader.plan.spi.Fetch; import org.hibernate.loader.plan.spi.FetchOwner; +import org.hibernate.type.EntityType; /** * @author Steve Ebersole @@ -54,11 +56,16 @@ public class AdviceHelper { ); } else { + EntityType entityType = (EntityType) Helper.resolveType( + (SessionFactoryImplementor) attributeNode.entityManagerFactory().getSessionFactory(), + attributeNode.getAttribute() + ); return new EntityFetch( (SessionFactoryImplementor) attributeNode.entityManagerFactory().getSessionFactory(), LockMode.NONE, fetchOwner, attributeNode.getAttributeName(), + entityType, new FetchStrategy( FetchTiming.IMMEDIATE, FetchStyle.SELECT ) ); } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/EntityManagerFactoryImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/EntityManagerFactoryImpl.java index 336b874881..a64f4b4569 100755 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/EntityManagerFactoryImpl.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/EntityManagerFactoryImpl.java @@ -27,12 +27,16 @@ import javax.persistence.Cache; import javax.persistence.EntityGraph; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; +import javax.persistence.NamedAttributeNode; +import javax.persistence.NamedEntityGraph; +import javax.persistence.NamedSubgraph; import javax.persistence.PersistenceContextType; import javax.persistence.PersistenceException; import javax.persistence.PersistenceUnitUtil; import javax.persistence.Query; import javax.persistence.SynchronizationType; import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.metamodel.Attribute; import javax.persistence.metamodel.EntityType; import javax.persistence.metamodel.Metamodel; import javax.persistence.spi.LoadState; @@ -42,6 +46,7 @@ import java.io.InvalidObjectException; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -54,6 +59,7 @@ import org.hibernate.Hibernate; import org.hibernate.SessionFactory; import org.hibernate.cache.spi.RegionFactory; import org.hibernate.cfg.Configuration; +import org.hibernate.cfg.annotations.NamedEntityGraphDefinition; import org.hibernate.ejb.HibernateEntityManagerFactory; import org.hibernate.engine.spi.NamedQueryDefinition; import org.hibernate.engine.spi.NamedQueryDefinitionBuilder; @@ -63,15 +69,19 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.id.IdentifierGenerator; import org.hibernate.id.UUIDGenerator; import org.hibernate.internal.SessionFactoryImpl; +import org.hibernate.internal.util.StringHelper; import org.hibernate.jpa.AvailableSettings; import org.hibernate.jpa.HibernateQuery; import org.hibernate.jpa.boot.internal.SettingsImpl; import org.hibernate.jpa.criteria.CriteriaBuilderImpl; +import org.hibernate.jpa.graph.internal.AbstractGraphNode; import org.hibernate.jpa.graph.internal.EntityGraphImpl; +import org.hibernate.jpa.graph.internal.SubgraphImpl; import org.hibernate.jpa.internal.util.PersistenceUtilHelper; import org.hibernate.jpa.metamodel.internal.EntityTypeImpl; import org.hibernate.jpa.metamodel.internal.MetamodelImpl; import org.hibernate.metadata.ClassMetadata; +import org.hibernate.metamodel.spi.MetadataImplementor; import org.hibernate.procedure.ProcedureCall; import org.hibernate.service.ServiceRegistry; @@ -124,7 +134,7 @@ public class EntityManagerFactoryImpl implements HibernateEntityManagerFactory { SettingsImpl settings, Map configurationValues, Configuration cfg) { - this(persistenceUnitName, sessionFactory, settings, configurationValues, cfg.getProperties() ); + this(persistenceUnitName, sessionFactory, settings, configurationValues, cfg.getProperties(), null ); } public EntityManagerFactoryImpl( @@ -132,7 +142,8 @@ public class EntityManagerFactoryImpl implements HibernateEntityManagerFactory { SessionFactoryImplementor sessionFactory, SettingsImpl settings, Map configurationValues, - Map cfg) { + Map cfg, + MetadataImplementor metadataImplementor) { this.sessionFactory = (SessionFactoryImpl) sessionFactory; this.transactionType = settings.getTransactionType(); this.discardOnClose = settings.isReleaseResourcesOnCloseEnabled(); @@ -155,7 +166,11 @@ public class EntityManagerFactoryImpl implements HibernateEntityManagerFactory { entityManagerFactoryName = (String) UUID_GENERATOR.generate(null, null); } this.entityManagerFactoryName = entityManagerFactoryName; - EntityManagerFactoryRegistry.INSTANCE.addEntityManagerFactory(entityManagerFactoryName, this); + if ( metadataImplementor != null ) { + applyNamedEntityGraphs( metadataImplementor.getNamedEntityGraphMap().values() ); + } + + EntityManagerFactoryRegistry.INSTANCE.addEntityManagerFactory( entityManagerFactoryName, this ); } private static void addAll(HashMap destination, Map source) { @@ -177,6 +192,68 @@ public class EntityManagerFactoryImpl implements HibernateEntityManagerFactory { } } + @SuppressWarnings("unchecked") + private void applyNamedEntityGraphs(Collection namedEntityGraphs) { + for ( NamedEntityGraphDefinition definition : namedEntityGraphs ) { + final EntityType entityType = metamodel.getEntityTypeByName( definition.getJpaEntityName() ); + final EntityGraphImpl entityGraph = new EntityGraphImpl( + definition.getRegisteredName(), + entityType, + this + ); + + final NamedEntityGraph namedEntityGraph = definition.getAnnotation(); + + if ( namedEntityGraph.includeAllAttributes() ) { + for ( Object attributeObject : entityType.getAttributes() ) { + entityGraph.addAttributeNodes( (Attribute) attributeObject ); + } + } + + if ( namedEntityGraph.attributeNodes() != null ) { + applyNamedAttributeNodes( namedEntityGraph.attributeNodes(), namedEntityGraph, entityGraph ); + } + + entityGraphs.put( definition.getRegisteredName(), entityGraph ); + } + } + + private void applyNamedAttributeNodes( + NamedAttributeNode[] namedAttributeNodes, + NamedEntityGraph namedEntityGraph, + AbstractGraphNode graphNode) { + for ( NamedAttributeNode namedAttributeNode : namedAttributeNodes ) { + if ( StringHelper.isNotEmpty( namedAttributeNode.subgraph() ) ) { + final SubgraphImpl subgraph = graphNode.addSubgraph( namedAttributeNode.value() ); + applyNamedSubgraphs( + namedEntityGraph, + namedAttributeNode.subgraph(), + subgraph + ); + } + if ( StringHelper.isNotEmpty( namedAttributeNode.keySubgraph() ) ) { + final SubgraphImpl subgraph = graphNode.addKeySubgraph( namedAttributeNode.value() ); + applyNamedSubgraphs( + namedEntityGraph, + namedAttributeNode.keySubgraph(), + subgraph + ); + } + } + } + + private void applyNamedSubgraphs(NamedEntityGraph namedEntityGraph, String subgraphName, SubgraphImpl subgraph) { + for ( NamedSubgraph namedSubgraph : namedEntityGraph.subgraphs() ) { + if ( subgraphName.equals( namedSubgraph.name() ) ) { + applyNamedAttributeNodes( + namedSubgraph.attributeNodes(), + namedEntityGraph, + subgraph + ); + } + } + } + public EntityManager createEntityManager() { return createEntityManager( Collections.EMPTY_MAP ); } diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/graphs/named/basic/BasicNamedEntityGraphTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/graphs/named/basic/BasicNamedEntityGraphTest.java new file mode 100644 index 0000000000..b6109b4a3a --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/graphs/named/basic/BasicNamedEntityGraphTest.java @@ -0,0 +1,48 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.jpa.test.graphs.named.basic; + +import javax.persistence.EntityGraph; + +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; + +import org.junit.Test; + +import static junit.framework.Assert.assertNotNull; + +/** + * @author Steve Ebersole + */ +public class BasicNamedEntityGraphTest extends BaseEntityManagerFunctionalTestCase { + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Person.class }; + } + + @Test + public void testIt() { + EntityGraph graph = getOrCreateEntityManager().getEntityGraph( "Person" ); + assertNotNull( graph ); + } +} diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/graphs/named/basic/Person.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/graphs/named/basic/Person.java new file mode 100644 index 0000000000..dc9a55bf35 --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/graphs/named/basic/Person.java @@ -0,0 +1,38 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.jpa.test.graphs.named.basic; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.NamedEntityGraph; + +/** + * @author Steve Ebersole + */ +@Entity(name = "Person") +@NamedEntityGraph() +public class Person { + @Id + private Long id; +} diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/transaction/TransactionJoiningTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/transaction/TransactionJoiningTest.java index eca6de48cb..896add1131 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/transaction/TransactionJoiningTest.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/transaction/TransactionJoiningTest.java @@ -23,7 +23,6 @@ */ package org.hibernate.jpa.test.transaction; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -31,14 +30,17 @@ import java.util.Map; import java.util.concurrent.CountDownLatch; import javax.persistence.EntityManager; +import javax.persistence.PersistenceException; import javax.transaction.Status; import javax.transaction.Synchronization; +import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.transaction.internal.jta.CMTTransaction; import org.hibernate.engine.transaction.internal.jta.JtaStatusHelper; +import org.hibernate.exception.GenericJDBCException; import org.hibernate.internal.SessionImpl; import org.hibernate.jpa.AvailableSettings; import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; @@ -152,32 +154,46 @@ public class TransactionJoiningTest extends BaseEntityManagerFunctionalTestCase * See HHH-7910 */ @Test - @TestForIssue(jiraKey="HHH-7910") + @TestForIssue(jiraKey = "HHH-7910") public void testMultiThreadTransactionTimeout() throws Exception { TestingJtaPlatformImpl.INSTANCE.getTransactionManager().begin(); - + EntityManager em = entityManagerFactory().createEntityManager(); final SessionImpl sImpl = em.unwrap( SessionImpl.class ); - - final CountDownLatch latch = new CountDownLatch(1); - + + final CountDownLatch latch = new CountDownLatch( 1 ); + Thread thread = new Thread() { public void run() { - sImpl.getTransactionCoordinator().getSynchronizationCallbackCoordinator().afterCompletion( Status.STATUS_ROLLEDBACK ); + sImpl.getTransactionCoordinator().getSynchronizationCallbackCoordinator() + .afterCompletion( Status.STATUS_ROLLEDBACK ); latch.countDown(); } }; thread.start(); - - latch.await(); - - em.persist( new Book( "The Book of Foo", 1 ) ); - - // Ensure that the session was cleared by the background thread. - assertEquals( "The background thread did not clear the session as expected!", - 0, em.createQuery( "from Book" ).getResultList().size() ); - TestingJtaPlatformImpl.INSTANCE.getTransactionManager().commit(); + latch.await(); + + boolean caught = false; + try { + em.persist( new Book( "The Book of Foo", 1 ) ); + } + catch ( PersistenceException e ) { + caught = e.getCause().getClass().equals( HibernateException.class ); + } + assertTrue( caught ); + + // Ensure that the connection was closed by the background thread. + caught = false; + try { + em.createQuery( "from Book" ).getResultList(); + } + catch ( PersistenceException e ) { + caught = e.getCause().getClass().equals( GenericJDBCException.class ); + } + assertTrue( caught ); + + TestingJtaPlatformImpl.INSTANCE.getTransactionManager().rollback(); em.close(); } diff --git a/hibernate-osgi/src/main/java/org/hibernate/osgi/OsgiJtaPlatform.java b/hibernate-osgi/src/main/java/org/hibernate/osgi/OsgiJtaPlatform.java index 5dd4888037..c79318c3a5 100644 --- a/hibernate-osgi/src/main/java/org/hibernate/osgi/OsgiJtaPlatform.java +++ b/hibernate-osgi/src/main/java/org/hibernate/osgi/OsgiJtaPlatform.java @@ -54,13 +54,13 @@ public class OsgiJtaPlatform implements JtaPlatform { @Override public TransactionManager retrieveTransactionManager() { final ServiceReference sr = bundleContext.getServiceReference( TransactionManager.class.getName() ); - return (TransactionManager) bundleContext.getService( sr ); + return sr == null ? null : (TransactionManager) bundleContext.getService( sr ); } @Override public UserTransaction retrieveUserTransaction() { final ServiceReference sr = bundleContext.getServiceReference( UserTransaction.class.getName() ); - return (UserTransaction) bundleContext.getService( sr ); + return sr == null ? null : (UserTransaction) bundleContext.getService( sr ); } @Override diff --git a/libraries.gradle b/libraries.gradle index f489115679..794597b3e9 100644 --- a/libraries.gradle +++ b/libraries.gradle @@ -43,7 +43,7 @@ ext { // Annotations commons_annotations: - 'org.hibernate.common:hibernate-commons-annotations:4.0.1.Final@jar', + 'org.hibernate.common:hibernate-commons-annotations:4.0.2.Final@jar', jandex: 'org.jboss:jandex:1.1.0.Alpha1', classmate: 'com.fasterxml:classmate:0.8.0', diff --git a/release/release.gradle b/release/release.gradle index e5cddbd8f5..54affb609b 100644 --- a/release/release.gradle +++ b/release/release.gradle @@ -8,15 +8,21 @@ buildDir = "target" idea.module { } +final String[] versionComponents = version.split( '\\.' ); +final String majorVersion = versionComponents[0]; +final String majorMinorVersion = versionComponents[0] + '.' + versionComponents[1]; +final File documentationDir = mkdir( new File( project.buildDir, 'documentation' ) ); +final File javadocDir = mkdir( new File( documentationDir, 'javadocs' ) ); -// Javadocs ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -ext.javadocBuildDir = mkdir( "${buildDir}/documentation/javadocs" ) - -def copyRightYear = new java.util.GregorianCalendar().get( java.util.Calendar.YEAR ); - +/** + * Builds the JavaDocs aggregated (unified) across all the sub-projects + */ task aggregateJavadocs(type: Javadoc) { + description = 'Builds the aggregated (unified) JavaDocs across all sub-projects' + + final int copyrightYear = new GregorianCalendar().get( Calendar.YEAR ); + // exclude any generated sources (this is not working: http://forums.gradle.org/gradle/topics/excluding_generated_source_from_javadoc) exclude "**/generated-src/**" @@ -63,15 +69,14 @@ task aggregateJavadocs(type: Javadoc) { } // apply standard config - description = "Build the aggregated JavaDocs for all modules" maxMemory = '512m' - destinationDir = javadocBuildDir + destinationDir = javadocDir configure( options ) { overview = rootProject.file( 'shared/javadoc/overview.html' ) stylesheetFile = rootProject.file( 'shared/javadoc/stylesheet.css' ) windowTitle = 'Hibernate JavaDocs' docTitle = "Hibernate JavaDoc ($project.version)" - bottom = "Copyright © 2001-$copyRightYear Red Hat, Inc. All Rights Reserved." + bottom = "Copyright © 2001-$copyrightYear Red Hat, Inc. All Rights Reserved." use = true links = [ 'http://download.oracle.com/javase/6/docs/api/', 'http://download.oracle.com/javaee/6/api/' ] group( 'API', apiPackages.asList() ) @@ -83,27 +88,95 @@ task aggregateJavadocs(type: Javadoc) { // work around: addStringOption( "tag", "todo:X" ) } -} -aggregateJavadocs.doLast { - copy { - from rootProject.file( 'shared/javadoc/images' ) - into new File( javadocBuildDir, "/images" ) + doLast { + copy { + from rootProject.file( 'shared/javadoc/images' ) + into new File( javadocDir, "/images" ) + } } } -// release bundles ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +final File documentationUploadStagingDir = mkdir( "${project.buildDir}/tmp/documentation" ); +final File versionedDocumentationDir = mkdir( new File( new File( documentationUploadStagingDir, "orm" ), majorMinorVersion ) ); -ext.releaseBuildDir = mkdir( buildDir ) -task prepareReleaseBundles( dependsOn: [parent.project( 'documentation' ).tasks.buildDocs,aggregateJavadocs] ) +/** + * Builds the complete documentation set in preparation for upload to the doc server. + * + * It builds the docbook manuals as well as the aggregated, cross-project JavaDocs and stages them into + * a single directory that can be directly rsync-ed to the doc server + */ +task buildDocumentation(type: Task, dependsOn: [rootProject.project( 'documentation' ).tasks.buildDocs, aggregateJavadocs]) { + description = "Builds and consolidates all documentation" + doLast { + // copy docbook outputs into target/documentation (javadocs are already there). this is used in + // building the dist bundles + copy { + from "${rootProject.project( 'documentation' ).buildDir}/docbook/publish" + into documentationDir + } + + // now prepare the upload staging directory + versionedDocumentationDir.mkdirs() + copy { + from documentationDir + into versionedDocumentationDir + } + + if ( ! version.endsWith( 'SNAPSHOT' ) ) { + final File currentSymLinkContainerDir = new File( documentationUploadStagingDir, 'stable' ) + currentSymLinkContainerDir.mkdirs(); + ant.symlink( + action: 'delete', + link: "${currentSymLinkContainerDir.absolutePath}/orm" + ) + ant.symlink( + action: 'single', + link: "${currentSymLinkContainerDir.absolutePath}/orm", + resource: "${versionedDocumentationDir.absolutePath}" + ) + } + } +} + +/** + * Upload the documentation to the JBoss doc server + */ +task uploadDocumentation(type:Exec, dependsOn: buildDocumentation) { + description = "Uploads documentation to the JBoss doc server" + + final String url = 'filemgmt.jboss.org:/docs_htdocs/hibernate/'; + + executable 'rsync' + args '-rv', '--links', '--protocol=28', "${documentationUploadStagingDir.absolutePath}/", url + + doFirst { + if ( version.endsWith( "SNAPSHOT" ) ) { + logger.error( "Cannot perform upload of SNAPSHOT documentation" ); + throw new RuntimeException( "Cannot perform upload of SNAPSHOT documentation" ); + } + else { + logger.lifecycle( "Uploading documentation [{$url}]..." ) + } + } + + doLast { + logger.lifecycle( 'Done uploading documentation' ) + } +} + + +/** + * Configuration of the distribution plugin, used to build release bundle as both ZIP and TGZ + */ distributions { main { baseName = 'hibernate-release' contents { - from new File( parent.projectDir, 'lgpl.txt' ) - from new File( parent.projectDir, 'changelog.txt' ) - from new File( parent.projectDir, 'hibernate_logo.gif' ) + from rootProject.file( 'lgpl.txt' ) + from rootProject.file( 'changelog.txt' ) + from rootProject.file( 'hibernate_logo.gif' ) into('lib/required') { from parent.project( 'hibernate-core' ).configurations.provided.files { dep -> dep.name == 'jta' } @@ -145,11 +218,7 @@ distributions { } into('documentation') { - from new File( parent.project( 'documentation' ).buildDir, 'docbook/publish' ) - } - - into('documentation/javadocs') { - from javadocBuildDir + from documentationDir } into( 'project' ) { @@ -180,12 +249,95 @@ distributions { } } -distZip.dependsOn prepareReleaseBundles -distTar.dependsOn prepareReleaseBundles +distZip.dependsOn buildDocumentation +distTar.dependsOn buildDocumentation distTar { compression = Compression.GZIP } -task buildReleaseBundles( dependsOn: [distZip,distTar] ) { - description = "Build release bundle in all formats" +/** + * "virtual" task for building both types of dist bundles + */ +task buildBundles(type: Task, dependsOn: [distZip,distTar]) { + description = "Builds all release bundles" +} + +task uploadBundles(type: Exec, dependsOn: buildBundles) { + description = "Uploads release bundles to SourceForge" + + final String url = "frs.sourceforge.net:/home/frs/project/hibernate/hibernate${majorVersion}/${version}"; + + executable 'rsync' + args '-vr', '-e ssh', "${project.buildDir}/distributions/", url + + doFirst { + if ( version.endsWith( "SNAPSHOT" ) ) { + logger.error( "Cannot perform upload of SNAPSHOT documentation" ); + throw new RuntimeException( "Cannot perform upload of SNAPSHOT bundles" ) + } + else { + logger.lifecycle( "Uploading release bundles [${url}]..." ) + } + } + + doLast { + logger.lifecycle( 'Done uploading release bundles' ) + } +} + + +// Full release related tasks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +task cleanAllSubProjects(type: Task) { + description = 'Performs clean on all sub-projects' +} + +task testAllSubProjects(type: Task) { + description = 'Performs test on all sub-projects' +} + +task publishAllSubProjects(type: Task) { + description = 'Performs publish on all sub-projects' +} + +task buildAllSubProjects(type: Task, dependsOn: [testAllSubProjects,publishAllSubProjects]) + +task uploadReleaseArtifacts(type: Task, dependsOn: [uploadDocumentation, uploadBundles]) + +task announce(type: Task) { doFirst { println 'Hear ye, hear ye...' } } + +task release(type: Task, dependsOn: [cleanAllSubProjects, buildAllSubProjects, uploadReleaseArtifacts, announce]) { + description = "Coordinates all release tasks" +} + + +// must-run-afters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +buildAllSubProjects.mustRunAfter cleanAllSubProjects + +publishAllSubProjects.mustRunAfter testAllSubProjects +publishAllSubProjects.mustRunAfter cleanAllSubProjects + +uploadReleaseArtifacts.mustRunAfter buildAllSubProjects + +announce.mustRunAfter uploadReleaseArtifacts + + +// sub-project task dependencies ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +rootProject.subprojects { Project subproject -> + final Task subprojectCleanTask = subproject.tasks.findByPath( 'clean' ); + if ( subprojectCleanTask != null ) { + cleanAllSubProjects.dependsOn subprojectCleanTask + } + + final Task subprojectTestTask = subproject.tasks.findByPath( 'test' ); + if ( subprojectTestTask != null ) { + testAllSubProjects.dependsOn subprojectTestTask + } + + final Task subprojectPublishTask = subproject.tasks.findByPath( 'publish' ); + if ( subprojectPublishTask != null ) { + publishAllSubProjects.dependsOn subprojectPublishTask + } }