From 91758f74ffa3c517af102e1211a1509723a7ec7f Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Mon, 20 May 2013 15:17:09 -0500 Subject: [PATCH 01/36] HHH-7582 - TableGenerator does not distinguish between different tenants (MultiTenant Schema based) --- .../org/hibernate/id/IdentifierGenerator.java | 38 +- .../id/MultipleHiLoPerTableGenerator.java | 16 +- .../hibernate/id/SequenceHiLoGenerator.java | 13 +- .../org/hibernate/id/TableHiLoGenerator.java | 19 +- .../id/enhanced/AbstractOptimizer.java | 66 +++ .../hibernate/id/enhanced/AccessCallback.java | 15 +- .../id/enhanced/DatabaseStructure.java | 8 +- .../hibernate/id/enhanced/HiLoOptimizer.java | 185 +++++++ .../enhanced/InitialValueAwareOptimizer.java | 44 ++ .../LegacyHiLoAlgorithmOptimizer.java | 148 ++++++ .../hibernate/id/enhanced/NoopOptimizer.java | 68 +++ .../id/enhanced/OptimizerFactory.java | 455 +----------------- .../id/enhanced/PooledLoOptimizer.java | 129 +++++ .../id/enhanced/PooledOptimizer.java | 177 +++++++ .../id/enhanced/SequenceStructure.java | 24 +- .../id/enhanced/SequenceStyleGenerator.java | 111 +++-- .../enhanced/StandardOptimizerDescriptor.java | 121 +++++ .../hibernate/id/enhanced/TableGenerator.java | 169 +++++-- .../hibernate/id/enhanced/TableStructure.java | 27 +- .../hibernate/id/enhanced/package-info.java | 5 + .../id/enhanced/OptimizerUnitTest.java | 15 +- .../enhanced/SequenceStyleConfigUnitTest.java | 28 +- .../NewGeneratorMappingsTest.java | 9 +- .../BasicForcedTableSequenceTest.java | 4 +- .../HiLoForcedTableSequenceTest.java | 6 +- .../PooledForcedTableSequenceTest.java | 6 +- .../enhanced/sequence/HiLoSequenceTest.java | 6 +- .../enhanced/sequence/PooledSequenceTest.java | 6 +- .../idgen/enhanced/table/HiLoTableTest.java | 6 +- .../idgen/enhanced/table/PooledTableTest.java | 6 +- .../test/multitenancy/schema/Invoice.java | 56 +++ .../schema/SchemaBasedMultiTenancyTest.java | 34 ++ 32 files changed, 1407 insertions(+), 613 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/id/enhanced/AbstractOptimizer.java create mode 100644 hibernate-core/src/main/java/org/hibernate/id/enhanced/HiLoOptimizer.java create mode 100644 hibernate-core/src/main/java/org/hibernate/id/enhanced/InitialValueAwareOptimizer.java create mode 100644 hibernate-core/src/main/java/org/hibernate/id/enhanced/LegacyHiLoAlgorithmOptimizer.java create mode 100644 hibernate-core/src/main/java/org/hibernate/id/enhanced/NoopOptimizer.java create mode 100644 hibernate-core/src/main/java/org/hibernate/id/enhanced/PooledLoOptimizer.java create mode 100644 hibernate-core/src/main/java/org/hibernate/id/enhanced/PooledOptimizer.java create mode 100644 hibernate-core/src/main/java/org/hibernate/id/enhanced/StandardOptimizerDescriptor.java create mode 100644 hibernate-core/src/main/java/org/hibernate/id/enhanced/package-info.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/multitenancy/schema/Invoice.java diff --git a/hibernate-core/src/main/java/org/hibernate/id/IdentifierGenerator.java b/hibernate-core/src/main/java/org/hibernate/id/IdentifierGenerator.java index 32a14eddea..e1fe219682 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/IdentifierGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/id/IdentifierGenerator.java @@ -1,10 +1,10 @@ /* * Hibernate, Relational Persistence for Idiomatic Java * - * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as + * Copyright (c) 2008, 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 Middleware LLC. + * 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 @@ -20,9 +20,9 @@ * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA - * */ package org.hibernate.id; + import java.io.Serializable; import org.hibernate.HibernateException; @@ -40,33 +40,33 @@ import org.hibernate.engine.spi.SessionImplementor; * Implementations that accept configuration parameters should * also implement Configurable. *
- * Implementors must be threadsafe + * Implementors must be thread-safe * * @author Gavin King + * * @see PersistentIdentifierGenerator * @see Configurable */ public interface IdentifierGenerator { + /** + * The configuration parameter holding the entity name + */ + public static final String ENTITY_NAME = "entity_name"; - /** - * The configuration parameter holding the entity name - */ - public static final String ENTITY_NAME = "entity_name"; - - /** - * The configuration parameter holding the JPA entity name - */ - public static final String JPA_ENTITY_NAME = "jpa_entity_name"; + /** + * The configuration parameter holding the JPA entity name + */ + public static final String JPA_ENTITY_NAME = "jpa_entity_name"; /** * Generate a new identifier. - * @param session - * @param object the entity or toplevel collection for which the id is being generated + * + * @param session The session from which the request originates + * @param object the entity or collection (idbag) for which the id is being generated * * @return a new identifier - * @throws HibernateException + * + * @throws HibernateException Indicates trouble generating the identifier */ - public Serializable generate(SessionImplementor session, Object object) - throws HibernateException; - + public Serializable generate(SessionImplementor session, Object object) throws HibernateException; } diff --git a/hibernate-core/src/main/java/org/hibernate/id/MultipleHiLoPerTableGenerator.java b/hibernate-core/src/main/java/org/hibernate/id/MultipleHiLoPerTableGenerator.java index 8562cf7f98..60fa18d28a 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/MultipleHiLoPerTableGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/id/MultipleHiLoPerTableGenerator.java @@ -43,7 +43,7 @@ import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.jdbc.spi.SqlStatementLogger; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.id.enhanced.AccessCallback; -import org.hibernate.id.enhanced.OptimizerFactory; +import org.hibernate.id.enhanced.LegacyHiLoAlgorithmOptimizer; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.config.ConfigurationHelper; import org.hibernate.jdbc.AbstractReturningWork; @@ -109,7 +109,7 @@ public class MultipleHiLoPerTableGenerator implements PersistentIdentifierGenera public static final String MAX_LO = "max_lo"; private int maxLo; - private OptimizerFactory.LegacyHiLoAlgorithmOptimizer hiloOptimizer; + private LegacyHiLoAlgorithmOptimizer hiloOptimizer; private Class returnClass; private int keySize; @@ -215,7 +215,15 @@ public class MultipleHiLoPerTableGenerator implements PersistentIdentifierGenera return hiloOptimizer.generate( new AccessCallback() { public IntegralDataTypeHolder getNextValue() { - return session.getTransactionCoordinator().getTransaction().createIsolationDelegate().delegateWork( work, true ); + return session.getTransactionCoordinator().getTransaction().createIsolationDelegate().delegateWork( + work, + true + ); + } + + @Override + public String getTenantIdentifier() { + return session.getTenantIdentifier(); } } ); @@ -282,7 +290,7 @@ public class MultipleHiLoPerTableGenerator implements PersistentIdentifierGenera returnClass = type.getReturnedClass(); if ( maxLo >= 1 ) { - hiloOptimizer = new OptimizerFactory.LegacyHiLoAlgorithmOptimizer( returnClass, maxLo ); + hiloOptimizer = new LegacyHiLoAlgorithmOptimizer( returnClass, maxLo ); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/id/SequenceHiLoGenerator.java b/hibernate-core/src/main/java/org/hibernate/id/SequenceHiLoGenerator.java index da02f952e0..e0bfa069af 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/SequenceHiLoGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/id/SequenceHiLoGenerator.java @@ -29,7 +29,7 @@ import org.hibernate.MappingException; import org.hibernate.dialect.Dialect; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.id.enhanced.AccessCallback; -import org.hibernate.id.enhanced.OptimizerFactory; +import org.hibernate.id.enhanced.LegacyHiLoAlgorithmOptimizer; import org.hibernate.internal.util.config.ConfigurationHelper; import org.hibernate.type.Type; @@ -49,7 +49,7 @@ public class SequenceHiLoGenerator extends SequenceGenerator { private int maxLo; - private OptimizerFactory.LegacyHiLoAlgorithmOptimizer hiloOptimizer; + private LegacyHiLoAlgorithmOptimizer hiloOptimizer; public void configure(Type type, Properties params, Dialect d) throws MappingException { super.configure(type, params, d); @@ -57,7 +57,7 @@ public class SequenceHiLoGenerator extends SequenceGenerator { maxLo = ConfigurationHelper.getInt( MAX_LO, params, 9 ); if ( maxLo >= 1 ) { - hiloOptimizer = new OptimizerFactory.LegacyHiLoAlgorithmOptimizer( + hiloOptimizer = new LegacyHiLoAlgorithmOptimizer( getIdentifierType().getReturnedClass(), maxLo ); @@ -80,6 +80,11 @@ public class SequenceHiLoGenerator extends SequenceGenerator { public IntegralDataTypeHolder getNextValue() { return generateHolder( session ); } + + @Override + public String getTenantIdentifier() { + return session.getTenantIdentifier(); + } } ); } @@ -89,7 +94,7 @@ public class SequenceHiLoGenerator extends SequenceGenerator { * * @return The optimizer */ - OptimizerFactory.LegacyHiLoAlgorithmOptimizer getHiloOptimizer() { + LegacyHiLoAlgorithmOptimizer getHiloOptimizer() { return hiloOptimizer; } } diff --git a/hibernate-core/src/main/java/org/hibernate/id/TableHiLoGenerator.java b/hibernate-core/src/main/java/org/hibernate/id/TableHiLoGenerator.java index 3d821731d0..60c3395980 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/TableHiLoGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/id/TableHiLoGenerator.java @@ -28,7 +28,7 @@ import java.util.Properties; import org.hibernate.dialect.Dialect; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.id.enhanced.AccessCallback; -import org.hibernate.id.enhanced.OptimizerFactory; +import org.hibernate.id.enhanced.LegacyHiLoAlgorithmOptimizer; import org.hibernate.id.enhanced.SequenceStyleGenerator; import org.hibernate.internal.util.config.ConfigurationHelper; import org.hibernate.type.Type; @@ -58,22 +58,24 @@ public class TableHiLoGenerator extends TableGenerator { */ public static final String MAX_LO = "max_lo"; - private OptimizerFactory.LegacyHiLoAlgorithmOptimizer hiloOptimizer; + private LegacyHiLoAlgorithmOptimizer hiloOptimizer; private int maxLo; + @Override public void configure(Type type, Properties params, Dialect d) { - super.configure(type, params, d); - maxLo = ConfigurationHelper.getInt(MAX_LO, params, Short.MAX_VALUE); + super.configure( type, params, d ); + maxLo = ConfigurationHelper.getInt( MAX_LO, params, Short.MAX_VALUE ); if ( maxLo >= 1 ) { - hiloOptimizer = new OptimizerFactory.LegacyHiLoAlgorithmOptimizer( type.getReturnedClass(), maxLo ); + hiloOptimizer = new LegacyHiLoAlgorithmOptimizer( type.getReturnedClass(), maxLo ); } } + @Override public synchronized Serializable generate(final SessionImplementor session, Object obj) { // maxLo < 1 indicates a hilo generator with no hilo :? - if ( maxLo < 1 ) { + if ( maxLo < 1 ) { //keep the behavior consistent even for boundary usages IntegralDataTypeHolder value = null; while ( value == null || value.lt( 0 ) ) { @@ -87,6 +89,11 @@ public class TableHiLoGenerator extends TableGenerator { public IntegralDataTypeHolder getNextValue() { return generateHolder( session ); } + + @Override + public String getTenantIdentifier() { + return session.getTenantIdentifier(); + } } ); } diff --git a/hibernate-core/src/main/java/org/hibernate/id/enhanced/AbstractOptimizer.java b/hibernate-core/src/main/java/org/hibernate/id/enhanced/AbstractOptimizer.java new file mode 100644 index 0000000000..526e1ab3b5 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/id/enhanced/AbstractOptimizer.java @@ -0,0 +1,66 @@ +/* + * 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.id.enhanced; + +import org.hibernate.HibernateException; + +/** + * Common support for optimizer implementations. + * + * @author Steve Ebersole + */ +public abstract class AbstractOptimizer implements Optimizer { + protected final Class returnClass; + protected final int incrementSize; + + /** + * Construct an optimizer + * + * @param returnClass The expected id class. + * @param incrementSize The increment size + */ + AbstractOptimizer(Class returnClass, int incrementSize) { + if ( returnClass == null ) { + throw new HibernateException( "return class is required" ); + } + this.returnClass = returnClass; + this.incrementSize = incrementSize; + } + + /** + * Getter for property 'returnClass'. This is the Java + * class which is used to represent the id (e.g. {@link Long}). + * + * @return Value for property 'returnClass'. + */ + @SuppressWarnings( {"UnusedDeclaration"}) + public final Class getReturnClass() { + return returnClass; + } + + @Override + public final int getIncrementSize() { + return incrementSize; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/id/enhanced/AccessCallback.java b/hibernate-core/src/main/java/org/hibernate/id/enhanced/AccessCallback.java index 639300a939..081e3c7c0f 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/enhanced/AccessCallback.java +++ b/hibernate-core/src/main/java/org/hibernate/id/enhanced/AccessCallback.java @@ -1,10 +1,10 @@ /* * Hibernate, Relational Persistence for Idiomatic Java * - * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as + * Copyright (c) 2008, 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 Middleware LLC. + * 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 @@ -20,9 +20,9 @@ * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA - * */ -package org.hibernate.id.enhanced; +package org.hibernate.id.enhanced; + import org.hibernate.id.IntegralDataTypeHolder; /** @@ -38,4 +38,11 @@ public interface AccessCallback { * @return The next value. */ public IntegralDataTypeHolder getNextValue(); + + /** + * Obtain the tenant identifier (multi-tenancy), if one, associated with this callback. + * + * @return The tenant identifier + */ + public String getTenantIdentifier(); } diff --git a/hibernate-core/src/main/java/org/hibernate/id/enhanced/DatabaseStructure.java b/hibernate-core/src/main/java/org/hibernate/id/enhanced/DatabaseStructure.java index f6f327c37a..f0080150f9 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/enhanced/DatabaseStructure.java +++ b/hibernate-core/src/main/java/org/hibernate/id/enhanced/DatabaseStructure.java @@ -1,10 +1,10 @@ /* * Hibernate, Relational Persistence for Idiomatic Java * - * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as + * Copyright (c) 2008, 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 Middleware LLC. + * 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 @@ -20,9 +20,9 @@ * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA - * */ package org.hibernate.id.enhanced; + import org.hibernate.dialect.Dialect; import org.hibernate.engine.spi.SessionImplementor; @@ -94,4 +94,4 @@ public interface DatabaseStructure { * @return {@code true} if the actual database structure is a sequence; {@code false} otherwise. */ public boolean isPhysicalSequence(); -} \ No newline at end of file +} diff --git a/hibernate-core/src/main/java/org/hibernate/id/enhanced/HiLoOptimizer.java b/hibernate-core/src/main/java/org/hibernate/id/enhanced/HiLoOptimizer.java new file mode 100644 index 0000000000..60403f9975 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/id/enhanced/HiLoOptimizer.java @@ -0,0 +1,185 @@ +/* + * 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.id.enhanced; + +import java.io.Serializable; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.jboss.logging.Logger; + +import org.hibernate.HibernateException; +import org.hibernate.id.IntegralDataTypeHolder; + +/** + * Optimizer which applies a 'hilo' algorithm in memory to achieve + * optimization. + *

+ * A 'hilo' algorithm is simply a means for a single value stored in the + * database to represent a "bucket" of possible, contiguous values. The + * database value identifies which particular bucket we are on. + *

+ * This database value must be paired with another value that defines the + * size of the bucket; the number of possible values available. + * The {@link #getIncrementSize() incrementSize} serves this purpose. The + * naming here is meant more for consistency in that this value serves the + * same purpose as the increment supplied to the {@link PooledOptimizer}. + *

+ * The general algorithms used to determine the bucket are:

    + *
  1. {@code upperLimit = (databaseValue * incrementSize) + 1}
  2. + *
  3. {@code lowerLimit = upperLimit - incrementSize}
  4. + *
+ * As an example, consider a case with incrementSize of 20. Initially the + * database holds 1:
    + *
  1. {@code upperLimit = (1 * 20) + 1 = 21}
  2. + *
  3. {@code lowerLimit = 21 - 20 = 1}
  4. + *
+ * From there we increment the value from lowerLimit until we reach the + * upperLimit, at which point we would define a new bucket. The database + * now contains 2, though incrementSize remains unchanged:
    + *
  1. {@code upperLimit = (2 * 20) + 1 = 41}
  2. + *
  3. {@code lowerLimit = 41 - 20 = 21}
  4. + *
+ * And so on... + *

+ * Note, 'value' always (after init) holds the next value to return + * + * @author Steve Ebersole + */ +public class HiLoOptimizer extends AbstractOptimizer { + private static final Logger log = Logger.getLogger( HiLoOptimizer.class ); + + private static class GenerationState { + private IntegralDataTypeHolder lastSourceValue; + private IntegralDataTypeHolder upperLimit; + private IntegralDataTypeHolder value; + } + + + /** + * Constructs a HiLoOptimizer + * + * @param returnClass The Java type of the values to be generated + * @param incrementSize The increment size. + */ + public HiLoOptimizer(Class returnClass, int incrementSize) { + super( returnClass, incrementSize ); + if ( incrementSize < 1 ) { + throw new HibernateException( "increment size cannot be less than 1" ); + } + if ( log.isTraceEnabled() ) { + log.tracev( "Creating hilo optimizer with [incrementSize={0}; returnClass={1}]", incrementSize, returnClass.getName() ); + } + } + + @Override + public synchronized Serializable generate(AccessCallback callback) { + final GenerationState generationState = locateGenerationState( callback.getTenantIdentifier() ); + + if ( generationState.lastSourceValue == null ) { + // first call, so initialize ourselves. we need to read the database + // value and set up the 'bucket' boundaries + generationState.lastSourceValue = callback.getNextValue(); + while ( generationState.lastSourceValue.lt( 1 ) ) { + generationState.lastSourceValue = callback.getNextValue(); + } + // upperLimit defines the upper end of the bucket values + generationState.upperLimit = generationState.lastSourceValue.copy().multiplyBy( incrementSize ).increment(); + // initialize value to the low end of the bucket + generationState.value = generationState.upperLimit.copy().subtract( incrementSize ); + } + else if ( ! generationState.upperLimit.gt( generationState.value ) ) { + generationState.lastSourceValue = callback.getNextValue(); + generationState.upperLimit = generationState.lastSourceValue.copy().multiplyBy( incrementSize ).increment(); + } + return generationState.value.makeValueThenIncrement(); + } + + private GenerationState noTenantState; + private Map tenantSpecificState; + + private GenerationState locateGenerationState(String tenantIdentifier) { + if ( tenantIdentifier == null ) { + if ( noTenantState == null ) { + noTenantState = new GenerationState(); + } + return noTenantState; + } + else { + GenerationState state; + if ( tenantSpecificState == null ) { + tenantSpecificState = new ConcurrentHashMap(); + state = new GenerationState(); + tenantSpecificState.put( tenantIdentifier, state ); + } + else { + state = tenantSpecificState.get( tenantIdentifier ); + if ( state == null ) { + state = new GenerationState(); + tenantSpecificState.put( tenantIdentifier, state ); + } + } + return state; + } + } + + private GenerationState noTenantGenerationState() { + if ( noTenantState == null ) { + throw new IllegalStateException( "Could not locate previous generation state for no-tenant" ); + } + return noTenantState; + } + + @Override + public IntegralDataTypeHolder getLastSourceValue() { + return noTenantGenerationState().lastSourceValue; + } + + @Override + public boolean applyIncrementSizeToSourceValues() { + return false; + } + + /** + * Getter for property 'lastValue'. + *

+ * Exposure intended for testing purposes. + * + * @return Value for property 'lastValue'. + */ + public IntegralDataTypeHolder getLastValue() { + return noTenantGenerationState().value.copy().decrement(); + } + + /** + * Getter for property 'upperLimit'. + *

+ * Exposure intended for testing purposes. + * + * @return Value for property 'upperLimit'. + */ + public IntegralDataTypeHolder getHiValue() { + return noTenantGenerationState().upperLimit; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/id/enhanced/InitialValueAwareOptimizer.java b/hibernate-core/src/main/java/org/hibernate/id/enhanced/InitialValueAwareOptimizer.java new file mode 100644 index 0000000000..cac3079c13 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/id/enhanced/InitialValueAwareOptimizer.java @@ -0,0 +1,44 @@ +/* + * 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.id.enhanced; + +/** + * Marker interface for optimizer which wish to know the user-specified initial value. + *

+ * Used instead of constructor injection since that is already a public understanding and + * because not all optimizers care. + * + * @author Steve Ebersole + */ +public interface InitialValueAwareOptimizer { + /** + * Reports the user specified initial value to the optimizer. + *

+ * -1 is used to indicate that the user did not specify. + * + * @param initialValue The initial value specified by the user, or -1 to indicate that the + * user did not specify. + */ + public void injectInitialValue(long initialValue); +} diff --git a/hibernate-core/src/main/java/org/hibernate/id/enhanced/LegacyHiLoAlgorithmOptimizer.java b/hibernate-core/src/main/java/org/hibernate/id/enhanced/LegacyHiLoAlgorithmOptimizer.java new file mode 100644 index 0000000000..abd33a9ca2 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/id/enhanced/LegacyHiLoAlgorithmOptimizer.java @@ -0,0 +1,148 @@ +/* + * 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.id.enhanced; + +import java.io.Serializable; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.jboss.logging.Logger; + +import org.hibernate.HibernateException; +import org.hibernate.id.IntegralDataTypeHolder; + +/** + * Slight variation from {@link HiLoOptimizer}, maintaining compatibility with the values generated by the + * legacy Hibernate hilo based generators. + * + * @author Steve Ebersole + */ +public class LegacyHiLoAlgorithmOptimizer extends AbstractOptimizer { + private static final Logger log = Logger.getLogger( LegacyHiLoAlgorithmOptimizer.class ); + + private final long initialMaxLo; + + private static class GenerationState { + private long maxLo; + private long lo; + private IntegralDataTypeHolder hi; + + private IntegralDataTypeHolder lastSourceValue; + private IntegralDataTypeHolder value; + } + + /** + * Constructs a LegacyHiLoAlgorithmOptimizer + * + * @param returnClass The Java type of the values to be generated + * @param incrementSize The increment size. + */ + public LegacyHiLoAlgorithmOptimizer(Class returnClass, int incrementSize) { + super( returnClass, incrementSize ); + if ( incrementSize < 1 ) { + throw new HibernateException( "increment size cannot be less than 1" ); + } + if ( log.isTraceEnabled() ) { + log.tracev( "Creating hilo optimizer (legacy) with [incrementSize={0}; returnClass={1}]", incrementSize, returnClass.getName() ); + } + initialMaxLo = incrementSize; + } + + @Override + public synchronized Serializable generate(AccessCallback callback) { + final GenerationState generationState = locateGenerationState( callback.getTenantIdentifier() ); + + if ( generationState.lo > generationState.maxLo ) { + generationState.lastSourceValue = callback.getNextValue(); + generationState.lo = generationState.lastSourceValue.eq( 0 ) ? 1 : 0; + generationState.hi = generationState.lastSourceValue.copy().multiplyBy( generationState.maxLo + 1 ); + } + generationState.value = generationState.hi.copy().add( generationState.lo++ ); + return generationState.value.makeValue(); + } + + private GenerationState noTenantState; + private Map tenantSpecificState; + + private GenerationState locateGenerationState(String tenantIdentifier) { + if ( tenantIdentifier == null ) { + if ( noTenantState == null ) { + noTenantState = createGenerationState(); + } + return noTenantState; + } + else { + GenerationState state; + if ( tenantSpecificState == null ) { + tenantSpecificState = new ConcurrentHashMap(); + state = createGenerationState(); + tenantSpecificState.put( tenantIdentifier, state ); + } + else { + state = tenantSpecificState.get( tenantIdentifier ); + if ( state == null ) { + state = createGenerationState(); + tenantSpecificState.put( tenantIdentifier, state ); + } + } + return state; + } + } + + private GenerationState createGenerationState() { + final GenerationState state = new GenerationState(); + state.maxLo = initialMaxLo; + state.lo = initialMaxLo + 1; + return state; + } + + private GenerationState noTenantGenerationState() { + if ( noTenantState == null ) { + throw new IllegalStateException( "Could not locate previous generation state for no-tenant" ); + } + return noTenantState; + } + + @Override + public IntegralDataTypeHolder getLastSourceValue() { + return noTenantGenerationState().lastSourceValue.copy(); + } + + @Override + public boolean applyIncrementSizeToSourceValues() { + return false; + } + + /** + * Getter for property 'lastValue'. + *

+ * Exposure intended for testing purposes. + * + * @return Value for property 'lastValue'. + */ + @SuppressWarnings( {"UnusedDeclaration"}) + public IntegralDataTypeHolder getLastValue() { + return noTenantGenerationState().value; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/id/enhanced/NoopOptimizer.java b/hibernate-core/src/main/java/org/hibernate/id/enhanced/NoopOptimizer.java new file mode 100644 index 0000000000..aba217aa91 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/id/enhanced/NoopOptimizer.java @@ -0,0 +1,68 @@ +/* + * 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.id.enhanced; + +import java.io.Serializable; + +import org.hibernate.id.IntegralDataTypeHolder; + +/** + * An optimizer that performs no optimization. The database is hit for + * every request. + */ +public class NoopOptimizer extends AbstractOptimizer { + private IntegralDataTypeHolder lastSourceValue; + + /** + * Constructs a NoopOptimizer + * + * @param returnClass The Java type of the values to be generated + * @param incrementSize The increment size. + */ + public NoopOptimizer(Class returnClass, int incrementSize) { + super( returnClass, incrementSize ); + } + + @Override + public Serializable generate(AccessCallback callback) { + // IMPL NOTE : it is incredibly important that the method-local variable be used here to + // avoid concurrency issues. + IntegralDataTypeHolder value = null; + while ( value == null || value.lt( 1 ) ) { + value = callback.getNextValue(); + } + lastSourceValue = value; + return value.makeValue(); + } + + @Override + public IntegralDataTypeHolder getLastSourceValue() { + return lastSourceValue; + } + + @Override + public boolean applyIncrementSizeToSourceValues() { + return false; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/id/enhanced/OptimizerFactory.java b/hibernate-core/src/main/java/org/hibernate/id/enhanced/OptimizerFactory.java index 5e64589894..9d4863655e 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/enhanced/OptimizerFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/id/enhanced/OptimizerFactory.java @@ -23,16 +23,12 @@ */ package org.hibernate.id.enhanced; -import java.io.Serializable; import java.lang.reflect.Constructor; import org.jboss.logging.Logger; -import org.hibernate.HibernateException; -import org.hibernate.id.IntegralDataTypeHolder; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.ReflectHelper; -import org.hibernate.internal.util.StringHelper; /** * Factory for {@link Optimizer} instances. @@ -40,95 +36,24 @@ import org.hibernate.internal.util.StringHelper; * @author Steve Ebersole */ public class OptimizerFactory { - private static final CoreMessageLogger LOG = Logger.getMessageLogger( + private static final CoreMessageLogger LOG = Logger.getMessageLogger( CoreMessageLogger.class, OptimizerFactory.class.getName() ); - public static enum StandardOptimizerDescriptor { - NONE( "none", NoopOptimizer.class ), - HILO( "hilo", HiLoOptimizer.class ), - LEGACY_HILO( "legacy-hilo", LegacyHiLoAlgorithmOptimizer.class ), - POOLED( "pooled", PooledOptimizer.class, true ), - POOLED_LO( "pooled-lo", PooledLoOptimizer.class, true ); - - private final String externalName; - private final Class optimizerClass; - private final boolean isPooled; - - StandardOptimizerDescriptor(String externalName, Class optimizerClass) { - this( externalName, optimizerClass, false ); - } - - StandardOptimizerDescriptor(String externalName, Class optimizerClass, boolean pooled) { - this.externalName = externalName; - this.optimizerClass = optimizerClass; - this.isPooled = pooled; - } - - public String getExternalName() { - return externalName; - } - - public Class getOptimizerClass() { - return optimizerClass; - } - - public boolean isPooled() { - return isPooled; - } - - public static StandardOptimizerDescriptor fromExternalName(String externalName) { - if ( StringHelper.isEmpty( externalName ) ) { - LOG.debug( "No optimizer specified, using NONE as default" ); - return NONE; - } - else if ( NONE.externalName.equals( externalName ) ) { - return NONE; - } - else if ( HILO.externalName.equals( externalName ) ) { - return HILO; - } - else if ( LEGACY_HILO.externalName.equals( externalName ) ) { - return LEGACY_HILO; - } - else if ( POOLED.externalName.equals( externalName ) ) { - return POOLED; - } - else if ( POOLED_LO.externalName.equals( externalName ) ) { - return POOLED_LO; - } - else { - LOG.debugf( "Unknown optimizer key [%s]; returning null assuming Optimizer impl class name", externalName ); - return null; - } - } - } - /** - * Marker interface for optimizer which wish to know the user-specified initial value. - *

- * Used instead of constructor injection since that is already a public understanding and - * because not all optimizers care. + * Does the given optimizer name represent a pooled strategy? + * + * @param optimizerName The name of the optimizer + * + * @return {@code true} indicates the optimizer is a pooled strategy. */ - public static interface InitialValueAwareOptimizer { - /** - * Reports the user specified initial value to the optimizer. - *

- * -1 is used to indicate that the user did not specify. - * - * @param initialValue The initial value specified by the user, or -1 to indicate that the - * user did not specify. - */ - public void injectInitialValue(long initialValue); - } - - public static boolean isPooledOptimizer(String type) { - final StandardOptimizerDescriptor standardDescriptor = StandardOptimizerDescriptor.fromExternalName( type ); + public static boolean isPooledOptimizer(String optimizerName) { + final StandardOptimizerDescriptor standardDescriptor = StandardOptimizerDescriptor.fromExternalName( optimizerName ); return standardDescriptor != null && standardDescriptor.isPooled(); } - private static Class[] CTOR_SIG = new Class[] { Class.class, int.class }; + private static final Class[] CTOR_SIG = new Class[] { Class.class, int.class }; /** * Builds an optimizer @@ -161,11 +86,11 @@ public class OptimizerFactory { } try { - Constructor ctor = optimizerClass.getConstructor( CTOR_SIG ); - return ( Optimizer ) ctor.newInstance( returnClass, Integer.valueOf( incrementSize ) ); + final Constructor ctor = optimizerClass.getConstructor( CTOR_SIG ); + return (Optimizer) ctor.newInstance( returnClass, Integer.valueOf( incrementSize ) ); } catch( Throwable ignore ) { - LOG.unableToInstantiateOptimizer( type ); + LOG.unableToInstantiateOptimizer( type ); } return buildFallbackOptimizer( returnClass, incrementSize ); @@ -195,349 +120,8 @@ public class OptimizerFactory { } /** - * Common support for optimizer implementations. - */ - public static abstract class OptimizerSupport implements Optimizer { - protected final Class returnClass; - protected final int incrementSize; - - /** - * Construct an optimizer - * - * @param returnClass The expected id class. - * @param incrementSize The increment size - */ - protected OptimizerSupport(Class returnClass, int incrementSize) { - if ( returnClass == null ) { - throw new HibernateException( "return class is required" ); - } - this.returnClass = returnClass; - this.incrementSize = incrementSize; - } - - /** - * Getter for property 'returnClass'. This is the Java - * class which is used to represent the id (e.g. {@link java.lang.Long}). - * - * @return Value for property 'returnClass'. - */ - @SuppressWarnings( {"UnusedDeclaration"}) - public final Class getReturnClass() { - return returnClass; - } - - @Override - public final int getIncrementSize() { - return incrementSize; - } - } - - /** - * An optimizer that performs no optimization. The database is hit for - * every request. - */ - public static class NoopOptimizer extends OptimizerSupport { - private IntegralDataTypeHolder lastSourceValue; - - public NoopOptimizer(Class returnClass, int incrementSize) { - super( returnClass, incrementSize ); - } - - @Override - public Serializable generate(AccessCallback callback) { - // IMPL NOTE : it is incredibly important that the method-local variable be used here to - // avoid concurrency issues. - IntegralDataTypeHolder value = null; - while ( value == null || value.lt( 1 ) ) { - value = callback.getNextValue(); - } - lastSourceValue = value; - return value.makeValue(); - } - - @Override - public IntegralDataTypeHolder getLastSourceValue() { - return lastSourceValue; - } - - @Override - public boolean applyIncrementSizeToSourceValues() { - return false; - } - } - - /** - * Optimizer which applies a 'hilo' algorithm in memory to achieve - * optimization. - *

- * A 'hilo' algorithm is simply a means for a single value stored in the - * database to represent a "bucket" of possible, contiguous values. The - * database value identifies which particular bucket we are on. - *

- * This database value must be paired with another value that defines the - * size of the bucket; the number of possible values available. - * The {@link #getIncrementSize() incrementSize} serves this purpose. The - * naming here is meant more for consistency in that this value serves the - * same purpose as the increment supplied to the {@link PooledOptimizer}. - *

- * The general algorithms used to determine the bucket are:

    - *
  1. {@code upperLimit = (databaseValue * incrementSize) + 1}
  2. - *
  3. {@code lowerLimit = upperLimit - incrementSize}
  4. - *
- * As an example, consider a case with incrementSize of 20. Initially the - * database holds 1:
    - *
  1. {@code upperLimit = (1 * 20) + 1 = 21}
  2. - *
  3. {@code lowerLimit = 21 - 20 = 1}
  4. - *
- * From there we increment the value from lowerLimit until we reach the - * upperLimit, at which point we would define a new bucket. The database - * now contains 2, though incrementSize remains unchanged:
    - *
  1. {@code upperLimit = (2 * 20) + 1 = 41}
  2. - *
  3. {@code lowerLimit = 41 - 20 = 21}
  4. - *
- * And so on... - *

- * Note, 'value' always (after init) holds the next value to return - */ - public static class HiLoOptimizer extends OptimizerSupport { - private IntegralDataTypeHolder lastSourceValue; - private IntegralDataTypeHolder upperLimit; - private IntegralDataTypeHolder value; - - public HiLoOptimizer(Class returnClass, int incrementSize) { - super( returnClass, incrementSize ); - if ( incrementSize < 1 ) - throw new HibernateException( "increment size cannot be less than 1" ); - if ( LOG.isTraceEnabled() ) { - LOG.tracev( "Creating hilo optimizer with [incrementSize={0}; returnClass={1}]", incrementSize, returnClass.getName() ); - } - } - - @Override - public synchronized Serializable generate(AccessCallback callback) { - if ( lastSourceValue == null ) { - // first call, so initialize ourselves. we need to read the database - // value and set up the 'bucket' boundaries - lastSourceValue = callback.getNextValue(); - while ( lastSourceValue.lt( 1 ) ) { - lastSourceValue = callback.getNextValue(); - } - // upperLimit defines the upper end of the bucket values - upperLimit = lastSourceValue.copy().multiplyBy( incrementSize ).increment(); - // initialize value to the low end of the bucket - value = upperLimit.copy().subtract( incrementSize ); - } - else if ( ! upperLimit.gt( value ) ) { - lastSourceValue = callback.getNextValue(); - upperLimit = lastSourceValue.copy().multiplyBy( incrementSize ).increment(); - } - return value.makeValueThenIncrement(); - } - - @Override - public IntegralDataTypeHolder getLastSourceValue() { - return lastSourceValue; - } - - @Override - public boolean applyIncrementSizeToSourceValues() { - return false; - } - - /** - * Getter for property 'lastValue'. - *

- * Exposure intended for testing purposes. - * - * @return Value for property 'lastValue'. - */ - public IntegralDataTypeHolder getLastValue() { - return value.copy().decrement(); - } - - /** - * Getter for property 'upperLimit'. - *

- * Exposure intended for testing purposes. - * - * @return Value for property 'upperLimit'. - */ - public IntegralDataTypeHolder getHiValue() { - return upperLimit; - } - } - - public static class LegacyHiLoAlgorithmOptimizer extends OptimizerSupport { - private long maxLo; - private long lo; - private IntegralDataTypeHolder hi; - - private IntegralDataTypeHolder lastSourceValue; - private IntegralDataTypeHolder value; - - - public LegacyHiLoAlgorithmOptimizer(Class returnClass, int incrementSize) { - super( returnClass, incrementSize ); - if ( incrementSize < 1 ) - throw new HibernateException( "increment size cannot be less than 1" ); - if ( LOG.isTraceEnabled() ) { - LOG.tracev( "Creating hilo optimizer (legacy) with [incrementSize={0}; returnClass={1}]", incrementSize, returnClass.getName() ); - } - maxLo = incrementSize; - lo = maxLo+1; - } - - @Override - public synchronized Serializable generate(AccessCallback callback) { - if ( lo > maxLo ) { - lastSourceValue = callback.getNextValue(); - lo = lastSourceValue.eq( 0 ) ? 1 : 0; - hi = lastSourceValue.copy().multiplyBy( maxLo+1 ); - } - value = hi.copy().add( lo++ ); - return value.makeValue(); - } - - @Override - public IntegralDataTypeHolder getLastSourceValue() { - return lastSourceValue.copy(); - } - - @Override - public boolean applyIncrementSizeToSourceValues() { - return false; - } - - /** - * Getter for property 'lastValue'. - *

- * Exposure intended for testing purposes. - * - * @return Value for property 'lastValue'. - */ - @SuppressWarnings( {"UnusedDeclaration"}) - public IntegralDataTypeHolder getLastValue() { - return value; - } - } - - /** - * Optimizer which uses a pool of values, storing the next low value of the - * range in the database. - *

- * Note that this optimizer works essentially the same as the - * {@link HiLoOptimizer} except that here the bucket ranges are actually - * encoded into the database structures. - *

- * Note if you prefer that the database value be interpreted as the bottom end of our current range, - * then use the {@link PooledLoOptimizer} strategy - */ - public static class PooledOptimizer extends OptimizerSupport implements InitialValueAwareOptimizer { - private IntegralDataTypeHolder hiValue; - private IntegralDataTypeHolder value; - private long initialValue = -1; - - public PooledOptimizer(Class returnClass, int incrementSize) { - super( returnClass, incrementSize ); - if ( incrementSize < 1 ) { - throw new HibernateException( "increment size cannot be less than 1" ); - } - if ( LOG.isTraceEnabled() ) { - LOG.tracev( "Creating pooled optimizer with [incrementSize={0}; returnClass={1}]", incrementSize, returnClass.getName() ); - } - } - - @Override - public synchronized Serializable generate(AccessCallback callback) { - if ( hiValue == null ) { - value = callback.getNextValue(); - // unfortunately not really safe to normalize this - // to 1 as an initial value like we do the others - // because we would not be able to control this if - // we are using a sequence... - if (value.lt(1)) LOG.pooledOptimizerReportedInitialValue(value); - // the call to obtain next-value just gave us the initialValue - if ( ( initialValue == -1 && value.lt( incrementSize ) ) || value.eq( initialValue ) ) { - hiValue = callback.getNextValue(); - } - else { - hiValue = value; - value = hiValue.copy().subtract( incrementSize ); - } - } - else if ( ! hiValue.gt( value ) ) { - hiValue = callback.getNextValue(); - value = hiValue.copy().subtract( incrementSize ); - } - return value.makeValueThenIncrement(); - } - - @Override - public IntegralDataTypeHolder getLastSourceValue() { - return hiValue; - } - - @Override - public boolean applyIncrementSizeToSourceValues() { - return true; - } - - /** - * Getter for property 'lastValue'. - *

- * Exposure intended for testing purposes. - * - * @return Value for property 'lastValue'. - */ - public IntegralDataTypeHolder getLastValue() { - return value.copy().decrement(); - } - - @Override - public void injectInitialValue(long initialValue) { - this.initialValue = initialValue; - } - } - - public static class PooledLoOptimizer extends OptimizerSupport { - private IntegralDataTypeHolder lastSourceValue; // last value read from db source - private IntegralDataTypeHolder value; // the current generator value - - public PooledLoOptimizer(Class returnClass, int incrementSize) { - super( returnClass, incrementSize ); - if ( incrementSize < 1 ) { - throw new HibernateException( "increment size cannot be less than 1" ); - } - if ( LOG.isTraceEnabled() ) { - LOG.tracev( "Creating pooled optimizer (lo) with [incrementSize={0}; returnClass=]", incrementSize, returnClass.getName() ); - } - } - - @Override - public synchronized Serializable generate(AccessCallback callback) { - if ( lastSourceValue == null || ! value.lt( lastSourceValue.copy().add( incrementSize ) ) ) { - lastSourceValue = callback.getNextValue(); - value = lastSourceValue.copy(); - // handle cases where initial-value is less that one (hsqldb for instance). - while ( value.lt( 1 ) ) { - value.increment(); - } - } - return value.makeValueThenIncrement(); - } - - @Override - public IntegralDataTypeHolder getLastSourceValue() { - return lastSourceValue; - } - - @Override - public boolean applyIncrementSizeToSourceValues() { - return true; - } - } - - /** + * Deprecated! + * * @deprecated Use {@link StandardOptimizerDescriptor#getExternalName()} via {@link StandardOptimizerDescriptor#NONE} */ @Deprecated @@ -545,6 +129,8 @@ public class OptimizerFactory { public static final String NONE = StandardOptimizerDescriptor.NONE.getExternalName(); /** + * Deprecated! + * * @deprecated Use {@link StandardOptimizerDescriptor#getExternalName()} via {@link StandardOptimizerDescriptor#HILO} */ @Deprecated @@ -552,6 +138,8 @@ public class OptimizerFactory { public static final String HILO = StandardOptimizerDescriptor.HILO.getExternalName(); /** + * Deprecated! + * * @deprecated Use {@link StandardOptimizerDescriptor#getExternalName()} via {@link StandardOptimizerDescriptor#LEGACY_HILO} */ @Deprecated @@ -559,6 +147,8 @@ public class OptimizerFactory { public static final String LEGACY_HILO = "legacy-hilo"; /** + * Deprecated! + * * @deprecated Use {@link StandardOptimizerDescriptor#getExternalName()} via {@link StandardOptimizerDescriptor#POOLED} */ @Deprecated @@ -566,9 +156,14 @@ public class OptimizerFactory { public static final String POOL = "pooled"; /** + * Deprecated! + * * @deprecated Use {@link StandardOptimizerDescriptor#getExternalName()} via {@link StandardOptimizerDescriptor#POOLED_LO} */ @Deprecated @SuppressWarnings( {"UnusedDeclaration"}) public static final String POOL_LO = "pooled-lo"; + + private OptimizerFactory() { + } } diff --git a/hibernate-core/src/main/java/org/hibernate/id/enhanced/PooledLoOptimizer.java b/hibernate-core/src/main/java/org/hibernate/id/enhanced/PooledLoOptimizer.java new file mode 100644 index 0000000000..399201528b --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/id/enhanced/PooledLoOptimizer.java @@ -0,0 +1,129 @@ +/* + * 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.id.enhanced; + +import java.io.Serializable; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.jboss.logging.Logger; + +import org.hibernate.HibernateException; +import org.hibernate.id.IntegralDataTypeHolder; + +/** + * Variation of {@link PooledOptimizer} which interprets the incoming database value as the lo value, rather than + * the hi value. + * + * @author Steve Ebersole + * + * @see PooledOptimizer + */ +public class PooledLoOptimizer extends AbstractOptimizer { + private static final Logger log = Logger.getLogger( PooledLoOptimizer.class ); + + private static class GenerationState { + // last value read from db source + private IntegralDataTypeHolder lastSourceValue; + // the current generator value + private IntegralDataTypeHolder value; + } + + /** + * Constructs a PooledLoOptimizer. + * + * @param returnClass The Java type of the values to be generated + * @param incrementSize The increment size. + */ + public PooledLoOptimizer(Class returnClass, int incrementSize) { + super( returnClass, incrementSize ); + if ( incrementSize < 1 ) { + throw new HibernateException( "increment size cannot be less than 1" ); + } + if ( log.isTraceEnabled() ) { + log.tracev( "Creating pooled optimizer (lo) with [incrementSize={0}; returnClass=]", incrementSize, returnClass.getName() ); + } + } + + @Override + public synchronized Serializable generate(AccessCallback callback) { + final GenerationState generationState = locateGenerationState( callback.getTenantIdentifier() ); + + if ( generationState.lastSourceValue == null + || ! generationState.value.lt( generationState.lastSourceValue.copy().add( incrementSize ) ) ) { + generationState.lastSourceValue = callback.getNextValue(); + generationState.value = generationState.lastSourceValue.copy(); + // handle cases where initial-value is less that one (hsqldb for instance). + while ( generationState.value.lt( 1 ) ) { + generationState.value.increment(); + } + } + return generationState.value.makeValueThenIncrement(); + } + + private GenerationState noTenantState; + private Map tenantSpecificState; + + private GenerationState locateGenerationState(String tenantIdentifier) { + if ( tenantIdentifier == null ) { + if ( noTenantState == null ) { + noTenantState = new GenerationState(); + } + return noTenantState; + } + else { + GenerationState state; + if ( tenantSpecificState == null ) { + tenantSpecificState = new ConcurrentHashMap(); + state = new GenerationState(); + tenantSpecificState.put( tenantIdentifier, state ); + } + else { + state = tenantSpecificState.get( tenantIdentifier ); + if ( state == null ) { + state = new GenerationState(); + tenantSpecificState.put( tenantIdentifier, state ); + } + } + return state; + } + } + + private GenerationState noTenantGenerationState() { + if ( noTenantState == null ) { + throw new IllegalStateException( "Could not locate previous generation state for no-tenant" ); + } + return noTenantState; + } + + @Override + public IntegralDataTypeHolder getLastSourceValue() { + return noTenantGenerationState().lastSourceValue; + } + + @Override + public boolean applyIncrementSizeToSourceValues() { + return true; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/id/enhanced/PooledOptimizer.java b/hibernate-core/src/main/java/org/hibernate/id/enhanced/PooledOptimizer.java new file mode 100644 index 0000000000..b833389ab1 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/id/enhanced/PooledOptimizer.java @@ -0,0 +1,177 @@ +/* + * 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.id.enhanced; + +import java.io.Serializable; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.jboss.logging.Logger; + +import org.hibernate.HibernateException; +import org.hibernate.id.IntegralDataTypeHolder; +import org.hibernate.internal.CoreMessageLogger; + +/** + * Optimizer which uses a pool of values, storing the next low value of the + * range in the database. + *

+ * Note that this optimizer works essentially the same as the + * {@link org.hibernate.id.enhanced.HiLoOptimizer} except that here the bucket ranges are actually + * encoded into the database structures. + *

+ * Note if you prefer that the database value be interpreted as the bottom end of our current range, + * then use the {@link PooledLoOptimizer} strategy + * + * @author Steve Ebersole + * + * @see PooledLoOptimizer + */ +public class PooledOptimizer extends AbstractOptimizer implements InitialValueAwareOptimizer { + private static final CoreMessageLogger log = Logger.getMessageLogger( + CoreMessageLogger.class, + PooledOptimizer.class.getName() + ); + + private static class GenerationState { + private IntegralDataTypeHolder hiValue; + private IntegralDataTypeHolder value; + } + + private long initialValue = -1; + + /** + * Constructs a PooledOptimizer + * + * @param returnClass The Java type of the values to be generated + * @param incrementSize The increment size. + */ + public PooledOptimizer(Class returnClass, int incrementSize) { + super( returnClass, incrementSize ); + if ( incrementSize < 1 ) { + throw new HibernateException( "increment size cannot be less than 1" ); + } + if ( log.isTraceEnabled() ) { + log.tracev( + "Creating pooled optimizer with [incrementSize={0}; returnClass={1}]", + incrementSize, + returnClass.getName() + ); + } + } + + + @Override + public synchronized Serializable generate(AccessCallback callback) { + final GenerationState generationState = locateGenerationState( callback.getTenantIdentifier() ); + + if ( generationState.hiValue == null ) { + generationState.value = callback.getNextValue(); + // unfortunately not really safe to normalize this + // to 1 as an initial value like we do the others + // because we would not be able to control this if + // we are using a sequence... + if ( generationState.value.lt( 1 ) ) { + log.pooledOptimizerReportedInitialValue( generationState.value ); + } + // the call to obtain next-value just gave us the initialValue + if ( ( initialValue == -1 + && generationState.value.lt( incrementSize ) ) + || generationState.value.eq( initialValue ) ) { + generationState.hiValue = callback.getNextValue(); + } + else { + generationState.hiValue = generationState.value; + generationState.value = generationState.hiValue.copy().subtract( incrementSize ); + } + } + else if ( ! generationState.hiValue.gt( generationState.value ) ) { + generationState.hiValue = callback.getNextValue(); + generationState.value = generationState.hiValue.copy().subtract( incrementSize ); + } + + return generationState.value.makeValueThenIncrement(); + } + + private GenerationState noTenantState; + private Map tenantSpecificState; + + private GenerationState locateGenerationState(String tenantIdentifier) { + if ( tenantIdentifier == null ) { + if ( noTenantState == null ) { + noTenantState = new GenerationState(); + } + return noTenantState; + } + else { + GenerationState state; + if ( tenantSpecificState == null ) { + tenantSpecificState = new ConcurrentHashMap(); + state = new GenerationState(); + tenantSpecificState.put( tenantIdentifier, state ); + } + else { + state = tenantSpecificState.get( tenantIdentifier ); + if ( state == null ) { + state = new GenerationState(); + tenantSpecificState.put( tenantIdentifier, state ); + } + } + return state; + } + } + + private GenerationState noTenantGenerationState() { + if ( noTenantState == null ) { + throw new IllegalStateException( "Could not locate previous generation state for no-tenant" ); + } + return noTenantState; + } + + @Override + public IntegralDataTypeHolder getLastSourceValue() { + return noTenantGenerationState().hiValue; + } + + @Override + public boolean applyIncrementSizeToSourceValues() { + return true; + } + + /** + * Getter for property 'lastValue'. + *

+ * Exposure intended for testing purposes. + * + * @return Value for property 'lastValue'. + */ + public IntegralDataTypeHolder getLastValue() { + return noTenantGenerationState().value.copy().decrement(); + } + + @Override + public void injectInitialValue(long initialValue) { + this.initialValue = initialValue; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/id/enhanced/SequenceStructure.java b/hibernate-core/src/main/java/org/hibernate/id/enhanced/SequenceStructure.java index 23ec0ce368..1b972c4114 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/enhanced/SequenceStructure.java +++ b/hibernate-core/src/main/java/org/hibernate/id/enhanced/SequenceStructure.java @@ -1,10 +1,10 @@ /* * Hibernate, Relational Persistence for Idiomatic Java * - * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as + * Copyright (c) 2008, 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 Middleware LLC. + * 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 @@ -20,7 +20,6 @@ * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA - * */ package org.hibernate.id.enhanced; import java.sql.PreparedStatement; @@ -42,8 +41,10 @@ import org.hibernate.internal.CoreMessageLogger; * @author Steve Ebersole */ public class SequenceStructure implements DatabaseStructure { - - private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, SequenceStructure.class.getName()); + private static final CoreMessageLogger LOG = Logger.getMessageLogger( + CoreMessageLogger.class, + SequenceStructure.class.getName() + ); private final String sequenceName; private final int initialValue; @@ -93,12 +94,12 @@ public class SequenceStructure implements DatabaseStructure { public IntegralDataTypeHolder getNextValue() { accessCounter++; try { - PreparedStatement st = session.getTransactionCoordinator().getJdbcCoordinator().getStatementPreparer().prepareStatement( sql ); + final PreparedStatement st = session.getTransactionCoordinator().getJdbcCoordinator().getStatementPreparer().prepareStatement( sql ); try { - ResultSet rs = session.getTransactionCoordinator().getJdbcCoordinator().getResultSetReturn().extract( st ); + final ResultSet rs = session.getTransactionCoordinator().getJdbcCoordinator().getResultSetReturn().extract( st ); try { rs.next(); - IntegralDataTypeHolder value = IdentifierGeneratorHelper.getIntegralDataTypeHolder( numberType ); + final IntegralDataTypeHolder value = IdentifierGeneratorHelper.getIntegralDataTypeHolder( numberType ); value.initialize( rs, 1 ); if ( LOG.isDebugEnabled() ) { LOG.debugf( "Sequence value obtained: %s", value.makeValue() ); @@ -127,6 +128,11 @@ public class SequenceStructure implements DatabaseStructure { ); } } + + @Override + public String getTenantIdentifier() { + return session.getTenantIdentifier(); + } }; } @@ -137,7 +143,7 @@ public class SequenceStructure implements DatabaseStructure { @Override public String[] sqlCreateStrings(Dialect dialect) throws HibernateException { - int sourceIncrementSize = applyIncrementSizeToSourceValues ? incrementSize : 1; + final int sourceIncrementSize = applyIncrementSizeToSourceValues ? incrementSize : 1; return dialect.getCreateSequenceStrings( sequenceName, initialValue, sourceIncrementSize ); } diff --git a/hibernate-core/src/main/java/org/hibernate/id/enhanced/SequenceStyleGenerator.java b/hibernate-core/src/main/java/org/hibernate/id/enhanced/SequenceStyleGenerator.java index c577e1a923..3d4d2a4284 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/enhanced/SequenceStyleGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/id/enhanced/SequenceStyleGenerator.java @@ -81,8 +81,9 @@ import org.hibernate.type.Type; * depends on defined increment size * Allows explicit definition of which optimization strategy to use * + * * {@link #FORCE_TBL_PARAM} - * false + * false * Allows explicit definition of which optimization strategy to use * * @@ -107,32 +108,85 @@ import org.hibernate.type.Type; public class SequenceStyleGenerator implements PersistentIdentifierGenerator, BulkInsertionCapableIdentifierGenerator, Configurable { - private static final CoreMessageLogger LOG = Logger.getMessageLogger( + private static final CoreMessageLogger LOG = Logger.getMessageLogger( CoreMessageLogger.class, SequenceStyleGenerator.class.getName() ); + // general purpose parameters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + /** + * Indicates the name of the sequence (or table) to use. The default value is {@link #DEF_SEQUENCE_NAME}, + * although {@link #CONFIG_PREFER_SEQUENCE_PER_ENTITY} effects the default as well. + */ public static final String SEQUENCE_PARAM = "sequence_name"; + + /** + * The default value for {@link #SEQUENCE_PARAM}, in the absence of any {@link #CONFIG_PREFER_SEQUENCE_PER_ENTITY} + * setting. + */ public static final String DEF_SEQUENCE_NAME = "hibernate_sequence"; + /** + * Indicates the initial value to use. The default value is {@link #DEFAULT_INITIAL_VALUE} + */ public static final String INITIAL_PARAM = "initial_value"; + + /** + * The default value for {@link #INITIAL_PARAM} + */ public static final int DEFAULT_INITIAL_VALUE = 1; + /** + * Indicates the increment size to use. The default value is {@link #DEFAULT_INCREMENT_SIZE} + */ public static final String INCREMENT_PARAM = "increment_size"; + + /** + * The default value for {@link #INCREMENT_PARAM} + */ public static final int DEFAULT_INCREMENT_SIZE = 1; + /** + * Used to create dedicated sequence for each entity based on the entity name. Sequence suffix can be + * controlled with {@link #CONFIG_SEQUENCE_PER_ENTITY_SUFFIX} option. + */ + public static final String CONFIG_PREFER_SEQUENCE_PER_ENTITY = "prefer_sequence_per_entity"; + + /** + * Indicates the suffix to use in naming the identifier sequence/table name, by appending the suffix to + * the name of the entity. Used in conjunction with {@link #CONFIG_PREFER_SEQUENCE_PER_ENTITY}. + */ + public static final String CONFIG_SEQUENCE_PER_ENTITY_SUFFIX = "sequence_per_entity_suffix"; + + /** + * The default value for {@link #CONFIG_SEQUENCE_PER_ENTITY_SUFFIX} + */ + public static final String DEF_SEQUENCE_SUFFIX = "_SEQ"; + + /** + * Indicates the optimizer to use, either naming a {@link Optimizer} implementation class or naming + * a {@link StandardOptimizerDescriptor} by name + */ public static final String OPT_PARAM = "optimizer"; + /** + * A flag to force using a table as the underlying structure rather than a sequence. + */ public static final String FORCE_TBL_PARAM = "force_table_use"; - public static final String CONFIG_PREFER_SEQUENCE_PER_ENTITY = "prefer_sequence_per_entity"; - public static final String CONFIG_SEQUENCE_PER_ENTITY_SUFFIX = "sequence_per_entity_suffix"; - public static final String DEF_SEQUENCE_SUFFIX = "_SEQ"; - // table-specific parameters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + /** + * Indicates the name of the column holding the identifier values. The default value is {@link #DEF_VALUE_COLUMN} + */ public static final String VALUE_COLUMN_PARAM = "value_column"; + + /** + * The default value for {@link #VALUE_COLUMN_PARAM} + */ public static final String DEF_VALUE_COLUMN = "next_val"; @@ -169,6 +223,7 @@ public class SequenceStyleGenerator } + // Configurable implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @Override @@ -187,7 +242,7 @@ public class SequenceStyleGenerator if ( dialect.supportsSequences() && !forceTableUse ) { if ( !dialect.supportsPooledSequences() && OptimizerFactory.isPooledOptimizer( optimizationStrategy ) ) { forceTableUse = true; - LOG.forcingTableUse(); + LOG.forcingTableUse(); } } @@ -220,27 +275,26 @@ public class SequenceStyleGenerator * @return The sequence name */ protected String determineSequenceName(Properties params, Dialect dialect) { - String sequencePerEntitySuffix = ConfigurationHelper.getString( CONFIG_SEQUENCE_PER_ENTITY_SUFFIX, params, DEF_SEQUENCE_SUFFIX ); + final String sequencePerEntitySuffix = ConfigurationHelper.getString( CONFIG_SEQUENCE_PER_ENTITY_SUFFIX, params, DEF_SEQUENCE_SUFFIX ); // JPA_ENTITY_NAME value honors (HBM) and @Entity#name (JPA) overrides. String sequenceName = ConfigurationHelper.getBoolean( CONFIG_PREFER_SEQUENCE_PER_ENTITY, params, false ) ? params.getProperty( JPA_ENTITY_NAME ) + sequencePerEntitySuffix : DEF_SEQUENCE_NAME; - ObjectNameNormalizer normalizer = ( ObjectNameNormalizer ) params.get( IDENTIFIER_NORMALIZER ); + final ObjectNameNormalizer normalizer = (ObjectNameNormalizer) params.get( IDENTIFIER_NORMALIZER ); sequenceName = ConfigurationHelper.getString( SEQUENCE_PARAM, params, sequenceName ); if ( sequenceName.indexOf( '.' ) < 0 ) { sequenceName = normalizer.normalizeIdentifierQuoting( sequenceName ); - String schemaName = params.getProperty( SCHEMA ); - String catalogName = params.getProperty( CATALOG ); + final String schemaName = params.getProperty( SCHEMA ); + final String catalogName = params.getProperty( CATALOG ); sequenceName = Table.qualify( dialect.quote( catalogName ), dialect.quote( schemaName ), dialect.quote( sequenceName ) ); } - else { - // if already qualified there is not much we can do in a portable manner so we pass it - // through and assume the user has set up the name correctly. - } + // if already qualified there is not much we can do in a portable manner so we pass it + // through and assume the user has set up the name correctly. + return sequenceName; } @@ -256,8 +310,8 @@ public class SequenceStyleGenerator * @return The value column name */ protected String determineValueColumnName(Properties params, Dialect dialect) { - ObjectNameNormalizer normalizer = ( ObjectNameNormalizer ) params.get( IDENTIFIER_NORMALIZER ); - String name = ConfigurationHelper.getString( VALUE_COLUMN_PARAM, params, DEF_VALUE_COLUMN ); + final ObjectNameNormalizer normalizer = (ObjectNameNormalizer) params.get( IDENTIFIER_NORMALIZER ); + final String name = ConfigurationHelper.getString( VALUE_COLUMN_PARAM, params, DEF_VALUE_COLUMN ); return dialect.quote( normalizer.normalizeIdentifierQuoting( name ) ); } @@ -300,11 +354,11 @@ public class SequenceStyleGenerator protected String determineOptimizationStrategy(Properties params, int incrementSize) { // if the increment size is greater than one, we prefer pooled optimization; but we first // need to see if the user prefers POOL or POOL_LO... - String defaultPooledOptimizerStrategy = ConfigurationHelper.getBoolean( Environment.PREFER_POOLED_VALUES_LO, params, false ) - ? OptimizerFactory.StandardOptimizerDescriptor.POOLED_LO.getExternalName() - : OptimizerFactory.StandardOptimizerDescriptor.POOLED.getExternalName(); - String defaultOptimizerStrategy = incrementSize <= 1 - ? OptimizerFactory.StandardOptimizerDescriptor.NONE.getExternalName() + final String defaultPooledOptimizerStrategy = ConfigurationHelper.getBoolean( Environment.PREFER_POOLED_VALUES_LO, params, false ) + ? StandardOptimizerDescriptor.POOLED_LO.getExternalName() + : StandardOptimizerDescriptor.POOLED.getExternalName(); + final String defaultOptimizerStrategy = incrementSize <= 1 + ? StandardOptimizerDescriptor.NONE.getExternalName() : defaultPooledOptimizerStrategy; return ConfigurationHelper.getString( OPT_PARAM, params, defaultOptimizerStrategy ); } @@ -318,10 +372,9 @@ public class SequenceStyleGenerator * @return The adjusted increment size. */ protected int determineAdjustedIncrementSize(String optimizationStrategy, int incrementSize) { - if ( incrementSize > 1 - && OptimizerFactory.StandardOptimizerDescriptor.NONE.getExternalName().equals( optimizationStrategy ) ) { - LOG.honoringOptimizerSetting( - OptimizerFactory.StandardOptimizerDescriptor.NONE.getExternalName(), + if ( incrementSize > 1 && StandardOptimizerDescriptor.NONE.getExternalName().equals( optimizationStrategy ) ) { + LOG.honoringOptimizerSetting( + StandardOptimizerDescriptor.NONE.getExternalName(), INCREMENT_PARAM, incrementSize ); @@ -351,12 +404,12 @@ public class SequenceStyleGenerator String sequenceName, int initialValue, int incrementSize) { - boolean useSequence = dialect.supportsSequences() && !forceTableUse; + final boolean useSequence = dialect.supportsSequences() && !forceTableUse; if ( useSequence ) { return new SequenceStructure( dialect, sequenceName, initialValue, incrementSize, type.getReturnedClass() ); } else { - String valueColumnName = determineValueColumnName( params, dialect ); + final String valueColumnName = determineValueColumnName( params, dialect ); return new TableStructure( dialect, sequenceName, valueColumnName, initialValue, incrementSize, type.getReturnedClass() ); } } @@ -395,7 +448,7 @@ public class SequenceStyleGenerator // it does, as long as // 1) there is no (non-noop) optimizer in use // 2) the underlying structure is a sequence - return OptimizerFactory.NoopOptimizer.class.isInstance( getOptimizer() ) + return NoopOptimizer.class.isInstance( getOptimizer() ) && getDatabaseStructure().isPhysicalSequence(); } diff --git a/hibernate-core/src/main/java/org/hibernate/id/enhanced/StandardOptimizerDescriptor.java b/hibernate-core/src/main/java/org/hibernate/id/enhanced/StandardOptimizerDescriptor.java new file mode 100644 index 0000000000..9f1df83b32 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/id/enhanced/StandardOptimizerDescriptor.java @@ -0,0 +1,121 @@ +/* + * 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.id.enhanced; + +import org.jboss.logging.Logger; + +import org.hibernate.internal.util.StringHelper; + +/** + * Enumeration of the standard Hibernate id generation optimizers. + * + * @author Steve Ebersole + */ +public enum StandardOptimizerDescriptor { + /** + * Describes the optimizer for no optimization + */ + NONE( "none", NoopOptimizer.class ), + /** + * Describes the optimizer for using a custom "hilo" algorithm optimization + */ + HILO( "hilo", HiLoOptimizer.class ), + /** + * Describes the optimizer for using a custom "hilo" algorithm optimization, following the legacy + * Hibernate hilo algorithm + */ + LEGACY_HILO( "legacy-hilo", LegacyHiLoAlgorithmOptimizer.class ), + /** + * Describes the optimizer for use with tables/sequences that store the chunk information. Here, specifically the + * hi value is stored in the database. + */ + POOLED( "pooled", PooledOptimizer.class, true ), + /** + * Describes the optimizer for use with tables/sequences that store the chunk information. Here, specifically the + * lo value is stored in the database. + */ + POOLED_LO( "pooled-lo", PooledLoOptimizer.class, true ); + + private static final Logger log = Logger.getLogger( StandardOptimizerDescriptor.class ); + + private final String externalName; + private final Class optimizerClass; + private final boolean isPooled; + + private StandardOptimizerDescriptor(String externalName, Class optimizerClass) { + this( externalName, optimizerClass, false ); + } + + private StandardOptimizerDescriptor(String externalName, Class optimizerClass, boolean pooled) { + this.externalName = externalName; + this.optimizerClass = optimizerClass; + this.isPooled = pooled; + } + + public String getExternalName() { + return externalName; + } + + public Class getOptimizerClass() { + return optimizerClass; + } + + public boolean isPooled() { + return isPooled; + } + + /** + * Interpret the incoming external name into the appropriate enum value + * + * @param externalName The external name + * + * @return The corresponding enum value; if no external name is supplied, {@link #NONE} is returned; if an + * unrecognized external name is supplied, {@code null} is returned + */ + public static StandardOptimizerDescriptor fromExternalName(String externalName) { + if ( StringHelper.isEmpty( externalName ) ) { + log.debug( "No optimizer specified, using NONE as default" ); + return NONE; + } + else if ( NONE.externalName.equals( externalName ) ) { + return NONE; + } + else if ( HILO.externalName.equals( externalName ) ) { + return HILO; + } + else if ( LEGACY_HILO.externalName.equals( externalName ) ) { + return LEGACY_HILO; + } + else if ( POOLED.externalName.equals( externalName ) ) { + return POOLED; + } + else if ( POOLED_LO.externalName.equals( externalName ) ) { + return POOLED_LO; + } + else { + log.debugf( "Unknown optimizer key [%s]; returning null assuming Optimizer impl class name", externalName ); + return null; + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java b/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java index b52aad778a..51b08a6110 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java @@ -131,32 +131,95 @@ import org.hibernate.type.Type; * @author Steve Ebersole */ public class TableGenerator implements PersistentIdentifierGenerator, Configurable { + private static final CoreMessageLogger LOG = Logger.getMessageLogger( + CoreMessageLogger.class, + TableGenerator.class.getName() + ); - private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, TableGenerator.class.getName()); - + /** + * By default (in the absence of a {@link #SEGMENT_VALUE_PARAM} setting) we use a single row for all + * generators. This setting can be used to change that to instead default to using a row for each entity name. + */ public static final String CONFIG_PREFER_SEGMENT_PER_ENTITY = "prefer_entity_table_as_segment_value"; + /** + * Configures the name of the table to use. The default value is {@link #DEF_TABLE} + */ public static final String TABLE_PARAM = "table_name"; + + /** + * The default {@link #TABLE_PARAM} value + */ public static final String DEF_TABLE = "hibernate_sequences"; + /** + * The name of column which holds the sequence value. The default value is {@link #DEF_VALUE_COLUMN} + */ public static final String VALUE_COLUMN_PARAM = "value_column_name"; + + /** + * The default {@link #VALUE_COLUMN_PARAM} value + */ public static final String DEF_VALUE_COLUMN = "next_val"; + /** + * The name of the column which holds the segment key. The segment defines the different buckets (segments) + * of values currently tracked in the table. The default value is {@link #DEF_SEGMENT_COLUMN} + */ public static final String SEGMENT_COLUMN_PARAM = "segment_column_name"; + + /** + * The default {@link #SEGMENT_COLUMN_PARAM} value + */ public static final String DEF_SEGMENT_COLUMN = "sequence_name"; + /** + * The value indicating which segment is used by this generator, as indicated by the actual value stored in the + * column indicated by {@link #SEGMENT_COLUMN_PARAM}. The default value for setting is {@link #DEF_SEGMENT_VALUE}, + * although {@link #CONFIG_PREFER_SEGMENT_PER_ENTITY} effects the default as well. + */ public static final String SEGMENT_VALUE_PARAM = "segment_value"; + + /** + * The default {@link #SEGMENT_VALUE_PARAM} value, unless {@link #CONFIG_PREFER_SEGMENT_PER_ENTITY} is specified + */ public static final String DEF_SEGMENT_VALUE = "default"; + /** + * Indicates the length of the column defined by {@link #SEGMENT_COLUMN_PARAM}. Used in schema export. The + * default value is {@link #DEF_SEGMENT_LENGTH} + */ public static final String SEGMENT_LENGTH_PARAM = "segment_value_length"; + + /** + * The default {@link #SEGMENT_LENGTH_PARAM} value + */ public static final int DEF_SEGMENT_LENGTH = 255; + /** + * Indicates the initial value to use. The default value is {@link #DEFAULT_INITIAL_VALUE} + */ public static final String INITIAL_PARAM = "initial_value"; + + /** + * The default {@link #INITIAL_PARAM} value + */ public static final int DEFAULT_INITIAL_VALUE = 1; + /** + * Indicates the increment size to use. The default value is {@link #DEFAULT_INCREMENT_SIZE} + */ public static final String INCREMENT_PARAM = "increment_size"; + + /** + * The default {@link #INCREMENT_PARAM} value + */ public static final int DEFAULT_INCREMENT_SIZE = 1; + /** + * Indicates the optimizer to use, either naming a {@link Optimizer} implementation class or by naming + * a {@link StandardOptimizerDescriptor} by name + */ public static final String OPT_PARAM = "optimizer"; @@ -177,7 +240,7 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab private String updateQuery; private Optimizer optimizer; - private long accessCount = 0; + private long accessCount; @Override public Object generatorKey() { @@ -304,11 +367,11 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab // if the increment size is greater than one, we prefer pooled optimization; but we // need to see if the user prefers POOL or POOL_LO... - String defaultPooledOptimizerStrategy = ConfigurationHelper.getBoolean( Environment.PREFER_POOLED_VALUES_LO, params, false ) - ? OptimizerFactory.StandardOptimizerDescriptor.POOLED_LO.getExternalName() - : OptimizerFactory.StandardOptimizerDescriptor.POOLED.getExternalName(); + final String defaultPooledOptimizerStrategy = ConfigurationHelper.getBoolean( Environment.PREFER_POOLED_VALUES_LO, params, false ) + ? StandardOptimizerDescriptor.POOLED_LO.getExternalName() + : StandardOptimizerDescriptor.POOLED.getExternalName(); final String defaultOptimizerStrategy = incrementSize <= 1 - ? OptimizerFactory.StandardOptimizerDescriptor.NONE.getExternalName() + ? StandardOptimizerDescriptor.NONE.getExternalName() : defaultPooledOptimizerStrategy; final String optimizationStrategy = ConfigurationHelper.getString( OPT_PARAM, params, defaultOptimizerStrategy ); optimizer = OptimizerFactory.buildOptimizer( @@ -331,23 +394,22 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab */ protected String determineGeneratorTableName(Properties params, Dialect dialect) { String name = ConfigurationHelper.getString( TABLE_PARAM, params, DEF_TABLE ); - boolean isGivenNameUnqualified = name.indexOf( '.' ) < 0; + final boolean isGivenNameUnqualified = name.indexOf( '.' ) < 0; if ( isGivenNameUnqualified ) { - ObjectNameNormalizer normalizer = ( ObjectNameNormalizer ) params.get( IDENTIFIER_NORMALIZER ); + final ObjectNameNormalizer normalizer = (ObjectNameNormalizer) params.get( IDENTIFIER_NORMALIZER ); name = normalizer.normalizeIdentifierQuoting( name ); // if the given name is un-qualified we may neen to qualify it - String schemaName = normalizer.normalizeIdentifierQuoting( params.getProperty( SCHEMA ) ); - String catalogName = normalizer.normalizeIdentifierQuoting( params.getProperty( CATALOG ) ); + final String schemaName = normalizer.normalizeIdentifierQuoting( params.getProperty( SCHEMA ) ); + final String catalogName = normalizer.normalizeIdentifierQuoting( params.getProperty( CATALOG ) ); name = Table.qualify( dialect.quote( catalogName ), dialect.quote( schemaName ), dialect.quote( name) ); } - else { - // if already qualified there is not much we can do in a portable manner so we pass it - // through and assume the user has set up the name correctly. - } + // if already qualified there is not much we can do in a portable manner so we pass it + // through and assume the user has set up the name correctly. + return name; } @@ -363,8 +425,8 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab * @return The name of the segment column */ protected String determineSegmentColumnName(Properties params, Dialect dialect) { - ObjectNameNormalizer normalizer = ( ObjectNameNormalizer ) params.get( IDENTIFIER_NORMALIZER ); - String name = ConfigurationHelper.getString( SEGMENT_COLUMN_PARAM, params, DEF_SEGMENT_COLUMN ); + final ObjectNameNormalizer normalizer = (ObjectNameNormalizer) params.get( IDENTIFIER_NORMALIZER ); + final String name = ConfigurationHelper.getString( SEGMENT_COLUMN_PARAM, params, DEF_SEGMENT_COLUMN ); return dialect.quote( normalizer.normalizeIdentifierQuoting( name ) ); } @@ -379,8 +441,8 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab * @return The name of the value column */ protected String determineValueColumnName(Properties params, Dialect dialect) { - ObjectNameNormalizer normalizer = ( ObjectNameNormalizer ) params.get( IDENTIFIER_NORMALIZER ); - String name = ConfigurationHelper.getString( VALUE_COLUMN_PARAM, params, DEF_VALUE_COLUMN ); + final ObjectNameNormalizer normalizer = (ObjectNameNormalizer) params.get( IDENTIFIER_NORMALIZER ); + final String name = ConfigurationHelper.getString( VALUE_COLUMN_PARAM, params, DEF_VALUE_COLUMN ); return dialect.quote( normalizer.normalizeIdentifierQuoting( name ) ); } @@ -409,9 +471,9 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab * @return The default segment value to use. */ protected String determineDefaultSegmentValue(Properties params) { - boolean preferSegmentPerEntity = ConfigurationHelper.getBoolean( CONFIG_PREFER_SEGMENT_PER_ENTITY, params, false ); - String defaultToUse = preferSegmentPerEntity ? params.getProperty( TABLE ) : DEF_SEGMENT_VALUE; - LOG.usingDefaultIdGeneratorSegmentValue(tableName, segmentColumnName, defaultToUse); + final boolean preferSegmentPerEntity = ConfigurationHelper.getBoolean( CONFIG_PREFER_SEGMENT_PER_ENTITY, params, false ); + final String defaultToUse = preferSegmentPerEntity ? params.getProperty( TABLE ) : DEF_SEGMENT_VALUE; + LOG.usingDefaultIdGeneratorSegmentValue( tableName, segmentColumnName, defaultToUse ); return defaultToUse; } @@ -438,12 +500,12 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab protected String buildSelectQuery(Dialect dialect) { final String alias = "tbl"; - String query = "select " + StringHelper.qualify( alias, valueColumnName ) + + final String query = "select " + StringHelper.qualify( alias, valueColumnName ) + " from " + tableName + ' ' + alias + " where " + StringHelper.qualify( alias, segmentColumnName ) + "=?"; - LockOptions lockOptions = new LockOptions( LockMode.PESSIMISTIC_WRITE ); + final LockOptions lockOptions = new LockOptions( LockMode.PESSIMISTIC_WRITE ); lockOptions.setAliasSpecificLockMode( alias, LockMode.PESSIMISTIC_WRITE ); - Map updateTargetColumnsMap = Collections.singletonMap( alias, new String[] { valueColumnName } ); + final Map updateTargetColumnsMap = Collections.singletonMap( alias, new String[] { valueColumnName } ); return dialect.applyLocksToSql( query, lockOptions, updateTargetColumnsMap ); } @@ -472,19 +534,27 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab new AbstractReturningWork() { @Override public IntegralDataTypeHolder execute(Connection connection) throws SQLException { - IntegralDataTypeHolder value = IdentifierGeneratorHelper.getIntegralDataTypeHolder( identifierType.getReturnedClass() ); + final IntegralDataTypeHolder value = IdentifierGeneratorHelper.getIntegralDataTypeHolder( + identifierType.getReturnedClass() + ); int rows; do { - statementLogger.logStatement( selectQuery, FormatStyle.BASIC.getFormatter() ); + statementLogger.logStatement( + selectQuery, + FormatStyle.BASIC.getFormatter() + ); PreparedStatement selectPS = connection.prepareStatement( selectQuery ); try { selectPS.setString( 1, segmentValue ); - ResultSet selectRS = selectPS.executeQuery(); + final ResultSet selectRS = selectPS.executeQuery(); if ( !selectRS.next() ) { value.initialize( initialValue ); PreparedStatement insertPS = null; try { - statementLogger.logStatement( insertQuery, FormatStyle.BASIC.getFormatter() ); + statementLogger.logStatement( + insertQuery, + FormatStyle.BASIC.getFormatter() + ); insertPS = connection.prepareStatement( insertQuery ); insertPS.setString( 1, segmentValue ); value.bind( insertPS, 2 ); @@ -501,16 +571,19 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab } selectRS.close(); } - catch ( SQLException e ) { - LOG.unableToReadOrInitHiValue(e); + catch (SQLException e) { + LOG.unableToReadOrInitHiValue( e ); throw e; } finally { selectPS.close(); } - statementLogger.logStatement( updateQuery, FormatStyle.BASIC.getFormatter() ); - PreparedStatement updatePS = connection.prepareStatement( updateQuery ); + statementLogger.logStatement( + updateQuery, + FormatStyle.BASIC.getFormatter() + ); + final PreparedStatement updatePS = connection.prepareStatement( updateQuery ); try { final IntegralDataTypeHolder updateValue = value.copy(); if ( optimizer.applyIncrementSizeToSourceValues() ) { @@ -524,8 +597,8 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab updatePS.setString( 3, segmentValue ); rows = updatePS.executeUpdate(); } - catch ( SQLException e ) { - LOG.unableToUpdateQueryHiValue(tableName, e); + catch (SQLException e) { + LOG.unableToUpdateQueryHiValue( tableName, e ); throw e; } finally { @@ -542,6 +615,11 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab true ); } + + @Override + public String getTenantIdentifier() { + return session.getTenantIdentifier(); + } } ); } @@ -549,23 +627,10 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab @Override public String[] sqlCreateStrings(Dialect dialect) throws HibernateException { return new String[] { - new StringBuilder() - .append( dialect.getCreateTableString() ) - .append( ' ' ) - .append( tableName ) - .append( " ( " ) - .append( segmentColumnName ) - .append( ' ' ) - .append( dialect.getTypeName( Types.VARCHAR, segmentValueLength, 0, 0 ) ) - .append( " not null " ) - .append( ", " ) - .append( valueColumnName ) - .append( ' ' ) - .append( dialect.getTypeName( Types.BIGINT ) ) - .append( ", primary key ( " ) - .append( segmentColumnName ) - .append( " ) ) " ) - .toString() + dialect.getCreateTableString() + ' ' + tableName + " ( " + + segmentColumnName + ' ' + dialect.getTypeName( Types.VARCHAR, segmentValueLength, 0, 0 ) + " not null " + + ", " + valueColumnName + ' ' + dialect.getTypeName( Types.BIGINT ) + + ", primary key ( " + segmentColumnName + " ) ) " }; } diff --git a/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableStructure.java b/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableStructure.java index dd99e25c10..c032b58a3a 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableStructure.java +++ b/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableStructure.java @@ -50,8 +50,10 @@ import org.hibernate.jdbc.AbstractReturningWork; * @author Steve Ebersole */ public class TableStructure implements DatabaseStructure { - - private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, TableStructure.class.getName()); + private static final CoreMessageLogger LOG = Logger.getMessageLogger( + CoreMessageLogger.class, + TableStructure.class.getName() + ); private final String tableName; private final String valueColumnName; @@ -125,22 +127,24 @@ public class TableStructure implements DatabaseStructure { .getServiceRegistry() .getService( JdbcServices.class ) .getSqlStatementLogger(); - IntegralDataTypeHolder value = IdentifierGeneratorHelper.getIntegralDataTypeHolder( numberType ); + final IntegralDataTypeHolder value = IdentifierGeneratorHelper.getIntegralDataTypeHolder( + numberType + ); int rows; do { statementLogger.logStatement( selectQuery, FormatStyle.BASIC.getFormatter() ); PreparedStatement selectStatement = connection.prepareStatement( selectQuery ); try { - ResultSet selectRS = selectStatement.executeQuery(); + final ResultSet selectRS = selectStatement.executeQuery(); if ( !selectRS.next() ) { - String err = "could not read a hi value - you need to populate the table: " + tableName; + final String err = "could not read a hi value - you need to populate the table: " + tableName; LOG.error( err ); throw new IdentifierGenerationException( err ); } value.initialize( selectRS, 1 ); selectRS.close(); } - catch ( SQLException sqle ) { + catch (SQLException sqle) { LOG.error( "could not read a hi value", sqle ); throw sqle; } @@ -149,7 +153,7 @@ public class TableStructure implements DatabaseStructure { } statementLogger.logStatement( updateQuery, FormatStyle.BASIC.getFormatter() ); - PreparedStatement updatePS = connection.prepareStatement( updateQuery ); + final PreparedStatement updatePS = connection.prepareStatement( updateQuery ); try { final int increment = applyIncrementSizeToSourceValues ? incrementSize : 1; final IntegralDataTypeHolder updateValue = value.copy().add( increment ); @@ -157,8 +161,8 @@ public class TableStructure implements DatabaseStructure { value.bind( updatePS, 2 ); rows = updatePS.executeUpdate(); } - catch ( SQLException e ) { - LOG.unableToUpdateQueryHiValue(tableName, e); + catch (SQLException e) { + LOG.unableToUpdateQueryHiValue( tableName, e ); throw e; } finally { @@ -174,6 +178,11 @@ public class TableStructure implements DatabaseStructure { true ); } + + @Override + public String getTenantIdentifier() { + return session.getTenantIdentifier(); + } }; } diff --git a/hibernate-core/src/main/java/org/hibernate/id/enhanced/package-info.java b/hibernate-core/src/main/java/org/hibernate/id/enhanced/package-info.java new file mode 100644 index 0000000000..498315bdf4 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/id/enhanced/package-info.java @@ -0,0 +1,5 @@ +/** + * Enhanced/improved versions of table and sequence based identifier generators targeting portability and unified + * configuration + */ +package org.hibernate.id.enhanced; diff --git a/hibernate-core/src/test/java/org/hibernate/id/enhanced/OptimizerUnitTest.java b/hibernate-core/src/test/java/org/hibernate/id/enhanced/OptimizerUnitTest.java index 6d6e1c5be5..b680f461f0 100644 --- a/hibernate-core/src/test/java/org/hibernate/id/enhanced/OptimizerUnitTest.java +++ b/hibernate-core/src/test/java/org/hibernate/id/enhanced/OptimizerUnitTest.java @@ -255,23 +255,23 @@ public class OptimizerUnitTest extends BaseUnitTestCase { } private static Optimizer buildNoneOptimizer(long initial, int increment) { - return buildOptimizer( OptimizerFactory.StandardOptimizerDescriptor.NONE, initial, increment ); + return buildOptimizer( StandardOptimizerDescriptor.NONE, initial, increment ); } private static Optimizer buildHiloOptimizer(long initial, int increment) { - return buildOptimizer( OptimizerFactory.StandardOptimizerDescriptor.HILO, initial, increment ); + return buildOptimizer( StandardOptimizerDescriptor.HILO, initial, increment ); } private static Optimizer buildPooledOptimizer(long initial, int increment) { - return buildOptimizer( OptimizerFactory.StandardOptimizerDescriptor.POOLED, initial, increment ); + return buildOptimizer( StandardOptimizerDescriptor.POOLED, initial, increment ); } private static Optimizer buildPooledLoOptimizer(long initial, int increment) { - return buildOptimizer( OptimizerFactory.StandardOptimizerDescriptor.POOLED_LO, initial, increment ); + return buildOptimizer( StandardOptimizerDescriptor.POOLED_LO, initial, increment ); } private static Optimizer buildOptimizer( - OptimizerFactory.StandardOptimizerDescriptor descriptor, + StandardOptimizerDescriptor descriptor, long initial, int increment) { return OptimizerFactory.buildOptimizer( descriptor.getExternalName(), Long.class, increment, initial ); @@ -319,6 +319,11 @@ public class OptimizerUnitTest extends BaseUnitTestCase { } } + @Override + public String getTenantIdentifier() { + return null; + } + private void initValue() { this.value.initialize( initialValue ); } diff --git a/hibernate-core/src/test/java/org/hibernate/id/enhanced/SequenceStyleConfigUnitTest.java b/hibernate-core/src/test/java/org/hibernate/id/enhanced/SequenceStyleConfigUnitTest.java index 6d2b508b7c..1c2196b4a0 100644 --- a/hibernate-core/src/test/java/org/hibernate/id/enhanced/SequenceStyleConfigUnitTest.java +++ b/hibernate-core/src/test/java/org/hibernate/id/enhanced/SequenceStyleConfigUnitTest.java @@ -63,7 +63,7 @@ public class SequenceStyleConfigUnitTest extends BaseUnitTestCase { generator.configure( StandardBasicTypes.LONG, props, dialect ); assertClassAssignability( SequenceStructure.class, generator.getDatabaseStructure().getClass() ); - assertClassAssignability( OptimizerFactory.NoopOptimizer.class, generator.getOptimizer().getClass() ); + assertClassAssignability( NoopOptimizer.class, generator.getOptimizer().getClass() ); assertEquals( SequenceStyleGenerator.DEF_SEQUENCE_NAME, generator.getDatabaseStructure().getName() ); } @@ -95,7 +95,7 @@ public class SequenceStyleConfigUnitTest extends BaseUnitTestCase { generator.configure( StandardBasicTypes.LONG, props, dialect ); assertClassAssignability( TableStructure.class, generator.getDatabaseStructure().getClass() ); - assertClassAssignability( OptimizerFactory.NoopOptimizer.class, generator.getOptimizer().getClass() ); + assertClassAssignability( NoopOptimizer.class, generator.getOptimizer().getClass() ); assertEquals( SequenceStyleGenerator.DEF_SEQUENCE_NAME, generator.getDatabaseStructure().getName() ); } @@ -114,7 +114,7 @@ public class SequenceStyleConfigUnitTest extends BaseUnitTestCase { SequenceStyleGenerator generator = new SequenceStyleGenerator(); generator.configure( StandardBasicTypes.LONG, props, dialect ); assertClassAssignability( TableStructure.class, generator.getDatabaseStructure().getClass() ); - assertClassAssignability( OptimizerFactory.PooledOptimizer.class, generator.getOptimizer().getClass() ); + assertClassAssignability( PooledOptimizer.class, generator.getOptimizer().getClass() ); assertEquals( SequenceStyleGenerator.DEF_SEQUENCE_NAME, generator.getDatabaseStructure().getName() ); // for dialects which do support pooled sequences, we default to pooled+sequence @@ -122,7 +122,7 @@ public class SequenceStyleConfigUnitTest extends BaseUnitTestCase { generator = new SequenceStyleGenerator(); generator.configure( StandardBasicTypes.LONG, props, dialect ); assertClassAssignability( SequenceStructure.class, generator.getDatabaseStructure().getClass() ); - assertClassAssignability( OptimizerFactory.PooledOptimizer.class, generator.getOptimizer().getClass() ); + assertClassAssignability( PooledOptimizer.class, generator.getOptimizer().getClass() ); assertEquals( SequenceStyleGenerator.DEF_SEQUENCE_NAME, generator.getDatabaseStructure().getName() ); } @@ -139,7 +139,7 @@ public class SequenceStyleConfigUnitTest extends BaseUnitTestCase { SequenceStyleGenerator generator = new SequenceStyleGenerator(); generator.configure( StandardBasicTypes.LONG, props, dialect ); assertClassAssignability( TableStructure.class, generator.getDatabaseStructure().getClass() ); - assertClassAssignability( OptimizerFactory.PooledOptimizer.class, generator.getOptimizer().getClass() ); + assertClassAssignability( PooledOptimizer.class, generator.getOptimizer().getClass() ); assertEquals( SequenceStyleGenerator.DEF_SEQUENCE_NAME, generator.getDatabaseStructure().getName() ); } @@ -154,7 +154,7 @@ public class SequenceStyleConfigUnitTest extends BaseUnitTestCase { SequenceStyleGenerator generator = new SequenceStyleGenerator(); generator.configure( StandardBasicTypes.LONG, props, dialect ); assertClassAssignability( TableStructure.class, generator.getDatabaseStructure().getClass() ); - assertClassAssignability( OptimizerFactory.NoopOptimizer.class, generator.getOptimizer().getClass() ); + assertClassAssignability( NoopOptimizer.class, generator.getOptimizer().getClass() ); assertEquals( SequenceStyleGenerator.DEF_SEQUENCE_NAME, generator.getDatabaseStructure().getName() ); } @@ -168,36 +168,36 @@ public class SequenceStyleConfigUnitTest extends BaseUnitTestCase { // optimizer=none w/ increment > 1 => should honor optimizer Properties props = buildGeneratorPropertiesBase(); - props.setProperty( SequenceStyleGenerator.OPT_PARAM, OptimizerFactory.StandardOptimizerDescriptor.NONE.getExternalName() ); + props.setProperty( SequenceStyleGenerator.OPT_PARAM, StandardOptimizerDescriptor.NONE.getExternalName() ); props.setProperty( SequenceStyleGenerator.INCREMENT_PARAM, "20" ); SequenceStyleGenerator generator = new SequenceStyleGenerator(); generator.configure( StandardBasicTypes.LONG, props, dialect ); assertClassAssignability( SequenceStructure.class, generator.getDatabaseStructure().getClass() ); - assertClassAssignability( OptimizerFactory.NoopOptimizer.class, generator.getOptimizer().getClass() ); + assertClassAssignability( NoopOptimizer.class, generator.getOptimizer().getClass() ); assertEquals( 1, generator.getOptimizer().getIncrementSize() ); assertEquals( 1, generator.getDatabaseStructure().getIncrementSize() ); // optimizer=hilo w/ increment > 1 => hilo props = buildGeneratorPropertiesBase(); - props.setProperty( SequenceStyleGenerator.OPT_PARAM, OptimizerFactory.StandardOptimizerDescriptor.HILO.getExternalName() ); + props.setProperty( SequenceStyleGenerator.OPT_PARAM, StandardOptimizerDescriptor.HILO.getExternalName() ); props.setProperty( SequenceStyleGenerator.INCREMENT_PARAM, "20" ); generator = new SequenceStyleGenerator(); generator.configure( StandardBasicTypes.LONG, props, dialect ); assertClassAssignability( SequenceStructure.class, generator.getDatabaseStructure().getClass() ); - assertClassAssignability( OptimizerFactory.HiLoOptimizer.class, generator.getOptimizer().getClass() ); + assertClassAssignability( HiLoOptimizer.class, generator.getOptimizer().getClass() ); assertEquals( 20, generator.getOptimizer().getIncrementSize() ); assertEquals( 20, generator.getDatabaseStructure().getIncrementSize() ); // optimizer=pooled w/ increment > 1 => hilo props = buildGeneratorPropertiesBase(); - props.setProperty( SequenceStyleGenerator.OPT_PARAM, OptimizerFactory.StandardOptimizerDescriptor.POOLED.getExternalName() ); + props.setProperty( SequenceStyleGenerator.OPT_PARAM, StandardOptimizerDescriptor.POOLED.getExternalName() ); props.setProperty( SequenceStyleGenerator.INCREMENT_PARAM, "20" ); generator = new SequenceStyleGenerator(); generator.configure( StandardBasicTypes.LONG, props, dialect ); // because the dialect reports to not support pooled seqyences, the expectation is that we will // use a table for the backing structure... assertClassAssignability( TableStructure.class, generator.getDatabaseStructure().getClass() ); - assertClassAssignability( OptimizerFactory.PooledOptimizer.class, generator.getOptimizer().getClass() ); + assertClassAssignability( PooledOptimizer.class, generator.getOptimizer().getClass() ); assertEquals( 20, generator.getOptimizer().getIncrementSize() ); assertEquals( 20, generator.getDatabaseStructure().getIncrementSize() ); } @@ -211,13 +211,13 @@ public class SequenceStyleConfigUnitTest extends BaseUnitTestCase { SequenceStyleGenerator generator = new SequenceStyleGenerator(); generator.configure( StandardBasicTypes.LONG, props, dialect ); assertClassAssignability( SequenceStructure.class, generator.getDatabaseStructure().getClass() ); - assertClassAssignability( OptimizerFactory.PooledOptimizer.class, generator.getOptimizer().getClass() ); + assertClassAssignability( PooledOptimizer.class, generator.getOptimizer().getClass() ); props.setProperty( Environment.PREFER_POOLED_VALUES_LO, "true" ); generator = new SequenceStyleGenerator(); generator.configure( StandardBasicTypes.LONG, props, dialect ); assertClassAssignability( SequenceStructure.class, generator.getDatabaseStructure().getClass() ); - assertClassAssignability( OptimizerFactory.PooledLoOptimizer.class, generator.getOptimizer().getClass() ); + assertClassAssignability( PooledLoOptimizer.class, generator.getOptimizer().getClass() ); } private static class TableDialect extends Dialect { diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/id/generationmappings/NewGeneratorMappingsTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/id/generationmappings/NewGeneratorMappingsTest.java index 10c4fb7848..bdde1562c0 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/id/generationmappings/NewGeneratorMappingsTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/id/generationmappings/NewGeneratorMappingsTest.java @@ -28,7 +28,8 @@ import org.junit.Test; import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Environment; import org.hibernate.id.IdentifierGenerator; -import org.hibernate.id.enhanced.OptimizerFactory; +import org.hibernate.id.enhanced.NoopOptimizer; +import org.hibernate.id.enhanced.PooledOptimizer; import org.hibernate.id.enhanced.SequenceStyleGenerator; import org.hibernate.id.enhanced.TableGenerator; import org.hibernate.internal.util.StringHelper; @@ -87,7 +88,7 @@ public class NewGeneratorMappingsTest extends BaseCoreFunctionalTestCase { assertEquals( 1, seqGenerator.getDatabaseStructure().getInitialValue() ); // 50 is the annotation default assertEquals( 50, seqGenerator.getDatabaseStructure().getIncrementSize() ); - assertFalse( OptimizerFactory.NoopOptimizer.class.isInstance( seqGenerator.getOptimizer() ) ); + assertFalse( NoopOptimizer.class.isInstance( seqGenerator.getOptimizer() ) ); } @Test @@ -99,7 +100,7 @@ public class NewGeneratorMappingsTest extends BaseCoreFunctionalTestCase { assertEquals( "my_catalog.my_schema."+CompleteSequenceEntity.SEQ_NAME, seqGenerator.getDatabaseStructure().getName() ); assertEquals( 1000, seqGenerator.getDatabaseStructure().getInitialValue() ); assertEquals( 52, seqGenerator.getDatabaseStructure().getIncrementSize() ); - assertFalse( OptimizerFactory.NoopOptimizer.class.isInstance( seqGenerator.getOptimizer() ) ); + assertFalse( NoopOptimizer.class.isInstance( seqGenerator.getOptimizer() ) ); } @Test @@ -127,7 +128,7 @@ public class NewGeneratorMappingsTest extends BaseCoreFunctionalTestCase { assertEquals( 1, tabGenerator.getInitialValue() ); // 50 is the annotation default assertEquals( 50, tabGenerator.getIncrementSize() ); - assertTrue( OptimizerFactory.PooledOptimizer.class.isInstance( tabGenerator.getOptimizer() ) ); + assertTrue( PooledOptimizer.class.isInstance( tabGenerator.getOptimizer() ) ); } @Test diff --git a/hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/BasicForcedTableSequenceTest.java b/hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/BasicForcedTableSequenceTest.java index fc949a72fd..4acaf7dfde 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/BasicForcedTableSequenceTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/BasicForcedTableSequenceTest.java @@ -27,7 +27,7 @@ import org.junit.Test; import org.hibernate.Session; import org.hibernate.id.IdentifierGeneratorHelper.BasicHolder; -import org.hibernate.id.enhanced.OptimizerFactory; +import org.hibernate.id.enhanced.NoopOptimizer; import org.hibernate.id.enhanced.SequenceStyleGenerator; import org.hibernate.id.enhanced.TableStructure; import org.hibernate.persister.entity.EntityPersister; @@ -59,7 +59,7 @@ public class BasicForcedTableSequenceTest extends BaseCoreFunctionalTestCase { ); assertTrue( "no-op optimizer was not used", - OptimizerFactory.NoopOptimizer.class.isInstance( generator.getOptimizer() ) + NoopOptimizer.class.isInstance( generator.getOptimizer() ) ); int count = 5; diff --git a/hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/HiLoForcedTableSequenceTest.java b/hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/HiLoForcedTableSequenceTest.java index 4eecf262a5..b7d1922766 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/HiLoForcedTableSequenceTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/HiLoForcedTableSequenceTest.java @@ -27,7 +27,7 @@ import org.junit.Test; import org.hibernate.Session; import org.hibernate.id.IdentifierGeneratorHelper.BasicHolder; -import org.hibernate.id.enhanced.OptimizerFactory; +import org.hibernate.id.enhanced.HiLoOptimizer; import org.hibernate.id.enhanced.SequenceStyleGenerator; import org.hibernate.id.enhanced.TableStructure; import org.hibernate.persister.entity.EntityPersister; @@ -60,9 +60,9 @@ public class HiLoForcedTableSequenceTest extends BaseCoreFunctionalTestCase { ); assertTrue( "hilo optimizer was not used", - OptimizerFactory.HiLoOptimizer.class.isInstance( generator.getOptimizer() ) + HiLoOptimizer.class.isInstance( generator.getOptimizer() ) ); - OptimizerFactory.HiLoOptimizer optimizer = ( OptimizerFactory.HiLoOptimizer ) generator.getOptimizer(); + HiLoOptimizer optimizer = (HiLoOptimizer) generator.getOptimizer(); int increment = optimizer.getIncrementSize(); Entity[] entities = new Entity[ increment + 1 ]; diff --git a/hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/PooledForcedTableSequenceTest.java b/hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/PooledForcedTableSequenceTest.java index 5e808b792d..d1851f0b6d 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/PooledForcedTableSequenceTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/PooledForcedTableSequenceTest.java @@ -27,7 +27,7 @@ import org.junit.Test; import org.hibernate.Session; import org.hibernate.id.IdentifierGeneratorHelper.BasicHolder; -import org.hibernate.id.enhanced.OptimizerFactory; +import org.hibernate.id.enhanced.PooledOptimizer; import org.hibernate.id.enhanced.SequenceStyleGenerator; import org.hibernate.id.enhanced.TableStructure; import org.hibernate.persister.entity.EntityPersister; @@ -58,9 +58,9 @@ public class PooledForcedTableSequenceTest extends BaseCoreFunctionalTestCase { ); assertTrue( "pooled optimizer was not used", - OptimizerFactory.PooledOptimizer.class.isInstance( generator.getOptimizer() ) + PooledOptimizer.class.isInstance( generator.getOptimizer() ) ); - OptimizerFactory.PooledOptimizer optimizer = ( OptimizerFactory.PooledOptimizer ) generator.getOptimizer(); + PooledOptimizer optimizer = (PooledOptimizer) generator.getOptimizer(); int increment = optimizer.getIncrementSize(); Entity[] entities = new Entity[ increment + 1 ]; diff --git a/hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/sequence/HiLoSequenceTest.java b/hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/sequence/HiLoSequenceTest.java index 888b56bdf6..d550836d76 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/sequence/HiLoSequenceTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/sequence/HiLoSequenceTest.java @@ -28,7 +28,7 @@ import org.junit.Test; import org.hibernate.Session; import org.hibernate.id.IdentifierGeneratorHelper.BasicHolder; -import org.hibernate.id.enhanced.OptimizerFactory; +import org.hibernate.id.enhanced.HiLoOptimizer; import org.hibernate.id.enhanced.SequenceStyleGenerator; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; @@ -49,8 +49,8 @@ public class HiLoSequenceTest extends BaseCoreFunctionalTestCase { EntityPersister persister = sessionFactory().getEntityPersister( Entity.class.getName() ); assertClassAssignability( SequenceStyleGenerator.class, persister.getIdentifierGenerator().getClass() ); SequenceStyleGenerator generator = ( SequenceStyleGenerator ) persister.getIdentifierGenerator(); - assertClassAssignability( OptimizerFactory.HiLoOptimizer.class, generator.getOptimizer().getClass() ); - OptimizerFactory.HiLoOptimizer optimizer = ( OptimizerFactory.HiLoOptimizer ) generator.getOptimizer(); + assertClassAssignability( HiLoOptimizer.class, generator.getOptimizer().getClass() ); + HiLoOptimizer optimizer = (HiLoOptimizer) generator.getOptimizer(); int increment = optimizer.getIncrementSize(); Entity[] entities = new Entity[ increment + 1 ]; diff --git a/hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/sequence/PooledSequenceTest.java b/hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/sequence/PooledSequenceTest.java index 9216f8952c..9c755f6a2b 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/sequence/PooledSequenceTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/sequence/PooledSequenceTest.java @@ -26,7 +26,7 @@ package org.hibernate.test.idgen.enhanced.sequence; import org.junit.Test; import org.hibernate.Session; -import org.hibernate.id.enhanced.OptimizerFactory; +import org.hibernate.id.enhanced.PooledOptimizer; import org.hibernate.id.enhanced.SequenceStyleGenerator; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; @@ -49,8 +49,8 @@ public class PooledSequenceTest extends BaseCoreFunctionalTestCase { EntityPersister persister = sessionFactory().getEntityPersister( Entity.class.getName() ); assertClassAssignability( SequenceStyleGenerator.class, persister.getIdentifierGenerator().getClass() ); SequenceStyleGenerator generator = ( SequenceStyleGenerator ) persister.getIdentifierGenerator(); - assertClassAssignability( OptimizerFactory.PooledOptimizer.class, generator.getOptimizer().getClass() ); - OptimizerFactory.PooledOptimizer optimizer = ( OptimizerFactory.PooledOptimizer ) generator.getOptimizer(); + assertClassAssignability( PooledOptimizer.class, generator.getOptimizer().getClass() ); + PooledOptimizer optimizer = (PooledOptimizer) generator.getOptimizer(); int increment = optimizer.getIncrementSize(); Entity[] entities = new Entity[ increment + 1 ]; diff --git a/hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/table/HiLoTableTest.java b/hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/table/HiLoTableTest.java index a540971d50..c024afabb0 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/table/HiLoTableTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/table/HiLoTableTest.java @@ -26,7 +26,7 @@ package org.hibernate.test.idgen.enhanced.table; import org.junit.Test; import org.hibernate.Session; -import org.hibernate.id.enhanced.OptimizerFactory; +import org.hibernate.id.enhanced.HiLoOptimizer; import org.hibernate.id.enhanced.TableGenerator; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; @@ -49,8 +49,8 @@ public class HiLoTableTest extends BaseCoreFunctionalTestCase { EntityPersister persister = sessionFactory().getEntityPersister( Entity.class.getName() ); assertClassAssignability( TableGenerator.class, persister.getIdentifierGenerator().getClass() ); TableGenerator generator = ( TableGenerator ) persister.getIdentifierGenerator(); - assertClassAssignability( OptimizerFactory.HiLoOptimizer.class, generator.getOptimizer().getClass() ); - OptimizerFactory.HiLoOptimizer optimizer = ( OptimizerFactory.HiLoOptimizer ) generator.getOptimizer(); + assertClassAssignability( HiLoOptimizer.class, generator.getOptimizer().getClass() ); + HiLoOptimizer optimizer = (HiLoOptimizer) generator.getOptimizer(); int increment = optimizer.getIncrementSize(); Entity[] entities = new Entity[ increment + 1 ]; diff --git a/hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/table/PooledTableTest.java b/hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/table/PooledTableTest.java index adf2d84a82..4df54a39bd 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/table/PooledTableTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/table/PooledTableTest.java @@ -26,7 +26,7 @@ package org.hibernate.test.idgen.enhanced.table; import org.junit.Test; import org.hibernate.Session; -import org.hibernate.id.enhanced.OptimizerFactory; +import org.hibernate.id.enhanced.PooledOptimizer; import org.hibernate.id.enhanced.TableGenerator; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; @@ -49,8 +49,8 @@ public class PooledTableTest extends BaseCoreFunctionalTestCase { EntityPersister persister = sessionFactory().getEntityPersister( Entity.class.getName() ); assertClassAssignability( TableGenerator.class, persister.getIdentifierGenerator().getClass() ); TableGenerator generator = ( TableGenerator ) persister.getIdentifierGenerator(); - assertClassAssignability( OptimizerFactory.PooledOptimizer.class, generator.getOptimizer().getClass() ); - OptimizerFactory.PooledOptimizer optimizer = ( OptimizerFactory.PooledOptimizer ) generator.getOptimizer(); + assertClassAssignability( PooledOptimizer.class, generator.getOptimizer().getClass() ); + PooledOptimizer optimizer = (PooledOptimizer) generator.getOptimizer(); int increment = optimizer.getIncrementSize(); Entity[] entities = new Entity[ increment + 1 ]; diff --git a/hibernate-core/src/test/java/org/hibernate/test/multitenancy/schema/Invoice.java b/hibernate-core/src/test/java/org/hibernate/test/multitenancy/schema/Invoice.java new file mode 100644 index 0000000000..2855edd527 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/multitenancy/schema/Invoice.java @@ -0,0 +1,56 @@ +/* + * 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.test.multitenancy.schema; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +import org.hibernate.annotations.GenericGenerator; +import org.hibernate.annotations.Parameter; +import org.hibernate.id.enhanced.TableGenerator; + +/** + * @author Steve Ebersole + */ +@Entity +public class Invoice { + @Id + @GeneratedValue(strategy = GenerationType.TABLE, generator = "number_sequence") + @GenericGenerator( + name = "number_sequence", + strategy = "org.hibernate.id.enhanced.TableGenerator", + parameters = { + @Parameter(name = TableGenerator.SEGMENT_VALUE_PARAM, value = "customer"), + @Parameter(name = TableGenerator.INCREMENT_PARAM, value = "5"), + @Parameter(name = TableGenerator.OPT_PARAM, value = "pooled") + } + ) + private Long id; + + public Long getId() { + return id; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/multitenancy/schema/SchemaBasedMultiTenancyTest.java b/hibernate-core/src/test/java/org/hibernate/test/multitenancy/schema/SchemaBasedMultiTenancyTest.java index 86bde2f2e1..969083faaa 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/multitenancy/schema/SchemaBasedMultiTenancyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/multitenancy/schema/SchemaBasedMultiTenancyTest.java @@ -80,6 +80,7 @@ public class SchemaBasedMultiTenancyTest extends BaseUnitTestCase { cfg.setProperty( Environment.CACHE_REGION_FACTORY, CachingRegionFactory.class.getName() ); cfg.setProperty( Environment.GENERATE_STATISTICS, "true" ); cfg.addAnnotatedClass( Customer.class ); + cfg.addAnnotatedClass( Invoice.class ); cfg.buildMappings(); RootClass meta = (RootClass) cfg.getClassMapping( Customer.class.getName() ); @@ -293,6 +294,39 @@ public class SchemaBasedMultiTenancyTest extends BaseUnitTestCase { session.close(); } + @Test + public void testTableIdentifiers() { + Session session = getNewSession( "jboss" ); + session.beginTransaction(); + Invoice orderJboss = new Invoice(); + session.save( orderJboss ); + Assert.assertEquals( Long.valueOf( 1 ), orderJboss.getId() ); + session.getTransaction().commit(); + session.close(); + + session = getNewSession( "acme" ); + session.beginTransaction(); + Invoice orderAcme = new Invoice(); + session.save( orderAcme ); + Assert.assertEquals( Long.valueOf( 1 ), orderAcme.getId() ); + session.getTransaction().commit(); + session.close(); + + session = getNewSession( "jboss" ); + session.beginTransaction(); + session.delete( orderJboss ); + session.getTransaction().commit(); + session.close(); + + session = getNewSession( "acme" ); + session.beginTransaction(); + session.delete( orderAcme ); + session.getTransaction().commit(); + session.close(); + + sessionFactory.getStatisticsImplementor().clear(); + } + protected Session getNewSession(String tenant) { return sessionFactory.withOptions().tenantIdentifier( tenant ).openSession(); } From ce77f159740b325c361e37b1cc8c62f5a78cd22d Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Mon, 20 May 2013 17:36:30 -0400 Subject: [PATCH 02/36] HHH-2872 reverting an incorrect test case --- .../annotations/join/JoinOrderingTest.java | 47 ---------------- .../test/annotations/join/Product.java | 53 ------------------- .../test/annotations/join/ProductDetails.java | 43 --------------- .../test/annotations/join/ProductVersion.java | 45 ---------------- 4 files changed, 188 deletions(-) delete mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/join/JoinOrderingTest.java delete mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/join/Product.java delete mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/join/ProductDetails.java delete mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/join/ProductVersion.java diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/join/JoinOrderingTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/join/JoinOrderingTest.java deleted file mode 100644 index 548bcb9eb7..0000000000 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/join/JoinOrderingTest.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * JBoss, Home of Professional Open Source - * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors - * as indicated by the @authors tag. All rights reserved. - * See the copyright.txt in the distribution for a - * full listing of individual contributors. - * - * 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, v. 2.1. - * This program is distributed in the hope that it will be useful, but WITHOUT A - * 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, - * v.2.1 along with this distribution; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ -package org.hibernate.test.annotations.join; - -import org.hibernate.testing.TestForIssue; -import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; -import org.junit.Test; - -/** - * @author Brett Meyer - */ -@TestForIssue( jiraKey = "HHH-2872" ) -public class JoinOrderingTest extends BaseCoreFunctionalTestCase { - - @Override - protected Class[] getAnnotatedClasses() { - // This is the important piece. ProductDetails must be first to - // reproduce the issue. -// return new Class[] { ProductDetails.class, Product.class, ProductVersion.class }; - // TODO: commented out -- @FailureExpected wasn't working on builds - // if it's a MappingException. - return new Class[] { }; - } - - @Test - public void testEntityOrderingWithJoins() { - // nothing to do - } -} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/join/Product.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/join/Product.java deleted file mode 100644 index 0b01963dc1..0000000000 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/join/Product.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.hibernate.test.annotations.join; - -import javax.persistence.CascadeType; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.Table; -import javax.persistence.UniqueConstraint; - -@Entity -@Table(name = "product", uniqueConstraints = @UniqueConstraint(columnNames = "code")) -public class Product { - - @Id - @GeneratedValue - @Column(name = "id_product") - private Integer id; - - @ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.ALL}) - @JoinColumn(name = "id_product_version", nullable = false) - private ProductVersion productVersion; - - @Column(name = "code", unique = true, nullable = false, precision = 4, scale = 0) - private Long code; - - public Integer getId() { - return this.id; - } - - public void setId(final Integer id) { - this.id = id; - } - - public ProductVersion getProductVersion() { - return this.productVersion; - } - - public void setProductVersion(final ProductVersion productVersion) { - this.productVersion = productVersion; - } - - public Long getCode() { - return this.code; - } - - public void setCode(final Long code) { - this.code = code; - } -} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/join/ProductDetails.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/join/ProductDetails.java deleted file mode 100644 index acb7e73228..0000000000 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/join/ProductDetails.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.hibernate.test.annotations.join; - -import javax.persistence.CascadeType; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.JoinColumns; -import javax.persistence.ManyToOne; -import javax.persistence.Table; - -@Entity -@Table(name = "product_details") -public class ProductDetails { - @Id - @GeneratedValue - @Column(name = "id_product_details") - private Integer id; - - @ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.ALL}) - @JoinColumns({ @JoinColumn(name = "id_product", referencedColumnName = "id_product", nullable = false), - @JoinColumn(name = "id_product_version", referencedColumnName = "id_product_version", nullable = false) }) - private Product product; - - public Integer getId() { - return this.id; - } - - public void setId(final Integer id) { - this.id = id; - } - - public Product getProduct() { - return this.product; - } - - public void setProduct(final Product product) { - this.product = product; - } - -} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/join/ProductVersion.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/join/ProductVersion.java deleted file mode 100644 index 01b5b4d7dd..0000000000 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/join/ProductVersion.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.hibernate.test.annotations.join; - -import static javax.persistence.GenerationType.IDENTITY; - -import java.util.ArrayList; -import java.util.List; - -import javax.persistence.CascadeType; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.OneToMany; -import javax.persistence.Table; - -@Entity -@Table(name = "product_version") -public class ProductVersion { - - @Id - @GeneratedValue(strategy = IDENTITY) - @Column(name = "id_product_version", unique = true, nullable = false) - private Integer id; - - @OneToMany(fetch = FetchType.LAZY, mappedBy = "productVersion", cascade = {CascadeType.ALL}) - private List products = new ArrayList(0); - - public Integer getId() { - return this.id; - } - - public void setId(final Integer id) { - this.id = id; - } - - public List getProducts() { - return this.products; - } - - public void setProducts(final ArrayList products) { - this.products = products; - } - -} From a03d44f290deb8ccddc0827834e49e03fc8da275 Mon Sep 17 00:00:00 2001 From: Strong Liu Date: Mon, 20 May 2013 20:37:16 -0700 Subject: [PATCH 03/36] HHH-8246 Implement XML binding of NamedStoredProcedureQuery --- .../org/hibernate/cfg/AnnotationBinder.java | 35 ++-- .../java/org/hibernate/cfg/Configuration.java | 2 +- .../NamedProcedureCallDefinition.java | 22 +-- .../cfg/annotations/QueryBinder.java | 158 +++--------------- .../cfg/annotations/QueryHintDefinition.java | 152 +++++++++++++++++ .../reflection/JPAMetadataProvider.java | 12 ++ .../JPAOverriddenAnnotationReader.java | 117 +++++++++++-- .../internal/NamedQueryRepository.java | 3 +- .../internal/SessionFactoryImpl.java | 10 +- .../internal/ProcedureCallMementoImpl.java | 4 +- .../java/org/hibernate/jpa/QueryHints.java | 19 ++- .../AbstractStoredProcedureTest.java | 67 ++++++++ .../jpa/test/procedure/AnnotationTest.java | 11 ++ .../hibernate/jpa/test/procedure/OrmTest.java | 11 ++ .../hibernate/jpa/test/procedure/User.java | 77 +++++++++ .../org/hibernate/jpa/test/procedure/orm.xml | 29 ++++ 16 files changed, 522 insertions(+), 207 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/cfg/annotations/QueryHintDefinition.java create mode 100644 hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/AbstractStoredProcedureTest.java create mode 100644 hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/AnnotationTest.java create mode 100644 hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/OrmTest.java create mode 100644 hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/User.java create mode 100644 hibernate-entitymanager/src/test/resources/org/hibernate/jpa/test/procedure/orm.xml diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java index 1ce6542a96..875cc3d769 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java @@ -272,7 +272,7 @@ public final class AnnotationBinder { (List) defaults.get( NamedStoredProcedureQuery.class ); if ( annotations != null ) { for ( NamedStoredProcedureQuery annotation : annotations ) { - QueryBinder.bindNamedStoredProcedureQuery( annotation, mappings ); + bindNamedStoredProcedureQuery( mappings, annotation ); } } } @@ -281,9 +281,7 @@ public final class AnnotationBinder { (List) defaults.get( NamedStoredProcedureQueries.class ); if ( annotations != null ) { for ( NamedStoredProcedureQueries annotation : annotations ) { - for ( NamedStoredProcedureQuery queryAnnotation : annotation.value() ) { - QueryBinder.bindNamedStoredProcedureQuery( queryAnnotation, mappings ); - } + bindNamedStoredProcedureQueries( mappings, annotation ); } } } @@ -394,24 +392,29 @@ public final class AnnotationBinder { } // NamedStoredProcedureQuery handling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - { - final NamedStoredProcedureQuery annotation = annotatedElement.getAnnotation( NamedStoredProcedureQuery.class ); - if ( annotation != null ) { - QueryBinder.bindNamedStoredProcedureQuery( annotation, mappings ); - } - } + bindNamedStoredProcedureQuery( mappings, annotatedElement.getAnnotation( NamedStoredProcedureQuery.class ) ); // NamedStoredProcedureQueries handling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - { - final NamedStoredProcedureQueries annotation = annotatedElement.getAnnotation( NamedStoredProcedureQueries.class ); - if ( annotation != null ) { - for ( NamedStoredProcedureQuery queryAnnotation : annotation.value() ) { - QueryBinder.bindNamedStoredProcedureQuery( queryAnnotation, mappings ); - } + bindNamedStoredProcedureQueries( + mappings, + annotatedElement.getAnnotation( NamedStoredProcedureQueries.class ) + ); + } + + private static void bindNamedStoredProcedureQueries(Mappings mappings, NamedStoredProcedureQueries annotation) { + if ( annotation != null ) { + for ( NamedStoredProcedureQuery queryAnnotation : annotation.value() ) { + bindNamedStoredProcedureQuery( mappings, queryAnnotation ); } } } + private static void bindNamedStoredProcedureQuery(Mappings mappings, NamedStoredProcedureQuery annotation) { + if ( annotation != null ) { + QueryBinder.bindNamedStoredProcedureQuery( annotation, mappings ); + } + } + private static IdGenerator buildIdGenerator(java.lang.annotation.Annotation ann, Mappings mappings) { IdGenerator idGen = new IdGenerator(); if ( mappings.getSchemaName() != null ) { 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 671b0bbb28..83a1949894 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java @@ -303,7 +303,7 @@ public class Configuration implements Serializable { namedSqlQueries = new HashMap(); sqlResultSetMappings = new HashMap(); namedEntityGraphMap = new HashMap(); - + namedProcedureCallMap = new HashMap( ); typeDefs = new HashMap(); filterDefinitions = new HashMap(); fetchProfiles = new HashMap(); diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/NamedProcedureCallDefinition.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/NamedProcedureCallDefinition.java index b50173c016..f8a9411c3f 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/NamedProcedureCallDefinition.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/NamedProcedureCallDefinition.java @@ -25,25 +25,20 @@ package org.hibernate.cfg.annotations; import javax.persistence.NamedStoredProcedureQuery; import javax.persistence.ParameterMode; -import javax.persistence.QueryHint; import javax.persistence.StoredProcedureParameter; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import org.hibernate.LockMode; import org.hibernate.MappingException; import org.hibernate.engine.ResultSetMappingDefinition; import org.hibernate.engine.query.spi.sql.NativeSQLQueryReturn; -import org.hibernate.engine.query.spi.sql.NativeSQLQueryRootReturn; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.internal.SessionFactoryImpl; import org.hibernate.internal.util.StringHelper; -import org.hibernate.persister.entity.EntityPersister; import org.hibernate.procedure.ProcedureCallMemento; import org.hibernate.procedure.internal.ParameterStrategy; import org.hibernate.procedure.internal.ProcedureCallMementoImpl; @@ -65,7 +60,7 @@ public class NamedProcedureCallDefinition { private final Class[] resultClasses; private final String[] resultSetMappings; private final ParameterDefinitions parameterDefinitions; - private final Map hints; + private final Map hints; NamedProcedureCallDefinition(NamedStoredProcedureQuery annotation) { this.registeredName = annotation.name(); @@ -73,7 +68,7 @@ public class NamedProcedureCallDefinition { this.resultClasses = annotation.resultClasses(); this.resultSetMappings = annotation.resultSetMappings(); this.parameterDefinitions = new ParameterDefinitions( annotation.parameters() ); - this.hints = extract( annotation.hints() ); + this.hints = new QueryHintDefinition( annotation.hints() ).getHintsMap(); final boolean specifiesResultClasses = resultClasses != null && resultClasses.length > 0; final boolean specifiesResultSetMappings = resultSetMappings != null && resultSetMappings.length > 0; @@ -88,17 +83,6 @@ public class NamedProcedureCallDefinition { } } - private Map extract(QueryHint[] hints) { - if ( hints == null || hints.length == 0 ) { - return Collections.emptyMap(); - } - final Map hintsMap = new HashMap(); - for ( QueryHint hint : hints ) { - hintsMap.put( hint.name(), hint.value() ); - } - return hintsMap; - } - public String getRegisteredName() { return registeredName; } @@ -201,7 +185,7 @@ public class NamedProcedureCallDefinition { public List toMementos(SessionFactoryImpl sessionFactory) { final List mementos = new ArrayList(); for ( ParameterDefinition definition : parameterDefinitions ) { - definition.toMemento( sessionFactory ); + mementos.add(definition.toMemento( sessionFactory )); } return mementos; } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/QueryBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/QueryBinder.java index 2c21637b04..28f8a0cdd9 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/QueryBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/QueryBinder.java @@ -23,13 +23,11 @@ */ package org.hibernate.cfg.annotations; import java.util.HashMap; -import javax.persistence.LockModeType; import javax.persistence.NamedNativeQueries; import javax.persistence.NamedNativeQuery; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.NamedStoredProcedureQuery; -import javax.persistence.QueryHint; import javax.persistence.SqlResultSetMapping; import javax.persistence.SqlResultSetMappings; @@ -40,7 +38,6 @@ import org.hibernate.AssertionFailure; import org.hibernate.CacheMode; import org.hibernate.FlushMode; import org.hibernate.LockMode; -import org.hibernate.LockOptions; import org.hibernate.annotations.CacheModeType; import org.hibernate.annotations.FlushModeType; import org.hibernate.annotations.QueryHints; @@ -54,7 +51,6 @@ import org.hibernate.engine.spi.NamedQueryDefinitionBuilder; import org.hibernate.engine.spi.NamedSQLQueryDefinition; import org.hibernate.engine.spi.NamedSQLQueryDefinitionBuilder; import org.hibernate.internal.CoreMessageLogger; -import org.hibernate.internal.util.LockModeConverter; /** * Query binder @@ -70,19 +66,19 @@ public abstract class QueryBinder { throw new AnnotationException( "A named query must have a name when used in class or package level" ); } //EJBQL Query - QueryHint[] hints = queryAnn.hints(); + QueryHintDefinition hints = new QueryHintDefinition( queryAnn.hints() ); String queryName = queryAnn.query(); NamedQueryDefinition queryDefinition = new NamedQueryDefinitionBuilder( queryAnn.name() ) - .setLockOptions( determineLockOptions( queryAnn, hints ) ) + .setLockOptions( hints.determineLockOptions( queryAnn ) ) .setQuery( queryName ) - .setCacheable( getBoolean( queryName, "org.hibernate.cacheable", hints ) ) - .setCacheRegion( getString( queryName, "org.hibernate.cacheRegion", hints ) ) - .setTimeout( getTimeout( queryName, hints ) ) - .setFetchSize( getInteger( queryName, "org.hibernate.fetchSize", hints ) ) - .setFlushMode( getFlushMode( queryName, hints ) ) - .setCacheMode( getCacheMode( queryName, hints ) ) - .setReadOnly( getBoolean( queryName, "org.hibernate.readOnly", hints ) ) - .setComment( getString( queryName, "org.hibernate.comment", hints ) ) + .setCacheable( hints.getBoolean( queryName, QueryHints.CACHEABLE ) ) + .setCacheRegion( hints.getString( queryName, QueryHints.CACHE_REGION ) ) + .setTimeout( hints.getTimeout( queryName ) ) + .setFetchSize( hints.getInteger( queryName, QueryHints.FETCH_SIZE ) ) + .setFlushMode( hints.getFlushMode( queryName ) ) + .setCacheMode( hints.getCacheMode( queryName ) ) + .setReadOnly( hints.getBoolean( queryName, QueryHints.READ_ONLY ) ) + .setComment( hints.getString( queryName, QueryHints.COMMENT ) ) .setParameterTypes( null ) .createNamedQueryDefinition(); @@ -97,17 +93,7 @@ public abstract class QueryBinder { } } - private static LockOptions determineLockOptions(NamedQuery namedQueryAnnotation, QueryHint[] hints) { - LockModeType lockModeType = namedQueryAnnotation.lockMode(); - Integer lockTimeoutHint = getInteger( namedQueryAnnotation.name(), "javax.persistence.lock.timeout", hints ); - LockOptions lockOptions = new LockOptions( LockModeConverter.convertToLockMode( lockModeType ) ); - if ( lockTimeoutHint != null ) { - lockOptions.setTimeOut( lockTimeoutHint ); - } - - return lockOptions; - } public static void bindNativeQuery(NamedNativeQuery queryAnn, Mappings mappings, boolean isDefault) { if ( queryAnn == null ) return; @@ -116,22 +102,22 @@ public abstract class QueryBinder { throw new AnnotationException( "A named query must have a name when used in class or package level" ); } String resultSetMapping = queryAnn.resultSetMapping(); - QueryHint[] hints = queryAnn.hints(); + QueryHintDefinition hints = new QueryHintDefinition( queryAnn.hints() ); String queryName = queryAnn.query(); NamedSQLQueryDefinitionBuilder builder = new NamedSQLQueryDefinitionBuilder( queryAnn.name() ) .setQuery( queryName ) .setQuerySpaces( null ) - .setCacheable( getBoolean( queryName, "org.hibernate.cacheable", hints ) ) - .setCacheRegion( getString( queryName, "org.hibernate.cacheRegion", hints ) ) - .setTimeout( getTimeout( queryName, hints ) ) - .setFetchSize( getInteger( queryName, "org.hibernate.fetchSize", hints ) ) - .setFlushMode( getFlushMode( queryName, hints ) ) - .setCacheMode( getCacheMode( queryName, hints ) ) - .setReadOnly( getBoolean( queryName, "org.hibernate.readOnly", hints ) ) - .setComment( getString( queryName, "org.hibernate.comment", hints ) ) + .setCacheable( hints.getBoolean( queryName, QueryHints.CACHEABLE ) ) + .setCacheRegion( hints.getString( queryName, QueryHints.CACHE_REGION ) ) + .setTimeout( hints.getTimeout( queryName ) ) + .setFetchSize( hints.getInteger( queryName, QueryHints.FETCH_SIZE ) ) + .setFlushMode( hints.getFlushMode( queryName ) ) + .setCacheMode( hints.getCacheMode( queryName ) ) + .setReadOnly( hints.getBoolean( queryName, QueryHints.READ_ONLY ) ) + .setComment( hints.getString( queryName, QueryHints.COMMENT ) ) .setParameterTypes( null ) - .setCallable( getBoolean( queryName, "org.hibernate.callable", hints ) ); + .setCallable( hints.getBoolean( queryName, QueryHints.CALLABLE ) ); if ( !BinderHelper.isEmptyAnnotationValue( resultSetMapping ) ) { //sql result set usage @@ -359,109 +345,5 @@ public abstract class QueryBinder { mappings.addSecondPass( new ResultsetMappingSecondPass( ann, mappings, isDefault ) ); } - private static CacheMode getCacheMode(String query, QueryHint[] hints) { - for (QueryHint hint : hints) { - if ( "org.hibernate.cacheMode".equals( hint.name() ) ) { - if ( hint.value().equalsIgnoreCase( CacheMode.GET.toString() ) ) { - return CacheMode.GET; - } - else if ( hint.value().equalsIgnoreCase( CacheMode.IGNORE.toString() ) ) { - return CacheMode.IGNORE; - } - else if ( hint.value().equalsIgnoreCase( CacheMode.NORMAL.toString() ) ) { - return CacheMode.NORMAL; - } - else if ( hint.value().equalsIgnoreCase( CacheMode.PUT.toString() ) ) { - return CacheMode.PUT; - } - else if ( hint.value().equalsIgnoreCase( CacheMode.REFRESH.toString() ) ) { - return CacheMode.REFRESH; - } - else { - throw new AnnotationException( "Unknown CacheMode in hint: " + query + ":" + hint.name() ); - } - } - } - return null; - } - private static FlushMode getFlushMode(String query, QueryHint[] hints) { - for (QueryHint hint : hints) { - if ( "org.hibernate.flushMode".equals( hint.name() ) ) { - if ( hint.value().equalsIgnoreCase( FlushMode.ALWAYS.toString() ) ) { - return FlushMode.ALWAYS; - } - else if ( hint.value().equalsIgnoreCase( FlushMode.AUTO.toString() ) ) { - return FlushMode.AUTO; - } - else if ( hint.value().equalsIgnoreCase( FlushMode.COMMIT.toString() ) ) { - return FlushMode.COMMIT; - } - else if ( hint.value().equalsIgnoreCase( FlushMode.NEVER.toString() ) ) { - return FlushMode.MANUAL; - } - else if ( hint.value().equalsIgnoreCase( FlushMode.MANUAL.toString() ) ) { - return FlushMode.MANUAL; - } - else { - throw new AnnotationException( "Unknown FlushMode in hint: " + query + ":" + hint.name() ); - } - } - } - return null; - } - - private static boolean getBoolean(String query, String hintName, QueryHint[] hints) { - for (QueryHint hint : hints) { - if ( hintName.equals( hint.name() ) ) { - if ( hint.value().equalsIgnoreCase( "true" ) ) { - return true; - } - else if ( hint.value().equalsIgnoreCase( "false" ) ) { - return false; - } - else { - throw new AnnotationException( "Not a boolean in hint: " + query + ":" + hint.name() ); - } - } - } - return false; - } - - private static String getString(String query, String hintName, QueryHint[] hints) { - for (QueryHint hint : hints) { - if ( hintName.equals( hint.name() ) ) { - return hint.value(); - } - } - return null; - } - - private static Integer getInteger(String query, String hintName, QueryHint[] hints) { - for (QueryHint hint : hints) { - if ( hintName.equals( hint.name() ) ) { - try { - return Integer.decode( hint.value() ); - } - catch (NumberFormatException nfe) { - throw new AnnotationException( "Not an integer in hint: " + query + ":" + hint.name(), nfe ); - } - } - } - return null; - } - - private static Integer getTimeout(String queryName, QueryHint[] hints) { - Integer timeout = getInteger( queryName, "javax.persistence.query.timeout", hints ); - - if ( timeout != null ) { - // convert milliseconds to seconds - timeout = (int)Math.round(timeout.doubleValue() / 1000.0 ); - } - else { - // timeout is already in seconds - timeout = getInteger( queryName, "org.hibernate.timeout", hints ); - } - return timeout; - } } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/QueryHintDefinition.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/QueryHintDefinition.java new file mode 100644 index 0000000000..37ec6ad059 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/QueryHintDefinition.java @@ -0,0 +1,152 @@ +/* + * 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 java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import javax.persistence.LockModeType; +import javax.persistence.NamedQuery; +import javax.persistence.QueryHint; + +import org.hibernate.AnnotationException; +import org.hibernate.CacheMode; +import org.hibernate.FlushMode; +import org.hibernate.LockOptions; +import org.hibernate.MappingException; +import org.hibernate.annotations.QueryHints; +import org.hibernate.internal.util.LockModeConverter; + +/** + * @author Strong Liu + */ +public class QueryHintDefinition { + private final Map hintsMap; + + public QueryHintDefinition(final QueryHint[] hints) { + if ( hints == null || hints.length == 0 ) { + hintsMap = Collections.emptyMap(); + } + else { + final Map hintsMap = new HashMap(); + for ( QueryHint hint : hints ) { + hintsMap.put( hint.name(), hint.value() ); + } + this.hintsMap = hintsMap; + } + } + + + public CacheMode getCacheMode(String query) { + String hitName = QueryHints.CACHE_MODE; + String value =(String) hintsMap.get( hitName ); + if ( value == null ) { + return null; + } + try { + return CacheMode.interpretExternalSetting( value ); + } + catch ( MappingException e ) { + throw new AnnotationException( "Unknown CacheMode in hint: " + query + ":" + hitName, e ); + } + } + + public FlushMode getFlushMode(String query) { + String hitName = QueryHints.FLUSH_MODE; + String value =(String) hintsMap.get( hitName ); + if ( value == null ) { + return null; + } + try { + return FlushMode.interpretExternalSetting( value ); + } + catch ( MappingException e ) { + throw new AnnotationException( "Unknown FlushMode in hint: " + query + ":" + hitName, e ); + } + } + + public boolean getBoolean(String query, String hintName) { + String value =(String) hintsMap.get( hintName ); + if ( value == null ) { + return false; + } + if ( value.equalsIgnoreCase( "true" ) ) { + return true; + } + else if ( value.equalsIgnoreCase( "false" ) ) { + return false; + } + else { + throw new AnnotationException( "Not a boolean in hint: " + query + ":" + hintName ); + } + + } + + public String getString(String query, String hintName) { + return (String) hintsMap.get( hintName ); + } + + public Integer getInteger(String query, String hintName) { + String value = (String) hintsMap.get( hintName ); + if ( value == null ) { + return null; + } + try { + return Integer.decode( value ); + } + catch ( NumberFormatException nfe ) { + throw new AnnotationException( "Not an integer in hint: " + query + ":" + hintName, nfe ); + } + } + + public Integer getTimeout(String queryName) { + Integer timeout = getInteger( queryName, QueryHints.TIMEOUT_JPA ); + + if ( timeout != null ) { + // convert milliseconds to seconds + timeout = (int) Math.round( timeout.doubleValue() / 1000.0 ); + } + else { + // timeout is already in seconds + timeout = getInteger( queryName, QueryHints.TIMEOUT_HIBERNATE ); + } + return timeout; + } + + public LockOptions determineLockOptions(NamedQuery namedQueryAnnotation) { + LockModeType lockModeType = namedQueryAnnotation.lockMode(); + Integer lockTimeoutHint = getInteger( namedQueryAnnotation.name(), "javax.persistence.lock.timeout" ); + + LockOptions lockOptions = new LockOptions( LockModeConverter.convertToLockMode( lockModeType ) ); + if ( lockTimeoutHint != null ) { + lockOptions.setTimeOut( lockTimeoutHint ); + } + + return lockOptions; + } + + public Map getHintsMap() { + return hintsMap; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/JPAMetadataProvider.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/JPAMetadataProvider.java index 9d80eeca41..c15a877eea 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/JPAMetadataProvider.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/JPAMetadataProvider.java @@ -33,6 +33,7 @@ import java.util.Map; import javax.persistence.EntityListeners; import javax.persistence.NamedNativeQuery; import javax.persistence.NamedQuery; +import javax.persistence.NamedStoredProcedureQuery; import javax.persistence.SequenceGenerator; import javax.persistence.SqlResultSetMapping; import javax.persistence.TableGenerator; @@ -49,6 +50,7 @@ import org.hibernate.internal.util.ReflectHelper; * * @author Emmanuel Bernard */ +@SuppressWarnings("unchecked") public class JPAMetadataProvider implements MetadataProvider, Serializable { private transient MetadataProvider delegate = new JavaMetadataProvider(); private transient Map defaults; @@ -152,6 +154,16 @@ public class JPAMetadataProvider implements MetadataProvider, Serializable { element, xmlDefaults ); sqlResultSetMappings.addAll( currentSqlResultSetMappings ); + + List namedStoredProcedureQueries = (List)defaults.get( NamedStoredProcedureQuery.class ); + if(namedStoredProcedureQueries==null){ + namedStoredProcedureQueries = new ArrayList( ); + defaults.put( NamedStoredProcedureQuery.class, namedStoredProcedureQueries ); + } + List currentNamedStoredProcedureQueries = JPAOverriddenAnnotationReader.buildNamedStoreProcedureQueries( + element, xmlDefaults + ); + namedStoredProcedureQueries.addAll( currentNamedStoredProcedureQueries ); } } return defaults; diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/JPAOverriddenAnnotationReader.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/JPAOverriddenAnnotationReader.java index 8db6de8449..765dcdae85 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/JPAOverriddenAnnotationReader.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/JPAOverriddenAnnotationReader.java @@ -92,10 +92,12 @@ import javax.persistence.NamedNativeQueries; import javax.persistence.NamedNativeQuery; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; +import javax.persistence.NamedStoredProcedureQuery; import javax.persistence.OneToMany; import javax.persistence.OneToOne; import javax.persistence.OrderBy; import javax.persistence.OrderColumn; +import javax.persistence.ParameterMode; import javax.persistence.PostLoad; import javax.persistence.PostPersist; import javax.persistence.PostRemove; @@ -111,6 +113,7 @@ import javax.persistence.SecondaryTables; import javax.persistence.SequenceGenerator; import javax.persistence.SqlResultSetMapping; import javax.persistence.SqlResultSetMappings; +import javax.persistence.StoredProcedureParameter; import javax.persistence.Table; import javax.persistence.TableGenerator; import javax.persistence.Temporal; @@ -1743,6 +1746,84 @@ public class JPAOverriddenAnnotationReader implements AnnotationReader { } } + public static List buildNamedStoreProcedureQueries(Element element, XMLContext.Default defaults) { + if ( element == null ) { + return new ArrayList(); + } + List namedStoredProcedureElements = element.elements( "named-stored-procedure-query" ); + List namedStoredProcedureQueries = new ArrayList(); + for ( Object obj : namedStoredProcedureElements ) { + Element subElement = (Element) obj; + AnnotationDescriptor ann = new AnnotationDescriptor( NamedStoredProcedureQuery.class ); + copyStringAttribute( ann, subElement, "name", true ); + copyStringAttribute( ann, subElement, "procedure-name", true ); + + List elements = subElement.elements( "parameter" ); + List storedProcedureParameters = new ArrayList(); + + for ( Element parameterElement : elements ) { + AnnotationDescriptor parameterDescriptor = new AnnotationDescriptor( StoredProcedureParameter.class ); + copyStringAttribute( parameterDescriptor, parameterElement, "name", false ); + String modeValue = parameterElement.attributeValue( "mode" ); + if ( modeValue == null ) { + parameterDescriptor.setValue( "mode", ParameterMode.IN ); + } + else { + parameterDescriptor.setValue( "mode", ParameterMode.valueOf( modeValue.toUpperCase() ) ); + } + String clazzName = parameterElement.attributeValue( "class" ); + Class clazz; + try { + clazz = ReflectHelper.classForName( + XMLContext.buildSafeClassName( clazzName, defaults ), + JPAOverriddenAnnotationReader.class + ); + } + catch ( ClassNotFoundException e ) { + throw new AnnotationException( "Unable to find entity-class: " + clazzName, e ); + } + parameterDescriptor.setValue( "type", clazz ); + storedProcedureParameters.add( (StoredProcedureParameter) AnnotationFactory.create( parameterDescriptor ) ); + } + + ann.setValue( + "parameters", + storedProcedureParameters.toArray( new StoredProcedureParameter[storedProcedureParameters.size()] ) + ); + + elements = subElement.elements( "result-class" ); + List returnClasses = new ArrayList(); + for ( Element classElement : elements ) { + String clazzName = classElement.getTextTrim(); + Class clazz; + try { + clazz = ReflectHelper.classForName( + XMLContext.buildSafeClassName( clazzName, defaults ), + JPAOverriddenAnnotationReader.class + ); + } + catch ( ClassNotFoundException e ) { + throw new AnnotationException( "Unable to find entity-class: " + clazzName, e ); + } + returnClasses.add( clazz ); + } + ann.setValue( "resultClasses", returnClasses.toArray( new Class[returnClasses.size()] ) ); + + + elements = subElement.elements( "result-set-mapping" ); + List resultSetMappings = new ArrayList(); + for ( Element resultSetMappingElement : elements ) { + resultSetMappings.add( resultSetMappingElement.getTextTrim() ); + } + ann.setValue( "resultSetMappings", resultSetMappings.toArray( new String[resultSetMappings.size()] ) ); + elements = subElement.elements( "hint" ); + buildQueryHints( elements, ann ); + namedStoredProcedureQueries.add( (NamedStoredProcedureQuery) AnnotationFactory.create( ann ) ); + } + return namedStoredProcedureElements; + + } + public static List buildSqlResultsetMappings(Element element, XMLContext.Default defaults) { if ( element == null ) { return new ArrayList(); @@ -1910,6 +1991,25 @@ public class JPAOverriddenAnnotationReader implements AnnotationReader { } } + private static void buildQueryHints(List elements, AnnotationDescriptor ann){ + List queryHints = new ArrayList( elements.size() ); + for ( Element hint : elements ) { + AnnotationDescriptor hintDescriptor = new AnnotationDescriptor( QueryHint.class ); + String value = hint.attributeValue( "name" ); + if ( value == null ) { + throw new AnnotationException( " without name. " + SCHEMA_VALIDATION ); + } + hintDescriptor.setValue( "name", value ); + value = hint.attributeValue( "value" ); + if ( value == null ) { + throw new AnnotationException( " without value. " + SCHEMA_VALIDATION ); + } + hintDescriptor.setValue( "value", value ); + queryHints.add( (QueryHint) AnnotationFactory.create( hintDescriptor ) ); + } + ann.setValue( "hints", queryHints.toArray( new QueryHint[queryHints.size()] ) ); + } + public static List buildNamedQueries(Element element, boolean isNative, XMLContext.Default defaults) { if ( element == null ) { return new ArrayList(); @@ -1931,22 +2031,7 @@ public class JPAOverriddenAnnotationReader implements AnnotationReader { } copyStringElement( queryElt, ann, "query" ); List elements = subelement.elements( "hint" ); - List queryHints = new ArrayList( elements.size() ); - for ( Element hint : elements ) { - AnnotationDescriptor hintDescriptor = new AnnotationDescriptor( QueryHint.class ); - String value = hint.attributeValue( "name" ); - if ( value == null ) { - throw new AnnotationException( " without name. " + SCHEMA_VALIDATION ); - } - hintDescriptor.setValue( "name", value ); - value = hint.attributeValue( "value" ); - if ( value == null ) { - throw new AnnotationException( " without value. " + SCHEMA_VALIDATION ); - } - hintDescriptor.setValue( "value", value ); - queryHints.add( (QueryHint) AnnotationFactory.create( hintDescriptor ) ); - } - ann.setValue( "hints", queryHints.toArray( new QueryHint[queryHints.size()] ) ); + buildQueryHints( elements, ann ); String clazzName = subelement.attributeValue( "result-class" ); if ( StringHelper.isNotEmpty( clazzName ) ) { Class clazz; diff --git a/hibernate-core/src/main/java/org/hibernate/internal/NamedQueryRepository.java b/hibernate-core/src/main/java/org/hibernate/internal/NamedQueryRepository.java index 90866b06ad..bbe9fdb444 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/NamedQueryRepository.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/NamedQueryRepository.java @@ -56,7 +56,7 @@ public class NamedQueryRepository { Iterable namedQueryDefinitions, Iterable namedSqlQueryDefinitions, Iterable namedSqlResultSetMappings, - List namedProcedureCalls) { + Map namedProcedureCalls) { final HashMap namedQueryDefinitionMap = new HashMap(); for ( NamedQueryDefinition namedQueryDefinition : namedQueryDefinitions ) { namedQueryDefinitionMap.put( namedQueryDefinition.getName(), namedQueryDefinition ); @@ -75,6 +75,7 @@ public class NamedQueryRepository { namedSqlResultSetMappingMap.put( resultSetMappingDefinition.getName(), resultSetMappingDefinition ); } this.namedSqlResultSetMappingMap = Collections.unmodifiableMap( namedSqlResultSetMappingMap ); + this.procedureCallMementoMap = Collections.unmodifiableMap( namedProcedureCalls ); } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java index 3f191d8a8b..eafe6aa75a 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java @@ -580,13 +580,13 @@ public final class SessionFactoryImpl this.observer.sessionFactoryCreated( this ); } - private List toProcedureCallMementos( + private Map toProcedureCallMementos( Map definitions, Map resultSetMappingMap) { - final List rtn = new ArrayList(); + final Map rtn = new HashMap(); if ( definitions != null ) { - for ( NamedProcedureCallDefinition definition : definitions.values() ) { - rtn.add( definition.toMemento( this, resultSetMappingMap ) ); + for (String name : definitions.keySet()){ + rtn.put( name, definitions.get( name ).toMemento( this, resultSetMappingMap )); } } return rtn; @@ -871,7 +871,7 @@ public final class SessionFactoryImpl metadata.getNamedQueryDefinitions(), metadata.getNamedNativeQueryDefinitions(), metadata.getResultSetMappingDefinitions(), - null + new HashMap( ) ); imports = new HashMap(); diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureCallMementoImpl.java b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureCallMementoImpl.java index 456198a494..90d29f5ff1 100644 --- a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureCallMementoImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureCallMementoImpl.java @@ -49,7 +49,7 @@ public class ProcedureCallMementoImpl implements ProcedureCallMemento { private final Set synchronizedQuerySpaces; - private final Map hintsMap; + private final Map hintsMap; /** * Constructs a ProcedureCallImpl @@ -67,7 +67,7 @@ public class ProcedureCallMementoImpl implements ProcedureCallMemento { ParameterStrategy parameterStrategy, List parameterDeclarations, Set synchronizedQuerySpaces, - Map hintsMap) { + Map hintsMap) { this.procedureName = procedureName; this.queryReturns = queryReturns; this.parameterStrategy = parameterStrategy; diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/QueryHints.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/QueryHints.java index da1d05ea5b..86fe2e7e1f 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/QueryHints.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/QueryHints.java @@ -24,6 +24,7 @@ package org.hibernate.jpa; import java.util.HashSet; import java.util.Set; +import static org.hibernate.annotations.QueryHints.*; /** * Defines the supported JPA query hints @@ -37,52 +38,52 @@ public class QueryHints { * @deprecated use {@link #SPEC_HINT_TIMEOUT} instead */ @Deprecated - public static final String HINT_TIMEOUT = "org.hibernate.timeout"; + public static final String HINT_TIMEOUT = TIMEOUT_HIBERNATE; /** * The hint key for specifying a query timeout per JPA, which defines the timeout in milliseconds */ - public static final String SPEC_HINT_TIMEOUT = "javax.persistence.query.timeout"; + public static final String SPEC_HINT_TIMEOUT = TIMEOUT_JPA; /** * The hint key for specifying a comment which is to be embedded into the SQL sent to the database. */ - public static final String HINT_COMMENT = "org.hibernate.comment"; + public static final String HINT_COMMENT = COMMENT; /** * The hint key for specifying a JDBC fetch size, used when executing the resulting SQL. */ - public static final String HINT_FETCH_SIZE = "org.hibernate.fetchSize"; + public static final String HINT_FETCH_SIZE = FETCH_SIZE; /** * The hint key for specifying whether the query results should be cached for the next (cached) execution of the * "same query". */ - public static final String HINT_CACHEABLE = "org.hibernate.cacheable"; + public static final String HINT_CACHEABLE = CACHEABLE; /** * The hint key for specifying the name of the cache region (within Hibernate's query result cache region) * to use for storing the query results. */ - public static final String HINT_CACHE_REGION = "org.hibernate.cacheRegion"; + public static final String HINT_CACHE_REGION = CACHE_REGION; /** * The hint key for specifying that objects loaded into the persistence context as a result of this query execution * should be associated with the persistence context as read-only. */ - public static final String HINT_READONLY = "org.hibernate.readOnly"; + public static final String HINT_READONLY = READ_ONLY; /** * The hint key for specifying the cache mode ({@link org.hibernate.CacheMode}) to be in effect for the * execution of the hinted query. */ - public static final String HINT_CACHE_MODE = "org.hibernate.cacheMode"; + public static final String HINT_CACHE_MODE = CACHE_MODE; /** * The hint key for specifying the flush mode ({@link org.hibernate.FlushMode}) to be in effect for the * execution of the hinted query. */ - public static final String HINT_FLUSH_MODE = "org.hibernate.flushMode"; + public static final String HINT_FLUSH_MODE = FLUSH_MODE; private static final Set HINTS = buildHintsSet(); diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/AbstractStoredProcedureTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/AbstractStoredProcedureTest.java new file mode 100644 index 0000000000..27ea7a3969 --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/AbstractStoredProcedureTest.java @@ -0,0 +1,67 @@ +package org.hibernate.jpa.test.procedure; + +import java.util.List; +import javax.persistence.EntityManager; + +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; +import org.hibernate.procedure.internal.ParameterStrategy; +import org.hibernate.procedure.internal.ProcedureCallMementoImpl; +import org.hibernate.type.IntegerType; +import org.hibernate.type.LongType; +import org.hibernate.type.StringType; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * @author Strong Liu + */ +public abstract class AbstractStoredProcedureTest extends BaseEntityManagerFunctionalTestCase { + @Test + public void testNamedStoredProcedureBinding() { + EntityManager em = getOrCreateEntityManager(); + SessionFactoryImplementor sf = em.getEntityManagerFactory().unwrap( SessionFactoryImplementor.class ); + final ProcedureCallMementoImpl m1 = (ProcedureCallMementoImpl) sf.getNamedQueryRepository() + .getNamedProcedureCallMemento( "s1" ); + assertNotNull( m1 ); + assertEquals( "p1", m1.getProcedureName() ); + assertEquals( ParameterStrategy.NAMED, m1.getParameterStrategy() ); + List list = m1.getParameterDeclarations(); + assertEquals( 2, list.size() ); + ProcedureCallMementoImpl.ParameterMemento memento = list.get( 0 ); + assertEquals( "p11", memento.getName() ); + assertEquals( javax.persistence.ParameterMode.IN, memento.getMode() ); + assertEquals( IntegerType.INSTANCE, memento.getHibernateType() ); + assertEquals( Integer.class, memento.getType() ); + + memento = list.get( 1 ); + assertEquals( "p12", memento.getName() ); + assertEquals( javax.persistence.ParameterMode.IN, memento.getMode() ); + assertEquals( IntegerType.INSTANCE, memento.getHibernateType() ); + assertEquals( Integer.class, memento.getType() ); + + + + final ProcedureCallMementoImpl m2 = (ProcedureCallMementoImpl) sf.getNamedQueryRepository() + .getNamedProcedureCallMemento( "s2" ); + assertNotNull( m2 ); + assertEquals( "p2", m2.getProcedureName() ); + assertEquals( ParameterStrategy.POSITIONAL, m2.getParameterStrategy() ); + list = m2.getParameterDeclarations(); + + memento = list.get( 0 ); + assertEquals( Integer.valueOf( 0 ), memento.getPosition() ); + assertEquals( javax.persistence.ParameterMode.INOUT, memento.getMode() ); + assertEquals( StringType.INSTANCE, memento.getHibernateType() ); + assertEquals( String.class, memento.getType() ); + + memento = list.get( 1 ); + assertEquals( Integer.valueOf( 1 ), memento.getPosition() ); + assertEquals( javax.persistence.ParameterMode.INOUT, memento.getMode() ); + assertEquals( LongType.INSTANCE, memento.getHibernateType() ); + assertEquals( Long.class, memento.getType() ); + + } +} diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/AnnotationTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/AnnotationTest.java new file mode 100644 index 0000000000..d9bdde87eb --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/AnnotationTest.java @@ -0,0 +1,11 @@ +package org.hibernate.jpa.test.procedure; + +/** + * @author Strong Liu + */ +public class AnnotationTest extends AbstractStoredProcedureTest { + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { User.class }; + } +} diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/OrmTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/OrmTest.java new file mode 100644 index 0000000000..0c12e35d55 --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/OrmTest.java @@ -0,0 +1,11 @@ +package org.hibernate.jpa.test.procedure; + +/** + * @author Strong Liu + */ +public class OrmTest extends AbstractStoredProcedureTest{ + @Override + public String[] getEjb3DD() { + return new String[]{"org/hibernate/jpa/test/procedure/orm.xml"}; + } +} diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/User.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/User.java new file mode 100644 index 0000000000..9bcbd6101f --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/User.java @@ -0,0 +1,77 @@ +package org.hibernate.jpa.test.procedure; + +import javax.persistence.Entity; +import javax.persistence.EntityResult; +import javax.persistence.FieldResult; +import javax.persistence.Id; +import javax.persistence.NamedStoredProcedureQueries; +import javax.persistence.NamedStoredProcedureQuery; +import javax.persistence.ParameterMode; +import javax.persistence.SqlResultSetMapping; +import javax.persistence.StoredProcedureParameter; + +/** + * @author Strong Liu + */ +@Entity +@NamedStoredProcedureQueries( + { + @NamedStoredProcedureQuery( + name = "s1", + procedureName = "p1", + parameters = { + @StoredProcedureParameter(name = "p11", + mode = ParameterMode.IN, + type = Integer.class), + @StoredProcedureParameter(name = "p12", + mode = ParameterMode.IN, + type = Integer.class + ) + }, + resultClasses = { User.class } + ), + @NamedStoredProcedureQuery( + name = "s2", + procedureName = "p2", + parameters = { + @StoredProcedureParameter( + mode = ParameterMode.INOUT, + type = String.class), + @StoredProcedureParameter( + mode = ParameterMode.INOUT, + type = Long.class) + }, + resultSetMappings = { "srms" } + + ) + } +) +@SqlResultSetMapping(name = "srms", + entities = { + @EntityResult(entityClass = User.class, fields = { + @FieldResult(name = "id", column = "order_id"), + @FieldResult(name = "name", column = "order_item") + }) + } +) +public class User { + @Id + private int id; + private String name; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/hibernate-entitymanager/src/test/resources/org/hibernate/jpa/test/procedure/orm.xml b/hibernate-entitymanager/src/test/resources/org/hibernate/jpa/test/procedure/orm.xml new file mode 100644 index 0000000000..684a7a5d71 --- /dev/null +++ b/hibernate-entitymanager/src/test/resources/org/hibernate/jpa/test/procedure/orm.xml @@ -0,0 +1,29 @@ + + + org.hibernate.jpa.test.procedure + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From b846fa35b5b8850366df665eadb1b314f3c5a3c1 Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Tue, 21 May 2013 00:51:15 -0700 Subject: [PATCH 04/36] HHH-7841 - Redesign Loader --- .../EntityJoinableAssociationImpl.java | 2 +- .../plan/internal/LoadPlanBuildingHelper.java | 1 - .../loader/plan/spi/AbstractFetchOwner.java | 14 +++++- .../spi/AbstractSingularAttributeFetch.java | 11 +++++ .../plan/spi/CompositeElementGraph.java | 10 ++++ .../loader/plan/spi/CompositeFetch.java | 42 +++++------------ .../plan/spi/CompositeFetchOwnerDelegate.java | 9 ++++ .../loader/plan/spi/CompositeIndexGraph.java | 16 ++++++- .../loader/plan/spi/EntityElementGraph.java | 10 ++++ .../loader/plan/spi/EntityFetch.java | 44 ++++++++++++++---- .../plan/spi/EntityFetchOwnerDelegate.java | 8 ++++ .../loader/plan/spi/EntityIndexGraph.java | 17 +++++++ .../loader/plan/spi/EntityReturn.java | 14 +++++- .../org/hibernate/loader/plan/spi/Fetch.java | 15 ++++++ .../hibernate/loader/plan/spi/FetchOwner.java | 21 +++++++++ .../loader/plan/spi/FetchOwnerDelegate.java | 23 ++++++++++ .../plan/spi/FetchableCollectionElement.java | 5 +- .../plan/spi/FetchableCollectionIndex.java | 6 +++ .../AbstractLoadPlanBuilderStrategy.java | 6 +-- .../AbstractCollectionPersister.java | 10 ++-- ...ompositionSingularSubAttributesHelper.java | 46 +++++++++++++++++-- .../spi/AssociationVisitationStrategy.java | 4 +- .../spi/CollectionElementDefinition.java | 37 ++++++++++++++- ...CompositeCollectionElementDefinition.java} | 8 +++- .../spi/MetadataDrivenModelGraphVisitor.java | 8 ++-- .../AbstractCompositeBasedAttribute.java | 2 + .../AbstractCompositionAttribute.java | 2 + .../persister/walking/BasicWalkingTest.java | 6 +-- .../graph/internal/advisor/AdviceHelper.java | 5 -- 29 files changed, 326 insertions(+), 76 deletions(-) rename hibernate-core/src/main/java/org/hibernate/persister/walking/spi/{CompositionElementDefinition.java => CompositeCollectionElementDefinition.java} (83%) diff --git a/hibernate-core/src/main/java/org/hibernate/loader/internal/EntityJoinableAssociationImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/internal/EntityJoinableAssociationImpl.java index 34d22b56ba..84830547d1 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/internal/EntityJoinableAssociationImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/internal/EntityJoinableAssociationImpl.java @@ -58,7 +58,7 @@ public class EntityJoinableAssociationImpl extends AbstractJoinableAssociationIm hasRestriction, enabledFilters ); - this.joinableType = entityFetch.getAssociationType(); + this.joinableType = entityFetch.getEntityType(); this.joinable = (Joinable) entityFetch.getEntityPersister(); } 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 ef71c92038..a22fb8171e 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 @@ -64,7 +64,6 @@ public class LoadPlanBuildingHelper { LockMode.NONE, // todo : for now fetchOwner, attributeDefinition.getName(), - (EntityType) attributeDefinition.getType(), fetchStrategy ); } 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 2e6293c1b5..83e01a9ebf 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 @@ -36,11 +36,17 @@ import org.hibernate.persister.walking.spi.CompositionDefinition; import org.hibernate.type.Type; /** + * This is a class for fetch owners, providing functionality related to the owned + * fetches. + * * @author Steve Ebersole * @author Gail Badner */ public abstract class AbstractFetchOwner extends AbstractPlanNode implements FetchOwner { + // TODO: I removed lockMode from this method because I *think* it only relates to EntityFetch and EntityReturn. + // lockMode should be moved back here if it applies to all fetch owners. + private List fetches; public AbstractFetchOwner(SessionFactoryImplementor factory) { @@ -55,17 +61,18 @@ public abstract class AbstractFetchOwner extends AbstractPlanNode implements Fet * A "copy" constructor. Used while making clones/copies of this. * * @param original - the original object to copy. + * @param copyContext - the copy context. */ protected AbstractFetchOwner(AbstractFetchOwner original, CopyContext copyContext) { super( original ); validate(); + // TODO: I don't think this is correct; shouldn't the fetches from original be copied into this??? copyContext.getReturnGraphVisitationStrategy().startingFetches( original ); if ( fetches == null || fetches.size() == 0 ) { 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 ) ); @@ -75,6 +82,7 @@ public abstract class AbstractFetchOwner extends AbstractPlanNode implements Fet copyContext.getReturnGraphVisitationStrategy().finishingFetches( original ); } + @Override public void addFetch(Fetch fetch) { if ( fetch.getOwner() != this ) { throw new IllegalArgumentException( "Fetch and owner did not match" ); @@ -92,6 +100,10 @@ public abstract class AbstractFetchOwner extends AbstractPlanNode implements Fet return fetches == null ? NO_FETCHES : fetches.toArray( new Fetch[ fetches.size() ] ); } + /** + * Abstract method returning the delegate for obtaining details about an owned fetch. + * @return the delegate + */ protected abstract FetchOwnerDelegate getFetchOwnerDelegate(); @Override 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 0b3689844d..7382e08c29 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 @@ -30,7 +30,10 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.loader.PropertyPath; /** + * Represents a singular attribute that is both a {@link FetchOwner} and a {@link Fetch}. + * * @author Steve Ebersole + * @author Gail Badner */ public abstract class AbstractSingularAttributeFetch extends AbstractFetchOwner implements Fetch { private final FetchOwner owner; @@ -39,6 +42,14 @@ public abstract class AbstractSingularAttributeFetch extends AbstractFetchOwner private final PropertyPath propertyPath; + /** + * Constructs an {@link AbstractSingularAttributeFetch} object. + * + * @param factory - the session factory. + * @param owner - the fetch owner for this fetch. + * @param ownerProperty - the owner's property referring to this fetch. + * @param fetchStrategy - the fetch strategy for this fetch. + */ public AbstractSingularAttributeFetch( SessionFactoryImplementor factory, FetchOwner owner, 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 e2d23540df..29314ac755 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 @@ -12,7 +12,10 @@ import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; import org.hibernate.type.CompositeType; /** + * Represents the {@link FetchOwner} for a composite collection element. + * * @author Steve Ebersole + * @author Gail Badner */ public class CompositeElementGraph extends AbstractFetchOwner implements FetchableCollectionElement { private final CollectionReference collectionReference; @@ -20,6 +23,13 @@ public class CompositeElementGraph extends AbstractFetchOwner implements Fetchab private final CollectionPersister collectionPersister; private final FetchOwnerDelegate fetchOwnerDelegate; + /** + * Constructs a {@link CompositeElementGraph}. + * + * @param sessionFactory - the session factory. + * @param collectionReference - the collection reference. + * @param collectionPath - the {@link PropertyPath} for the collection. + */ public CompositeElementGraph( SessionFactoryImplementor sessionFactory, CollectionReference collectionReference, 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 c1c043308c..0a6c449bea 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 @@ -29,22 +29,29 @@ 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; /** + * Represents a {@link Fetch} for a composite attribute as well as a + * {@link FetchOwner} for any sub-attributes fetches. + * * @author Steve Ebersole + * @author Gail Badner */ public class CompositeFetch extends AbstractSingularAttributeFetch { public static final FetchStrategy FETCH_PLAN = new FetchStrategy( FetchTiming.IMMEDIATE, FetchStyle.JOIN ); private final FetchOwnerDelegate delegate; + /** + * Constructs a {@link CompositeFetch} object. + * + * @param sessionFactory - the session factory. + * @param owner - the fetch owner for this fetch. + * @param ownerProperty - the owner's property referring to this fetch. + */ public CompositeFetch( SessionFactoryImplementor sessionFactory, FetchOwner owner, @@ -72,33 +79,6 @@ public class CompositeFetch extends AbstractSingularAttributeFetch { return getOwner().retrieveFetchSourcePersister(); } - @Override - public CollectionFetch buildCollectionFetch( - AssociationAttributeDefinition attributeDefinition, - FetchStrategy fetchStrategy, - LoadPlanBuildingContext loadPlanBuildingContext) { - return null; //To change body of implemented methods use File | Settings | File Templates. - } - - @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 { //To change body of implemented methods use File | Settings | File Templates. 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 index 8c530eb5d2..f081387fe8 100644 --- 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 @@ -32,6 +32,9 @@ import org.hibernate.type.CompositeType; import org.hibernate.type.Type; /** + * This interface provides a delegate for a composite fetch owner to + * obtain details about an owned sub-attribute fetch. + * * @author Gail Badner */ public class CompositeFetchOwnerDelegate implements FetchOwnerDelegate { @@ -39,6 +42,12 @@ public class CompositeFetchOwnerDelegate implements FetchOwnerDelegate { private final CompositeType compositeType; private final String[] columnNames; + /** + * Constructs a {@link CompositeFetchOwnerDelegate}. + * @param sessionFactory - the session factory. + * @param compositeType - the composite type. + * @param columnNames - the column names used by sub-attribute fetches. + */ public CompositeFetchOwnerDelegate( SessionFactoryImplementor sessionFactory, CompositeType compositeType, 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 f4f4eb7fe4..7f8e29aaed 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 @@ -12,7 +12,10 @@ import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; import org.hibernate.type.CompositeType; /** + * Represents the {@link FetchOwner} for a composite collection index. + * * @author Steve Ebersole + * @author Gail Badner */ public class CompositeIndexGraph extends AbstractFetchOwner implements FetchableCollectionIndex { private final CollectionReference collectionReference; @@ -20,14 +23,21 @@ public class CompositeIndexGraph extends AbstractFetchOwner implements Fetchable private final CollectionPersister collectionPersister; private final FetchOwnerDelegate fetchOwnerDelegate; + /** + * Constructs a {@link CompositeElementGraph}. + * + * @param sessionFactory - the session factory. + * @param collectionReference - the collection reference. + * @param collectionPath - the {@link PropertyPath} for the collection. + */ public CompositeIndexGraph( SessionFactoryImplementor sessionFactory, CollectionReference collectionReference, - PropertyPath propertyPath) { + PropertyPath collectionPath) { super( sessionFactory ); this.collectionReference = collectionReference; this.collectionPersister = collectionReference.getCollectionPersister(); - this.propertyPath = propertyPath.append( "" ); + this.propertyPath = collectionPath.append( "" ); this.fetchOwnerDelegate = new CompositeFetchOwnerDelegate( sessionFactory, (CompositeType) collectionPersister.getIndexType(), @@ -52,6 +62,7 @@ public class CompositeIndexGraph extends AbstractFetchOwner implements Fetchable return collectionPersister.getOwnerEntityPersister(); } + @Override public CollectionReference getCollectionReference() { return collectionReference; } @@ -71,6 +82,7 @@ public class CompositeIndexGraph extends AbstractFetchOwner implements Fetchable return fetchOwnerDelegate; } + @Override public CollectionFetch buildCollectionFetch( AssociationAttributeDefinition attributeDefinition, FetchStrategy fetchStrategy, 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 a3ccde17d7..b318efb667 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 @@ -9,6 +9,9 @@ import org.hibernate.persister.entity.EntityPersister; import org.hibernate.type.AssociationType; /** + * Represents the {@link FetchOwner} for a collection element that is + * an entity association type. + * * @author Steve Ebersole */ public class EntityElementGraph extends AbstractFetchOwner implements FetchableCollectionElement, EntityReference { @@ -21,6 +24,13 @@ public class EntityElementGraph extends AbstractFetchOwner implements FetchableC private IdentifierDescription identifierDescription; + /** + * Constructs an {@link EntityElementGraph}. + * + * @param sessionFactory - the session factory. + * @param collectionReference - the collection reference. + * @param collectionPath - the {@link PropertyPath} for the collection. + */ public EntityElementGraph( SessionFactoryImplementor sessionFactory, CollectionReference collectionReference, 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 9842166561..ed18f21465 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 @@ -37,28 +37,39 @@ import org.hibernate.persister.entity.EntityPersister; import org.hibernate.type.EntityType; /** + * Represents a {@link Fetch} for an entity association attribute as well as a + * {@link FetchOwner} of the entity association sub-attribute fetches. + * @author Steve Ebersole */ 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; + /** + * Constructs an {@link EntityFetch} object. + * + * @param sessionFactory - the session factory. + * @param lockMode - the lock mode. + * @param owner - the fetch owner for this fetch. + * @param ownerProperty - the owner's property referring to this fetch. + * @param fetchStrategy - the fetch strategy for this fetch. + */ public EntityFetch( SessionFactoryImplementor sessionFactory, LockMode lockMode, FetchOwner owner, String ownerProperty, - EntityType entityType, FetchStrategy fetchStrategy) { super( sessionFactory, owner, ownerProperty, fetchStrategy ); - this.associationType = entityType; - this.persister = sessionFactory.getEntityPersister( associationType.getAssociatedEntityName() ); + this.persister = sessionFactory.getEntityPersister( + getEntityType().getAssociatedEntityName() + ); this.lockMode = lockMode; this.fetchOwnerDelegate = new EntityFetchOwnerDelegate( persister ); } @@ -71,14 +82,17 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit */ protected EntityFetch(EntityFetch original, CopyContext copyContext, FetchOwner fetchOwnerCopy) { super( original, copyContext, fetchOwnerCopy ); - this.associationType = original.associationType; this.persister = original.persister; this.lockMode = original.lockMode; this.fetchOwnerDelegate = original.fetchOwnerDelegate; } - public EntityType getAssociationType() { - return associationType; + /** + * Returns the entity type for this fetch. + * @return the entity type for this fetch. + */ + public final EntityType getEntityType() { + return (EntityType) getOwner().getType( this ); } @Override @@ -134,12 +148,12 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit entityKey = identifierDescription.resolve( resultSet, context ); if ( entityKey == null ) { // register the non-existence (though only for one-to-one associations) - if ( associationType.isOneToOne() ) { + if ( getEntityType().isOneToOne() ) { // first, find our owner's entity-key... final EntityKey ownersEntityKey = context.getIdentifierResolutionContext( (EntityReference) getOwner() ).getEntityKey(); if ( ownersEntityKey != null ) { context.getSession().getPersistenceContext() - .addNullProperty( ownersEntityKey, associationType.getPropertyName() ); + .addNullProperty( ownersEntityKey, getEntityType().getPropertyName() ); } } } @@ -154,6 +168,16 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit return entityKey; } + /** + * Resolve any fetches required to resolve the identifier as well + * as the entity key for this fetch.. + * + * @param resultSet - the result set. + * @param context - the result set processing context. + * @return the entity key for this fetch. + * + * @throws SQLException + */ public EntityKey resolveInIdentifier(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException { // todo : may not need to do this if entitykey is already part of the resolution context @@ -219,7 +243,7 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit acquiredLockMode, persister, getFetchStrategy().getTiming() == FetchTiming.IMMEDIATE, - associationType + getEntityType() ); // materialize associations (and initialize the object) later 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 index 7ef801df07..956471717e 100644 --- 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 @@ -30,11 +30,19 @@ import org.hibernate.type.AssociationType; import org.hibernate.type.Type; /** + * This interface provides a delegate for an entity fetch owner to + * obtain details about an owned attribute fetch. + * * @author Gail Badner */ public class EntityFetchOwnerDelegate implements FetchOwnerDelegate { private final EntityPersister entityPersister; + /** + * Constructs an {@link EntityFetchOwnerDelegate}. + * + * @param entityPersister - the entity persister. + */ public EntityFetchOwnerDelegate(EntityPersister entityPersister) { this.entityPersister = entityPersister; } 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 384b8e98d4..df16559c81 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 @@ -32,6 +32,8 @@ import org.hibernate.persister.entity.EntityPersister; import org.hibernate.type.AssociationType; /** + * Represents the {@link FetchOwner} for a collection index that is an entity. + * * @author Steve Ebersole */ public class EntityIndexGraph extends AbstractFetchOwner implements FetchableCollectionIndex, EntityReference { @@ -44,6 +46,13 @@ public class EntityIndexGraph extends AbstractFetchOwner implements FetchableCol private IdentifierDescription identifierDescription; + /** + * Constructs an {@link EntityIndexGraph}. + * + * @param sessionFactory - the session factory. + * @param collectionReference - the collection reference. + * @param collectionPath - the {@link PropertyPath} for the collection. + */ public EntityIndexGraph( SessionFactoryImplementor sessionFactory, CollectionReference collectionReference, @@ -67,6 +76,9 @@ public class EntityIndexGraph extends AbstractFetchOwner implements FetchableCol this.fetchOwnerDelegate = original.fetchOwnerDelegate; } + /** + * TODO: Does lock mode apply to a collection index that is an entity? + */ @Override public LockMode getLockMode() { return null; @@ -111,6 +123,11 @@ public class EntityIndexGraph extends AbstractFetchOwner implements FetchableCol return new EntityIndexGraph( this, copyContext ); } + @Override + public CollectionReference getCollectionReference() { + return collectionReference; + } + @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 7680e4a30f..db6321aa7d 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 @@ -38,13 +38,18 @@ import org.hibernate.persister.entity.EntityPersister; import static org.hibernate.loader.spi.ResultSetProcessingContext.IdentifierResolutionContext; /** + * Represents an entity return value in the query results. Not the same + * as a result (column) in the JDBC ResultSet! + * + * @see Return + * * @author Steve Ebersole */ public class EntityReturn extends AbstractFetchOwner implements Return, EntityReference, CopyableReturn { private final EntityPersister persister; - private final PropertyPath propertyPath = new PropertyPath(); // its a root + private final PropertyPath propertyPath = new PropertyPath(); // it's a root private final LockMode lockMode; @@ -52,6 +57,13 @@ public class EntityReturn extends AbstractFetchOwner implements Return, EntityRe private IdentifierDescription identifierDescription; + /** + * Construct an {@link EntityReturn}. + * + * @param sessionFactory - the session factory. + * @param lockMode - the lock mode. + * @param entityName - the entity name. + */ public EntityReturn( SessionFactoryImplementor sessionFactory, LockMode lockMode, 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 9d34ff51ad..8eed47bf01 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,10 +52,25 @@ public interface Fetch extends CopyableFetch { */ public String getOwnerPropertyName(); + /** + * Is this fetch nullable? + * + * @return true, if this fetch is nullable; false, otherwise. + */ public boolean isNullable(); + /** + * Gets the column names used for this fetch. + * + * @return the column names used for this fetch. + */ public String[] getColumnNames(); + /** + * Gets the fetch strategy for this fetch. + * + * @return the fetch strategy for this fetch. + */ 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 9558e987d3..81ac8685a4 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 @@ -57,10 +57,31 @@ public interface FetchOwner { */ public Fetch[] getFetches(); + /** + * Returns the type of the specified fetch. + * + * @param fetch - the owned fetch. + * + * @return the type of the specified fetch. + */ public Type getType(Fetch fetch); + /** + * Is the specified fetch nullable? + * + * @param fetch - the owned fetch. + * + * @return true, if the fetch is nullable; false, otherwise. + */ public boolean isNullable(Fetch fetch); + /** + * Returns the column names used for loading the specified fetch. + * + * @param fetch - the owned fetch. + * + * @return the column names used for loading the specified fetch. + */ public String[] getColumnNames(Fetch 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 index 523891b3ef..31916efe06 100644 --- 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 @@ -26,13 +26,36 @@ package org.hibernate.loader.plan.spi; import org.hibernate.type.Type; /** + * This interface provides a delegate for a fetch owner to obtain details about an owned fetch. + * * @author Gail Badner */ public interface FetchOwnerDelegate { + /** + * Is the specified fetch nullable? + * + * @param fetch - the owned fetch. + * + * @return true, if the fetch is nullable; false, otherwise. + */ public boolean isNullable(Fetch fetch); + /** + * Returns the type of the specified fetch. + * + * @param fetch - the owned fetch. + * + * @return the type of the specified fetch. + */ public Type getType(Fetch fetch); + /** + * Returns the column names used for loading the specified fetch. + * + * @param fetch - the owned fetch. + * + * @return the column names used for loading the specified 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 c4f090f9e5..f714ad3240 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,7 +23,6 @@ */ package org.hibernate.loader.plan.spi; -import org.hibernate.persister.collection.CollectionPersister; /** * @author Steve Ebersole @@ -32,5 +31,9 @@ public interface FetchableCollectionElement extends FetchOwner, CopyableReturn { @Override public FetchableCollectionElement makeCopy(CopyContext copyContext); + /** + * Returns the collection reference. + * @return the collection reference. + */ public CollectionReference getCollectionReference(); } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/FetchableCollectionIndex.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/FetchableCollectionIndex.java index 7fe443bb2f..d051fb3099 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/FetchableCollectionIndex.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/FetchableCollectionIndex.java @@ -31,4 +31,10 @@ package org.hibernate.loader.plan.spi; public interface FetchableCollectionIndex extends FetchOwner, CopyableReturn { @Override public FetchableCollectionIndex makeCopy(CopyContext copyContext); + + /** + * Returns the collection reference. + * @return the collection reference. + */ + 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 d7c12c762d..039a0d515d 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 @@ -69,8 +69,8 @@ import org.hibernate.persister.walking.spi.AttributeDefinition; 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.CompositeCollectionElementDefinition; 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; @@ -319,7 +319,7 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder } @Override - public void startingCompositeElement(CompositionElementDefinition compositeElementDefinition) { + public void startingCompositeCollectionElement(CompositeCollectionElementDefinition compositeElementDefinition) { System.out.println( String.format( "%s Starting composite collection element for (%s)", @@ -330,7 +330,7 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder } @Override - public void finishingCompositeElement(CompositionElementDefinition compositeElementDefinition) { + public void finishingCompositeCollectionElement(CompositeCollectionElementDefinition compositeElementDefinition) { // pop the current fetch owner, and make sure what we just popped represents this composition final FetchOwner poppedFetchOwner = popFromStack(); 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 aa1831b121..160057e76c 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 @@ -86,8 +86,8 @@ 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.CompositeCollectionElementDefinition; 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; @@ -2010,15 +2010,13 @@ public abstract class AbstractCollectionPersister } @Override - public CompositionElementDefinition toCompositeElementDefinition() { - final String propertyName = role.substring( entityName.length() + 1 ); - final int propertyIndex = ownerPersister.getEntityMetamodel().getPropertyIndex( propertyName ); + public CompositeCollectionElementDefinition toCompositeElementDefinition() { if ( ! getType().isComponentType() ) { throw new IllegalStateException( "Cannot treat entity collection element type as composite" ); } - return new CompositionElementDefinition() { + return new CompositeCollectionElementDefinition() { @Override public String getName() { return ""; @@ -2038,7 +2036,7 @@ public abstract class AbstractCollectionPersister @Override public Iterable getAttributes() { - return CompositionSingularSubAttributesHelper.getCompositionElementSubAttributes( this ); + return CompositionSingularSubAttributesHelper.getCompositeCollectionElementSubAttributes( this ); } @Override 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 index 8cac9860c0..3a68c09b7b 100644 --- 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 @@ -1,3 +1,26 @@ +/* + * 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 java.util.Iterator; @@ -20,8 +43,8 @@ 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.CompositeCollectionElementDefinition; 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; @@ -29,10 +52,22 @@ import org.hibernate.type.CompositeType; import org.hibernate.type.Type; /** + * A helper for getting attributes from a composition that is known + * to have only singular attributes; for example, sub-attributes of a + * composite ID or a composite collection element. + * + * TODO: This should be refactored into a delegate and renamed. + * * @author Gail Badner */ public class CompositionSingularSubAttributesHelper { + /** + * Get composite ID sub-attribute definitions. + * + * @param entityPersister - the entity persister. + * @return composite ID sub-attribute definitions. + */ public static Iterable getIdentifierSubAttributes( final AbstractEntityPersister entityPersister) { return getSingularSubAttributes( @@ -44,8 +79,13 @@ public class CompositionSingularSubAttributesHelper { ); } - public static Iterable getCompositionElementSubAttributes( - CompositionElementDefinition compositionElementDefinition) { + /** + * Get sub-attribute definitions for a composite collection element. + * @param compositionElementDefinition - composite collection element definition. + * @return sub-attribute definitions for a composite collection element. + */ + public static Iterable getCompositeCollectionElementSubAttributes( + CompositeCollectionElementDefinition compositionElementDefinition) { final QueryableCollection collectionPersister = (QueryableCollection) compositionElementDefinition.getCollectionDefinition().getCollectionPersister(); return getSingularSubAttributes( 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 6bf7d9a156..d9d475a445 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,8 +55,8 @@ public interface AssociationVisitationStrategy { public void startingComposite(CompositionDefinition compositionDefinition); public void finishingComposite(CompositionDefinition compositionDefinition); - public void startingCompositeElement(CompositionElementDefinition compositionElementDefinition); - public void finishingCompositeElement(CompositionElementDefinition compositionElementDefinition); + public void startingCompositeCollectionElement(CompositeCollectionElementDefinition compositionElementDefinition); + public void finishingCompositeCollectionElement(CompositeCollectionElementDefinition 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 ff138f76f4..94e05cc242 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 @@ -26,14 +26,49 @@ package org.hibernate.persister.walking.spi; import org.hibernate.type.Type; /** + * Represents a collection element. + * * @author Steve Ebersole */ public interface CollectionElementDefinition { + + /** + * Returns the collection definition. + * @return the collection definition. + */ public CollectionDefinition getCollectionDefinition(); + /** + * Returns the collection element type. + * @return the collection element type + */ public Type getType(); + /** + * If the element type returned by {@link #getType()} is an + * {@link org.hibernate.type.EntityType}, then the entity + * definition for the collection element is returned; + * otherwise, IllegalStateException is thrown. + * + * @return the entity definition for the collection element. + * + * @throws IllegalStateException if the collection element type + * returned by {@link #getType()} is not of type + * {@link org.hibernate.type.EntityType}. + */ public EntityDefinition toEntityDefinition(); - public CompositionElementDefinition toCompositeElementDefinition(); + /** + * If the element type returned by {@link #getType()} is a + * {@link org.hibernate.type.CompositeType}, then the composite + * element definition for the collection element is returned; + * otherwise, IllegalStateException is thrown. + * + * @return the composite element definition for the collection element. + * + * @throws IllegalStateException if the collection element type + * returned by {@link #getType()} is not of type + * {@link org.hibernate.type.CompositeType}. + */ + public CompositeCollectionElementDefinition 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/CompositeCollectionElementDefinition.java similarity index 83% rename from hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CompositionElementDefinition.java rename to hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CompositeCollectionElementDefinition.java index cd073881c8..f7354e4c3a 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CompositionElementDefinition.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CompositeCollectionElementDefinition.java @@ -24,8 +24,14 @@ package org.hibernate.persister.walking.spi; /** + * The definition for a composite collection element. + * * @author Gail Badner */ -public interface CompositionElementDefinition extends CompositionDefinition{ +public interface CompositeCollectionElementDefinition extends CompositionDefinition{ + /** + * Returns the collection definition. + * @return the collection definition. + */ 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 ffdccb2b99..ee6e494d8a 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 @@ -202,7 +202,7 @@ public class MetadataDrivenModelGraphVisitor { strategy.startingCollectionElements( elementDefinition ); if ( elementDefinition.getType().isComponentType() ) { - visitCompositeElementDefinition( elementDefinition.toCompositeElementDefinition() ); + visitCompositeCollectionElementDefinition( elementDefinition.toCompositeElementDefinition() ); } else if ( elementDefinition.getType().isEntityType() ) { visitEntityDefinition( elementDefinition.toEntityDefinition() ); @@ -211,12 +211,12 @@ public class MetadataDrivenModelGraphVisitor { strategy.finishingCollectionElements( elementDefinition ); } - private void visitCompositeElementDefinition(CompositionElementDefinition compositionElementDefinition) { - strategy.startingCompositeElement( compositionElementDefinition ); + private void visitCompositeCollectionElementDefinition(CompositeCollectionElementDefinition compositionElementDefinition) { + strategy.startingCompositeCollectionElement( compositionElementDefinition ); visitAttributes( compositionElementDefinition ); - strategy.finishingCompositeElement( compositionElementDefinition ); + strategy.finishingCompositeCollectionElement( compositionElementDefinition ); } private final Set visitedAssociationKeys = new HashSet(); 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 9dbcbc7558..f9371d0649 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 @@ -30,6 +30,8 @@ import org.hibernate.tuple.NonIdentifierAttribute; import org.hibernate.type.Type; /** + * A base class for a sub-attribute of a composite, non-identifier attribute. + * * @author Steve Ebersole */ public abstract class AbstractCompositeBasedAttribute diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositionAttribute.java b/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositionAttribute.java index e563a9dd9e..d2bf2c4526 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositionAttribute.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositionAttribute.java @@ -46,6 +46,8 @@ import static org.hibernate.engine.internal.JoinHelper.getLHSTableName; import static org.hibernate.engine.internal.JoinHelper.getRHSColumnNames; /** + * A base class for a composite, non-identifier attribute. + * * @author Steve Ebersole */ public abstract class AbstractCompositionAttribute extends AbstractNonIdentifierAttribute implements 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 411a42f714..90788b3911 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 @@ -37,8 +37,8 @@ import org.hibernate.persister.walking.spi.AttributeDefinition; 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.CompositeCollectionElementDefinition; 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; @@ -170,7 +170,7 @@ public class BasicWalkingTest extends BaseCoreFunctionalTestCase { } @Override - public void startingCompositeElement(CompositionElementDefinition compositionElementDefinition) { + public void startingCompositeCollectionElement(CompositeCollectionElementDefinition compositionElementDefinition) { System.out.println( String.format( "%s Starting composite (%s)", @@ -181,7 +181,7 @@ public class BasicWalkingTest extends BaseCoreFunctionalTestCase { } @Override - public void finishingCompositeElement(CompositionElementDefinition compositionElementDefinition) { + public void finishingCompositeCollectionElement(CompositeCollectionElementDefinition compositionElementDefinition) { System.out.println( String.format( "%s Finishing composite (%s)", 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 3f7bcab15b..1e65c7a037 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 @@ -56,16 +56,11 @@ 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 ) ); } From b2828115278b45fa7e703d80ac13dcaad5a036c4 Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Tue, 21 May 2013 03:08:47 -0700 Subject: [PATCH 05/36] HHH-7841 - Redesign Loader --- .../plan/spi/CompositeFetchOwnerDelegate.java | 4 +- .../plan/spi/EntityFetchOwnerDelegate.java | 6 +- .../AbstractLoadPlanBuilderStrategy.java | 216 +++++++----------- 3 files changed, 85 insertions(+), 141 deletions(-) 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 index f081387fe8..7782b10354 100644 --- 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 @@ -32,8 +32,8 @@ import org.hibernate.type.CompositeType; import org.hibernate.type.Type; /** - * This interface provides a delegate for a composite fetch owner to - * obtain details about an owned sub-attribute fetch. + * A delegate for a composite fetch owner to obtain details about an + * owned sub-attribute fetch. * * @author Gail Badner */ 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 index 956471717e..f8a599aad6 100644 --- 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 @@ -30,8 +30,8 @@ import org.hibernate.type.AssociationType; import org.hibernate.type.Type; /** - * This interface provides a delegate for an entity fetch owner to - * obtain details about an owned attribute fetch. + * A delegate for an entity fetch owner to obtain details about + * an owned attribute fetch. * * @author Gail Badner */ @@ -59,6 +59,7 @@ public class EntityFetchOwnerDelegate implements FetchOwnerDelegate { @Override public String[] getColumnNames(Fetch fetch) { + // TODO: cache this info final OuterJoinLoadable outerJoinLoadable = (OuterJoinLoadable) entityPersister; Type fetchType = getType( fetch ); if ( fetchType.isAssociationType() ) { @@ -75,6 +76,7 @@ public class EntityFetchOwnerDelegate implements FetchOwnerDelegate { } private int determinePropertyIndex(Fetch fetch) { + // TODO: cache this info return entityPersister.getEntityMetamodel().getPropertyIndex( fetch.getOwnerPropertyName() ); } } 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 039a0d515d..817d7db847 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 @@ -27,9 +27,7 @@ import java.io.Serializable; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayDeque; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; import org.jboss.logging.Logger; @@ -44,8 +42,7 @@ import org.hibernate.engine.spi.EntityKey; 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.AbstractFetchOwner; import org.hibernate.loader.plan.spi.CollectionFetch; import org.hibernate.loader.plan.spi.CollectionReference; import org.hibernate.loader.plan.spi.CollectionReturn; @@ -198,15 +195,10 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder final FetchOwner identifierAttributeCollector; if ( entityIdentifierDefinition.isEncapsulated() ) { - if ( entityIdentifierDefinition.getEntityDefinition().getEntityPersister().getIdentifierType().isComponentType() ) { - identifierAttributeCollector = new EncapsulatedCompositeIdentifierAttributeCollector( entityReference ); - } - else { - identifierAttributeCollector = new EncapsulatedIdentifierAttributeCollector( entityReference ); - } + identifierAttributeCollector = new EncapsulatedIdentifierAttributeCollector( sessionFactory, entityReference ); } else { - identifierAttributeCollector = new NonEncapsulatedIdentifierAttributeCollector( entityReference ); + identifierAttributeCollector = new NonEncapsulatedIdentifierAttributeCollector( sessionFactory, entityReference ); } pushToStack( identifierAttributeCollector ); } @@ -525,16 +517,14 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder public void poppedFromStack(); } - protected static abstract class AbstractIdentifierAttributeCollector + protected static abstract class AbstractIdentifierAttributeCollector extends AbstractFetchOwner implements FetchOwner, EntityReference, FetchStackAware { - protected final EntityReference entityReference; + protected final Map fetchToHydratedStateExtractorMap + = new HashMap(); - protected final List identifierFetches = new ArrayList(); - protected final Map fetchToHydratedStateExtractorMap - = new HashMap(); - - public AbstractIdentifierAttributeCollector(EntityReference entityReference) { + public AbstractIdentifierAttributeCollector(SessionFactoryImplementor sessionFactory, EntityReference entityReference) { + super( sessionFactory ); this.entityReference = entityReference; } @@ -553,11 +543,6 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder return entityReference.getEntityPersister(); } - @Override - public boolean isNullable(Fetch fetch) { - return false; - } - @Override public IdentifierDescription getIdentifierDescription() { return entityReference.getIdentifierDescription(); @@ -580,29 +565,24 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder // // 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, - attributeDefinition, - fetchStrategy, - loadPlanBuildingContext - ); + final EntityFetch fetch = super.buildEntityFetch( attributeDefinition, fetchStrategy, loadPlanBuildingContext ); fetchToHydratedStateExtractorMap.put( fetch, attributeDefinition.getHydratedCompoundValueExtractor() ); return fetch; } @Override - public CompositeFetch buildCompositeFetch( - CompositionDefinition attributeDefinition, LoadPlanBuildingContext loadPlanBuildingContext) { - // nested composition. Unusual, but not disallowed. - // - // 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, - attributeDefinition, - loadPlanBuildingContext - ); + 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 super.getType( fetch ); } @Override @@ -613,16 +593,6 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder protected abstract IdentifierDescription buildIdentifierDescription(); - @Override - public void addFetch(Fetch fetch) { - identifierFetches.add( (AbstractSingularAttributeFetch) fetch ); - } - - @Override - public Fetch[] getFetches() { - return ( (FetchOwner) entityReference ).getFetches(); - } - @Override public void validateFetchPlan(FetchStrategy fetchStrategy) { ( (FetchOwner) entityReference ).validateFetchPlan( fetchStrategy ); @@ -641,38 +611,64 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder } } - protected static abstract class AbstractCompositeIdentifierAttributeCollector extends AbstractIdentifierAttributeCollector { - - public AbstractCompositeIdentifierAttributeCollector(EntityReference entityReference) { - super( entityReference ); - } - } - protected static class EncapsulatedIdentifierAttributeCollector extends AbstractIdentifierAttributeCollector { private final PropertyPath propertyPath; + private final FetchOwnerDelegate delegate; - public EncapsulatedIdentifierAttributeCollector(EntityReference entityReference) { - super( entityReference ); + public EncapsulatedIdentifierAttributeCollector( + final SessionFactoryImplementor sessionFactory, + final EntityReference entityReference) { + super( sessionFactory, entityReference ); this.propertyPath = ( (FetchOwner) entityReference ).getPropertyPath(); + final boolean isCompositeType = entityReference.getEntityPersister().getIdentifierType().isComponentType(); + this.delegate = new FetchOwnerDelegate() { + @Override + public boolean isNullable(Fetch fetch) { + if ( !isCompositeType ) { + throw new IllegalStateException( "Non-composite ID cannot have fetches." ); + } + return true; + } + + @Override + public Type getType(Fetch fetch) { + if ( !isCompositeType ) { + throw new IllegalStateException( "Non-composite ID cannot have fetches." ); + } + if ( !fetch.getOwnerPropertyName().equals( entityReference.getEntityPersister().getIdentifierPropertyName() ) ) { + throw new IllegalArgumentException( + String.format( + "Fetch owner property name [%s] is not the same as the identifier prop" + + fetch.getOwnerPropertyName(), + entityReference.getEntityPersister().getIdentifierPropertyName() + ) + ); + } + return entityReference.getEntityPersister().getIdentifierType(); + } + + @Override + public String[] getColumnNames(Fetch fetch) { + if ( !isCompositeType ) { + throw new IllegalStateException( "Non-composite ID cannot have fetches." ); + } + return ( (Loadable) entityReference.getEntityPersister() ).getIdentifierColumnNames(); + } + }; } @Override protected IdentifierDescription buildIdentifierDescription() { return new IdentifierDescriptionImpl( entityReference, - identifierFetches.toArray( new AbstractSingularAttributeFetch[ identifierFetches.size() ] ), + getFetches(), 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(); + protected FetchOwnerDelegate getFetchOwnerDelegate() { + return delegate; } @Override @@ -681,54 +677,12 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder } } - 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 { + protected static class NonEncapsulatedIdentifierAttributeCollector extends AbstractIdentifierAttributeCollector { private final PropertyPath propertyPath; private final FetchOwnerDelegate fetchOwnerDelegate; - public NonEncapsulatedIdentifierAttributeCollector(EntityReference entityReference) { - super( entityReference ); + public NonEncapsulatedIdentifierAttributeCollector(SessionFactoryImplementor sessionfactory, EntityReference entityReference) { + super( sessionfactory, entityReference ); this.propertyPath = ( (FetchOwner) entityReference ).getPropertyPath().append( "" ); this.fetchOwnerDelegate = new CompositeFetchOwnerDelegate( entityReference.getEntityPersister().getFactory(), @@ -741,7 +695,7 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder protected IdentifierDescription buildIdentifierDescription() { return new IdentifierDescriptionImpl( entityReference, - identifierFetches.toArray( new AbstractSingularAttributeFetch[ identifierFetches.size() ] ), + getFetches(), fetchToHydratedStateExtractorMap ); } @@ -752,33 +706,21 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder } - 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 ); + @Override + protected FetchOwnerDelegate getFetchOwnerDelegate() { + return fetchOwnerDelegate; } } private static class IdentifierDescriptionImpl implements IdentifierDescription { private final EntityReference entityReference; - private final AbstractSingularAttributeFetch[] identifierFetches; - private final Map fetchToHydratedStateExtractorMap; + private final Fetch[] identifierFetches; + private final Map fetchToHydratedStateExtractorMap; private IdentifierDescriptionImpl( EntityReference entityReference, - AbstractSingularAttributeFetch[] identifierFetches, - Map fetchToHydratedStateExtractorMap) { + Fetch[] identifierFetches, + Map fetchToHydratedStateExtractorMap) { this.entityReference = entityReference; this.identifierFetches = identifierFetches; this.fetchToHydratedStateExtractorMap = fetchToHydratedStateExtractorMap; @@ -796,7 +738,7 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder final Object ownerIdentifierHydratedState = ownerIdentifierResolutionContext.getHydratedForm(); if ( ownerIdentifierHydratedState != null ) { - for ( AbstractSingularAttributeFetch fetch : identifierFetches ) { + for ( Fetch fetch : identifierFetches ) { if ( fetch instanceof EntityFetch ) { final IdentifierResolutionContext identifierResolutionContext = context.getIdentifierResolutionContext( (EntityFetch) fetch ); @@ -833,7 +775,7 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder @Override public EntityKey resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException { - for ( AbstractSingularAttributeFetch fetch : identifierFetches ) { + for ( Fetch fetch : identifierFetches ) { resolveIdentifierFetch( resultSet, context, fetch ); } @@ -850,7 +792,7 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder private static void resolveIdentifierFetch( ResultSet resultSet, ResultSetProcessingContext context, - AbstractSingularAttributeFetch fetch) throws SQLException { + Fetch fetch) throws SQLException { if ( fetch instanceof EntityFetch ) { EntityFetch entityFetch = (EntityFetch) fetch; final IdentifierResolutionContext identifierResolutionContext = @@ -863,8 +805,8 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder identifierResolutionContext.registerEntityKey( fetchKey ); } else if ( fetch instanceof CompositeFetch ) { - for ( Fetch subFetch : fetch.getFetches() ) { - resolveIdentifierFetch( resultSet, context, (AbstractSingularAttributeFetch) subFetch ); + for ( Fetch subFetch : ( (CompositeFetch) fetch ).getFetches() ) { + resolveIdentifierFetch( resultSet, context, subFetch ); } } } From 19da1ad21181147e4c7b326898c12e79d8036e75 Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Tue, 21 May 2013 10:49:46 -0400 Subject: [PATCH 06/36] HHH-8241 Reusing of scanner instance is impossible with Apache Aries JPA --- .../jpa/boot/scan/spi/AbstractScannerImpl.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) 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 3d2c441370..985d453658 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 @@ -200,12 +200,14 @@ public abstract class AbstractScannerImpl implements Scanner { } } - private static class ArchiveDescriptorInfo { - private final ArchiveDescriptor archiveDescriptor; - private final boolean isRoot; - private final ScanOptions scanOptions; + // This needs to be protected and attributes/constructor visible in case + // a custom scanner needs to override validateReuse. + protected static class ArchiveDescriptorInfo { + public final ArchiveDescriptor archiveDescriptor; + public final boolean isRoot; + public final ScanOptions scanOptions; - private ArchiveDescriptorInfo( + public ArchiveDescriptorInfo( ArchiveDescriptor archiveDescriptor, boolean isRoot, ScanOptions scanOptions) { From 21444339ecdba857fd315f453ac4800bebc49800 Mon Sep 17 00:00:00 2001 From: Strong Liu Date: Tue, 21 May 2013 09:46:30 -0700 Subject: [PATCH 07/36] HHH-8247 Implement XML binding of NamedEntityGraph --- .../NamedEntityGraphDefinition.java | 6 +- .../JPAOverriddenAnnotationReader.java | 149 +++++++++++++++++- ...java => AbstractNamedEntityGraphTest.java} | 7 +- .../basic/BasicAnnNamedEntityGraphTest.java | 35 ++++ .../basic/BasicOrmNamedEntityGraphTest.java | 34 ++++ .../jpa/test/graphs/named/basic/Person.java | 4 +- .../jpa/test/graphs/named/basic/orm.xml | 15 ++ 7 files changed, 240 insertions(+), 10 deletions(-) rename hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/graphs/named/basic/{BasicNamedEntityGraphTest.java => AbstractNamedEntityGraphTest.java} (88%) create mode 100644 hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/graphs/named/basic/BasicAnnNamedEntityGraphTest.java create mode 100644 hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/graphs/named/basic/BasicOrmNamedEntityGraphTest.java create mode 100644 hibernate-entitymanager/src/test/resources/org/hibernate/jpa/test/graphs/named/basic/orm.xml 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 index 4c10a940b1..3082e58770 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/NamedEntityGraphDefinition.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/NamedEntityGraphDefinition.java @@ -25,6 +25,8 @@ package org.hibernate.cfg.annotations; import javax.persistence.NamedEntityGraph; +import org.hibernate.internal.util.StringHelper; + /** * Models the definition of a {@link NamedEntityGraph} annotation * @@ -34,15 +36,17 @@ public class NamedEntityGraphDefinition { private final NamedEntityGraph annotation; private final String jpaEntityName; private final String entityName; + private final String name; public NamedEntityGraphDefinition(NamedEntityGraph annotation, String jpaEntityName, String entityName) { this.annotation = annotation; this.jpaEntityName = jpaEntityName; this.entityName = entityName; + this.name = StringHelper.isEmpty( annotation.name() ) ? jpaEntityName : annotation.name(); } public String getRegisteredName() { - return jpaEntityName; + return name; } public String getJpaEntityName() { diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/JPAOverriddenAnnotationReader.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/JPAOverriddenAnnotationReader.java index 765dcdae85..50b8e21033 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/JPAOverriddenAnnotationReader.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/JPAOverriddenAnnotationReader.java @@ -88,11 +88,16 @@ import javax.persistence.MapKeyJoinColumns; import javax.persistence.MapKeyTemporal; import javax.persistence.MappedSuperclass; import javax.persistence.MapsId; +import javax.persistence.NamedAttributeNode; +import javax.persistence.NamedEntityGraph; +import javax.persistence.NamedEntityGraphs; import javax.persistence.NamedNativeQueries; import javax.persistence.NamedNativeQuery; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; +import javax.persistence.NamedStoredProcedureQueries; import javax.persistence.NamedStoredProcedureQuery; +import javax.persistence.NamedSubgraph; import javax.persistence.OneToMany; import javax.persistence.OneToOne; import javax.persistence.OrderBy; @@ -179,10 +184,14 @@ public class JPAOverriddenAnnotationReader implements AnnotationReader { annotationToXml.put( DiscriminatorColumn.class, "discriminator-column" ); annotationToXml.put( SequenceGenerator.class, "sequence-generator" ); annotationToXml.put( TableGenerator.class, "table-generator" ); + annotationToXml.put( NamedEntityGraph.class, "named-entity-graph" ); + annotationToXml.put( NamedEntityGraphs.class, "named-entity-graph" ); annotationToXml.put( NamedQuery.class, "named-query" ); annotationToXml.put( NamedQueries.class, "named-query" ); annotationToXml.put( NamedNativeQuery.class, "named-native-query" ); annotationToXml.put( NamedNativeQueries.class, "named-native-query" ); + annotationToXml.put( NamedStoredProcedureQuery.class, "named-stored-procedure-query" ); + annotationToXml.put( NamedStoredProcedureQueries.class, "named-stored-procedure-query" ); annotationToXml.put( SqlResultSetMapping.class, "sql-result-set-mapping" ); annotationToXml.put( SqlResultSetMappings.class, "sql-result-set-mapping" ); annotationToXml.put( ExcludeDefaultListeners.class, "exclude-default-listeners" ); @@ -360,6 +369,8 @@ public class JPAOverriddenAnnotationReader implements AnnotationReader { addIfNotNull( annotationList, getTableGenerator( tree, defaults ) ); addIfNotNull( annotationList, getNamedQueries( tree, defaults ) ); addIfNotNull( annotationList, getNamedNativeQueries( tree, defaults ) ); + addIfNotNull( annotationList, getNamedStoredProcedureQueries( tree, defaults ) ); + addIfNotNull( annotationList, getNamedEntityGraphs( tree, defaults ) ); addIfNotNull( annotationList, getSqlResultSetMappings( tree, defaults ) ); addIfNotNull( annotationList, getExcludeDefaultListeners( tree, defaults ) ); addIfNotNull( annotationList, getExcludeSuperclassListeners( tree, defaults ) ); @@ -1746,6 +1757,64 @@ public class JPAOverriddenAnnotationReader implements AnnotationReader { } } + public static List buildNamedEntityGraph(Element element, XMLContext.Default defaults) { + if ( element == null ) { + return new ArrayList(); + } + List namedEntityGraphList = new ArrayList(); + List namedEntityGraphElements = element.elements( "named-entity-graph" ); + for ( Element subElement : namedEntityGraphElements ) { + AnnotationDescriptor ann = new AnnotationDescriptor( NamedEntityGraph.class ); + copyStringAttribute( ann, subElement, "name", false ); + copyBooleanAttribute( ann, subElement, "include-all-attributes" ); + bindNamedAttributeNodes( subElement, ann ); + + List subgraphNodes = subElement.elements( "subgraph" ); + bindNamedSubgraph( defaults, ann, subgraphNodes ); + List subclassSubgraphNodes = subElement.elements( "subclass-subgraph" ); + bindNamedSubgraph( defaults, ann, subclassSubgraphNodes ); + namedEntityGraphList.add( (NamedEntityGraph) AnnotationFactory.create( ann ) ); + } + //TODO + return namedEntityGraphList; + } + + private static void bindNamedSubgraph(XMLContext.Default defaults, AnnotationDescriptor ann, List subgraphNodes) { + List annSubgraphNodes = new ArrayList( ); + for(Element subgraphNode : subgraphNodes){ + AnnotationDescriptor annSubgraphNode = new AnnotationDescriptor( NamedSubgraph.class ); + copyStringAttribute( annSubgraphNode, subgraphNode, "name", true ); + String clazzName = subgraphNode.attributeValue( "class" ); + Class clazz; + try { + clazz = ReflectHelper.classForName( + XMLContext.buildSafeClassName( clazzName, defaults ), + JPAOverriddenAnnotationReader.class + ); + } + catch ( ClassNotFoundException e ) { + throw new AnnotationException( "Unable to find entity-class: " + clazzName, e ); + } + annSubgraphNode.setValue( "type", clazz ); + bindNamedAttributeNodes(subgraphNode, annSubgraphNode); + annSubgraphNodes.add( (NamedSubgraph) AnnotationFactory.create( annSubgraphNode ) ); + } + ann.setValue( "subgraphs", annSubgraphNodes.toArray( new NamedSubgraph[annSubgraphNodes.size()] ) ); + } + + private static void bindNamedAttributeNodes(Element subElement, AnnotationDescriptor ann) { + List namedAttributeNodes = subElement.elements("named-attribute-node"); + List annNamedAttributeNodes = new ArrayList( ); + for(Element namedAttributeNode : namedAttributeNodes){ + AnnotationDescriptor annNamedAttributeNode = new AnnotationDescriptor( NamedAttributeNode.class ); + copyStringAttribute( annNamedAttributeNode, namedAttributeNode, "value", true ); + copyStringAttribute( annNamedAttributeNode, namedAttributeNode, "subgraph", false ); + copyStringAttribute( annNamedAttributeNode, namedAttributeNode, "key-subgraph", false ); + annNamedAttributeNodes.add( (NamedAttributeNode) AnnotationFactory.create( annNamedAttributeNode ) ); + } + ann.setValue( "attributeNodes", annNamedAttributeNodes.toArray( new NamedAttributeNode[annNamedAttributeNodes.size()] ) ); + } + public static List buildNamedStoreProcedureQueries(Element element, XMLContext.Default defaults) { if ( element == null ) { return new ArrayList(); @@ -1820,7 +1889,7 @@ public class JPAOverriddenAnnotationReader implements AnnotationReader { buildQueryHints( elements, ann ); namedStoredProcedureQueries.add( (NamedStoredProcedureQuery) AnnotationFactory.create( ann ) ); } - return namedStoredProcedureElements; + return namedStoredProcedureQueries; } @@ -1953,6 +2022,84 @@ public class JPAOverriddenAnnotationReader implements AnnotationReader { } } + private NamedEntityGraphs getNamedEntityGraphs(Element tree, XMLContext.Default defaults) { + List queries = buildNamedEntityGraph( tree, defaults ); + if ( defaults.canUseJavaAnnotations() ) { + NamedEntityGraph annotation = getJavaAnnotation( NamedEntityGraph.class ); + addNamedEntityGraphIfNeeded( annotation, queries ); + NamedEntityGraphs annotations = getJavaAnnotation( NamedEntityGraphs.class ); + if ( annotations != null ) { + for ( NamedEntityGraph current : annotations.value() ) { + addNamedEntityGraphIfNeeded( current, queries ); + } + } + } + if ( queries.size() > 0 ) { + AnnotationDescriptor ad = new AnnotationDescriptor( NamedEntityGraphs.class ); + ad.setValue( "value", queries.toArray( new NamedEntityGraph[queries.size()] ) ); + return AnnotationFactory.create( ad ); + } + else { + return null; + } + } + + private void addNamedEntityGraphIfNeeded(NamedEntityGraph annotation, List queries) { + if ( annotation != null ) { + String queryName = annotation.name(); + boolean present = false; + for ( NamedEntityGraph current : queries ) { + if ( current.name().equals( queryName ) ) { + present = true; + break; + } + } + if ( !present ) { + queries.add( annotation ); + } + } + + } + + private NamedStoredProcedureQueries getNamedStoredProcedureQueries(Element tree, XMLContext.Default defaults) { + List queries = buildNamedStoreProcedureQueries( tree, defaults ); + if ( defaults.canUseJavaAnnotations() ) { + NamedStoredProcedureQuery annotation = getJavaAnnotation( NamedStoredProcedureQuery.class ); + addNamedStoredProcedureQueryIfNeeded( annotation, queries ); + NamedStoredProcedureQueries annotations = getJavaAnnotation( NamedStoredProcedureQueries.class ); + if ( annotations != null ) { + for ( NamedStoredProcedureQuery current : annotations.value() ) { + addNamedStoredProcedureQueryIfNeeded( current, queries ); + } + } + } + if ( queries.size() > 0 ) { + AnnotationDescriptor ad = new AnnotationDescriptor( NamedStoredProcedureQueries.class ); + ad.setValue( "value", queries.toArray( new NamedStoredProcedureQuery[queries.size()] ) ); + return AnnotationFactory.create( ad ); + } + else { + return null; + } + } + + private void addNamedStoredProcedureQueryIfNeeded(NamedStoredProcedureQuery annotation, List queries) { + if ( annotation != null ) { + String queryName = annotation.name(); + boolean present = false; + for ( NamedStoredProcedureQuery current : queries ) { + if ( current.name().equals( queryName ) ) { + present = true; + break; + } + } + if ( !present ) { + queries.add( annotation ); + } + } + } + + private NamedNativeQueries getNamedNativeQueries(Element tree, XMLContext.Default defaults) { List queries = (List) buildNamedQueries( tree, true, defaults ); if ( defaults.canUseJavaAnnotations() ) { 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/AbstractNamedEntityGraphTest.java similarity index 88% rename from hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/graphs/named/basic/BasicNamedEntityGraphTest.java rename to hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/graphs/named/basic/AbstractNamedEntityGraphTest.java index b6109b4a3a..ece15e61d4 100644 --- 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/AbstractNamedEntityGraphTest.java @@ -34,12 +34,7 @@ import static junit.framework.Assert.assertNotNull; /** * @author Steve Ebersole */ -public class BasicNamedEntityGraphTest extends BaseEntityManagerFunctionalTestCase { - @Override - protected Class[] getAnnotatedClasses() { - return new Class[] { Person.class }; - } - +public abstract class AbstractNamedEntityGraphTest extends BaseEntityManagerFunctionalTestCase { @Test public void testIt() { EntityGraph graph = getOrCreateEntityManager().getEntityGraph( "Person" ); diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/graphs/named/basic/BasicAnnNamedEntityGraphTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/graphs/named/basic/BasicAnnNamedEntityGraphTest.java new file mode 100644 index 0000000000..96720c82fd --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/graphs/named/basic/BasicAnnNamedEntityGraphTest.java @@ -0,0 +1,35 @@ +/* + * 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; + +/** + * @author Strong Liu + */ +public class BasicAnnNamedEntityGraphTest extends AbstractNamedEntityGraphTest{ + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Person.class }; + } + +} diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/graphs/named/basic/BasicOrmNamedEntityGraphTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/graphs/named/basic/BasicOrmNamedEntityGraphTest.java new file mode 100644 index 0000000000..fe139e8797 --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/graphs/named/basic/BasicOrmNamedEntityGraphTest.java @@ -0,0 +1,34 @@ +/* + * 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; + +/** + * @author Strong Liu + */ +public class BasicOrmNamedEntityGraphTest extends AbstractNamedEntityGraphTest{ + @Override + public String[] getEjb3DD() { + return new String[]{"org/hibernate/jpa/test/graphs/named/basic/orm.xml"}; + } +} 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 index dc9a55bf35..b3e4b93b29 100644 --- 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 @@ -31,8 +31,8 @@ import javax.persistence.NamedEntityGraph; * @author Steve Ebersole */ @Entity(name = "Person") -@NamedEntityGraph() +@NamedEntityGraph public class Person { @Id - private Long id; + public Long id; } diff --git a/hibernate-entitymanager/src/test/resources/org/hibernate/jpa/test/graphs/named/basic/orm.xml b/hibernate-entitymanager/src/test/resources/org/hibernate/jpa/test/graphs/named/basic/orm.xml new file mode 100644 index 0000000000..e2486e7bfb --- /dev/null +++ b/hibernate-entitymanager/src/test/resources/org/hibernate/jpa/test/graphs/named/basic/orm.xml @@ -0,0 +1,15 @@ + + + org.hibernate.jpa.test.graphs.named.basic + + + + + + + + + \ No newline at end of file From 04c8e8c87ae3c1a49e5920ae9e5208f7c07c20a6 Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Tue, 21 May 2013 14:47:26 -0400 Subject: [PATCH 08/36] HHH-8112 OSGi quickstart tutorial projects --- .../tutorials/osgi/datasource-h2.xml | 21 ++++ .../tutorials/osgi/managed-jpa/features.xml | 72 ++++++++++++ .../tutorials/osgi/managed-jpa/pom.xml | 63 ++++++++++ .../hibernate/osgitest/DataPointService.java | 37 ++++++ .../osgitest/DataPointServiceImpl.java | 58 +++++++++ .../osgitest/command/AddCommand.java | 44 +++++++ .../osgitest/command/DeleteAllCommand.java | 37 ++++++ .../osgitest/command/GetAllCommand.java | 43 +++++++ .../hibernate/osgitest/entity/DataPoint.java | 53 +++++++++ .../main/resources/META-INF/persistence.xml | 15 +++ .../OSGI-INF/blueprint/blueprint.xml | 50 ++++++++ .../tutorials/osgi/unmanaged-jpa/features.xml | 42 +++++++ .../tutorials/osgi/unmanaged-jpa/pom.xml | 74 ++++++++++++ .../hibernate/osgitest/DataPointService.java | 41 +++++++ .../osgitest/DataPointServiceImpl.java | 76 ++++++++++++ .../org/hibernate/osgitest/HibernateUtil.java | 57 +++++++++ .../osgitest/command/AddCommand.java | 44 +++++++ .../osgitest/command/DeleteAllCommand.java | 37 ++++++ .../osgitest/command/GetAllCommand.java | 43 +++++++ .../osgitest/command/UpdateCommand.java | 47 ++++++++ .../hibernate/osgitest/entity/DataPoint.java | 53 +++++++++ .../main/resources/META-INF/persistence.xml | 18 +++ .../OSGI-INF/blueprint/blueprint.xml | 50 ++++++++ .../osgi/unmanaged-native/features.xml | 72 ++++++++++++ .../tutorials/osgi/unmanaged-native/pom.xml | 82 +++++++++++++ .../hibernate/osgitest/DataPointService.java | 45 +++++++ .../osgitest/DataPointServiceImpl.java | 89 ++++++++++++++ .../org/hibernate/osgitest/HibernateUtil.java | 53 +++++++++ .../osgitest/command/AddCommand.java | 44 +++++++ .../osgitest/command/DeleteAllCommand.java | 37 ++++++ .../osgitest/command/GetAllCommand.java | 43 +++++++ .../osgitest/command/GetRevisionsCommand.java | 48 ++++++++ .../osgitest/command/UpdateCommand.java | 47 ++++++++ .../hibernate/osgitest/entity/DataPoint.java | 56 +++++++++ .../OSGI-INF/blueprint/blueprint.xml | 55 +++++++++ .../src/main/resources/ehcache.xml | 111 ++++++++++++++++++ .../src/main/resources/hibernate.cfg.xml | 46 ++++++++ .../src/main/resources/pool-one.properties | 7 ++ .../main/docbook/quickstart/tutorials/pom.xml | 1 + 39 files changed, 1911 insertions(+) create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/datasource-h2.xml create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/features.xml create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/pom.xml create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/java/org/hibernate/osgitest/DataPointService.java create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/java/org/hibernate/osgitest/DataPointServiceImpl.java create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/java/org/hibernate/osgitest/command/AddCommand.java create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/java/org/hibernate/osgitest/command/DeleteAllCommand.java create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/java/org/hibernate/osgitest/command/GetAllCommand.java create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/java/org/hibernate/osgitest/entity/DataPoint.java create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/resources/META-INF/persistence.xml create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/resources/OSGI-INF/blueprint/blueprint.xml create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/features.xml create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/pom.xml create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/java/org/hibernate/osgitest/DataPointService.java create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/java/org/hibernate/osgitest/DataPointServiceImpl.java create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/java/org/hibernate/osgitest/HibernateUtil.java create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/java/org/hibernate/osgitest/command/AddCommand.java create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/java/org/hibernate/osgitest/command/DeleteAllCommand.java create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/java/org/hibernate/osgitest/command/GetAllCommand.java create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/java/org/hibernate/osgitest/command/UpdateCommand.java create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/java/org/hibernate/osgitest/entity/DataPoint.java create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/resources/META-INF/persistence.xml create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/resources/OSGI-INF/blueprint/blueprint.xml create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/features.xml create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/pom.xml create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/DataPointService.java create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/DataPointServiceImpl.java create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/HibernateUtil.java create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/command/AddCommand.java create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/command/DeleteAllCommand.java create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/command/GetAllCommand.java create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/command/GetRevisionsCommand.java create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/command/UpdateCommand.java create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/entity/DataPoint.java create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/resources/OSGI-INF/blueprint/blueprint.xml create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/resources/ehcache.xml create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/resources/hibernate.cfg.xml create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/resources/pool-one.properties diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/datasource-h2.xml b/documentation/src/main/docbook/quickstart/tutorials/osgi/datasource-h2.xml new file mode 100644 index 0000000000..76ede3c842 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/datasource-h2.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/features.xml b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/features.xml new file mode 100644 index 0000000000..47ac76c30d --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/features.xml @@ -0,0 +1,72 @@ + + + + + karaf-framework + + + + aries.transaction.recoverable = true + aries.transaction.timeout = 600 + aries.transaction.howl.logFileDir = ${karaf.data}/txlog + aries.transaction.howl.maxLogFiles = 2 + aries.transaction.howl.maxBlocksPerFile = 512 + aries.transaction.howl.bufferSizeKBytes = 4 + + mvn:org.apache.geronimo.specs/geronimo-jta_1.1_spec/1.1.1 + mvn:org.apache.aries.transaction/org.apache.aries.transaction.blueprint/1.0.0 + mvn:org.apache.aries.transaction/org.apache.aries.transaction.manager/1.0.1 + + + mvn:org.hibernate.javax.persistence/hibernate-jpa-2.1-api/1.0.0-SNAPSHOT + mvn:org.apache.aries/org.apache.aries.util/1.0.0 + mvn:org.apache.aries.jpa/org.apache.aries.jpa.api/1.0.1-SNAPSHOT + mvn:org.apache.aries.jpa/org.apache.aries.jpa.blueprint.aries/1.0.2-SNAPSHOT + mvn:org.apache.aries.jpa/org.apache.aries.jpa.container/1.0.1-SNAPSHOT + mvn:org.apache.aries.jpa/org.apache.aries.jpa.container.context/1.0.2-SNAPSHOT + + + mvn:org.apache.aries.jndi/org.apache.aries.jndi.api/1.0.0 + mvn:org.apache.aries.jndi/org.apache.aries.jndi.core/1.0.0 + mvn:org.apache.aries.jndi/org.apache.aries.jndi.rmi/1.0.0 + mvn:org.apache.aries.jndi/org.apache.aries.jndi.url/1.0.0 + mvn:org.apache.aries.jndi/org.apache.aries.jndi.legacy.support/1.0.0 + + + mvn:commons-collections/commons-collections/3.2.1 + mvn:commons-pool/commons-pool/1.5.4 + mvn:commons-dbcp/commons-dbcp/1.4 + mvn:commons-lang/commons-lang/2.6 + wrap:mvn:net.sourceforge.serp/serp/1.13.1 + + mvn:com.h2database/h2/1.3.170 + blueprint:file:/[PATH]/datasource-h2.xml + + + mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.antlr/2.7.7_5 + mvn:org.jboss.javassist/com.springsource.javassist/3.15.0.GA + mvn:org.apache.servicemix.specs/org.apache.servicemix.specs.jsr303-api-1.0.0/2.2.0 + mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.ant/1.8.2_2 + mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.dom4j/1.6.1_5 + + + wrap:mvn:org.jboss/jandex/1.1.0.Alpha1 + + wrap:mvn:org.hibernate.common/hibernate-commons-annotations/4.0.2.Final + mvn:com.fasterxml/classmate/0.5.4 + mvn:org.jboss.logging/jboss-logging/3.1.0.GA + + mvn:org.hibernate/hibernate-core/4.3.0-SNAPSHOT + mvn:org.hibernate/hibernate-entitymanager/4.3.0-SNAPSHOT + + + mvn:org.hibernate.osgi/managed-jpa/1.0.0 + + mvn:org.hibernate/hibernate-osgi/4.3.0-SNAPSHOT + + diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/pom.xml b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/pom.xml new file mode 100644 index 0000000000..35f7009ab9 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/pom.xml @@ -0,0 +1,63 @@ + + 4.0.0 + org.hibernate.osgi + managed-jpa + 1.0.0 + bundle + + + + org.hibernate.javax.persistence + hibernate-jpa-2.1-api + 1.0.0-SNAPSHOT + + + org.osgi + org.osgi.core + 4.3.1 + + + org.osgi + org.osgi.enterprise + 4.2.0 + + + org.apache.karaf.shell + org.apache.karaf.shell.console + 2.3.0 + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + ${pom.groupId}.${pom.artifactId} + ${pom.artifactId} + 1.0.0 + + org.hibernate.osgitest, + org.hibernate.osgitest.entity + + + org.apache.felix.service.command, + org.apache.felix.gogo.commands, + org.apache.karaf.shell.console, + org.apache.karaf.shell.commands, + org.hibernate.proxy, + javassist.util.proxy, + javax.persistence;version="[1.0.0,2.1.0]", + * + + META-INF/persistence.xml + + + + + + diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/java/org/hibernate/osgitest/DataPointService.java b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/java/org/hibernate/osgitest/DataPointService.java new file mode 100644 index 0000000000..bdeaace115 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/java/org/hibernate/osgitest/DataPointService.java @@ -0,0 +1,37 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * JBoss, Home of Professional Open Source + * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @authors tag. All rights reserved. + * See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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, v. 2.1. + * This program is distributed in the hope that it will be useful, but WITHOUT A + * 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, + * v.2.1 along with this distribution; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ +package org.hibernate.osgitest; + +import java.util.List; + +import org.hibernate.osgitest.entity.DataPoint; + +/** + * @author Brett Meyer + */ +public interface DataPointService { + + public void add(DataPoint dp); + + public List getAll(); + + public void deleteAll(); +} diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/java/org/hibernate/osgitest/DataPointServiceImpl.java b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/java/org/hibernate/osgitest/DataPointServiceImpl.java new file mode 100644 index 0000000000..f324889603 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/java/org/hibernate/osgitest/DataPointServiceImpl.java @@ -0,0 +1,58 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * JBoss, Home of Professional Open Source + * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @authors tag. All rights reserved. + * See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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, v. 2.1. + * This program is distributed in the hope that it will be useful, but WITHOUT A + * 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, + * v.2.1 along with this distribution; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ +package org.hibernate.osgitest; + +import java.util.List; + +import javax.persistence.EntityManager; + +import org.hibernate.osgitest.entity.DataPoint; + +/** + * @author Brett Meyer + */ +public class DataPointServiceImpl implements DataPointService { + + private EntityManager entityManager; + + public void add(DataPoint dp) { + entityManager.persist( dp ); + entityManager.flush(); + } + + public List getAll() { + return entityManager.createQuery( "select d from DataPoint d", DataPoint.class ).getResultList(); + } + + public void deleteAll() { + entityManager.createQuery( "delete from DataPoint" ).executeUpdate(); + entityManager.flush(); + } + + public EntityManager getEntityManager() { + return entityManager; + } + + public void setEntityManager(EntityManager entityManager) { + this.entityManager = entityManager; + } + +} diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/java/org/hibernate/osgitest/command/AddCommand.java b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/java/org/hibernate/osgitest/command/AddCommand.java new file mode 100644 index 0000000000..9e9844c331 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/java/org/hibernate/osgitest/command/AddCommand.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.hibernate.osgitest.command; + +import org.apache.felix.gogo.commands.Action; +import org.apache.felix.gogo.commands.Argument; +import org.apache.felix.gogo.commands.Command; +import org.apache.felix.service.command.CommandSession; +import org.hibernate.osgitest.DataPointService; +import org.hibernate.osgitest.entity.DataPoint; + +@Command(scope = "dp", name = "add") +public class AddCommand implements Action { + @Argument(index=0, name="Name", required=true, description="Name", multiValued=false) + String name; + + private DataPointService dpService; + + public void setDpService(DataPointService dpService) { + this.dpService = dpService; + } + + public Object execute(CommandSession session) throws Exception { + DataPoint dp = new DataPoint(); + dp.setName( name ); + dpService.add( dp ); + return null; + } + +} diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/java/org/hibernate/osgitest/command/DeleteAllCommand.java b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/java/org/hibernate/osgitest/command/DeleteAllCommand.java new file mode 100644 index 0000000000..d6c4ccf676 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/java/org/hibernate/osgitest/command/DeleteAllCommand.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.hibernate.osgitest.command; + +import org.apache.felix.gogo.commands.Action; +import org.apache.felix.gogo.commands.Command; +import org.apache.felix.service.command.CommandSession; +import org.hibernate.osgitest.DataPointService; + +@Command(scope = "dp", name = "deleteAll") +public class DeleteAllCommand implements Action { +private DataPointService dpService; + + public void setDpService(DataPointService dpService) { + this.dpService = dpService; + } + + public Object execute(CommandSession session) throws Exception { + dpService.deleteAll(); + return null; + } + +} diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/java/org/hibernate/osgitest/command/GetAllCommand.java b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/java/org/hibernate/osgitest/command/GetAllCommand.java new file mode 100644 index 0000000000..ebdac60ea2 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/java/org/hibernate/osgitest/command/GetAllCommand.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.hibernate.osgitest.command; + +import java.util.List; + +import org.apache.felix.gogo.commands.Action; +import org.apache.felix.gogo.commands.Command; +import org.apache.felix.service.command.CommandSession; +import org.hibernate.osgitest.DataPointService; +import org.hibernate.osgitest.entity.DataPoint; + +@Command(scope = "dp", name = "getAll") +public class GetAllCommand implements Action { + private DataPointService dpService; + + public void setDpService(DataPointService dpService) { + this.dpService = dpService; + } + + public Object execute(CommandSession session) throws Exception { + List dps = dpService.getAll(); + for (DataPoint dp : dps) { + System.out.println(dp.getId() + ", " + dp.getName()); + } + return null; + } + +} diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/java/org/hibernate/osgitest/entity/DataPoint.java b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/java/org/hibernate/osgitest/entity/DataPoint.java new file mode 100644 index 0000000000..2a566d8829 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/java/org/hibernate/osgitest/entity/DataPoint.java @@ -0,0 +1,53 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * JBoss, Home of Professional Open Source + * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @authors tag. All rights reserved. + * See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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, v. 2.1. + * This program is distributed in the hope that it will be useful, but WITHOUT A + * 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, + * v.2.1 along with this distribution; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ +package org.hibernate.osgitest.entity; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +/** + * @author Brett Meyer + */ +@Entity +public class DataPoint { + @Id + @GeneratedValue + private long id; + + private String name; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/resources/META-INF/persistence.xml b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/resources/META-INF/persistence.xml new file mode 100644 index 0000000000..5db3a42357 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/resources/META-INF/persistence.xml @@ -0,0 +1,15 @@ + + + + osgi:service/javax.sql.DataSource/(osgi.jndi.service.name=jdbc/h2ds) + + + + + + + + + \ No newline at end of file diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/resources/OSGI-INF/blueprint/blueprint.xml b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/resources/OSGI-INF/blueprint/blueprint.xml new file mode 100644 index 0000000000..d7f2a95a47 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/resources/OSGI-INF/blueprint/blueprint.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/features.xml b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/features.xml new file mode 100644 index 0000000000..9e31bed850 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/features.xml @@ -0,0 +1,42 @@ + + + + + karaf-framework + + + mvn:org.apache.geronimo.specs/geronimo-jta_1.1_spec/1.1.1 + + + mvn:org.hibernate.javax.persistence/hibernate-jpa-2.1-api/1.0.0-SNAPSHOT + + + mvn:commons-collections/commons-collections/3.2.1 + mvn:commons-pool/commons-pool/1.5.4 + mvn:commons-dbcp/commons-dbcp/1.4 + mvn:commons-lang/commons-lang/2.6 + wrap:mvn:net.sourceforge.serp/serp/1.13.1 + + mvn:com.h2database/h2/1.3.170 + + + mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.antlr/2.7.7_5 + mvn:org.jboss.javassist/com.springsource.javassist/3.15.0.GA + mvn:org.apache.servicemix.specs/org.apache.servicemix.specs.jsr303-api-1.0.0/2.2.0 + mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.ant/1.8.2_2 + mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.dom4j/1.6.1_5 + + + wrap:mvn:org.hibernate.common/hibernate-commons-annotations/4.0.2.Final + wrap:mvn:org.jboss/jandex/1.1.0.Alpha1 + + mvn:com.fasterxml/classmate/0.5.4 + mvn:org.jboss.logging/jboss-logging/3.1.0.GA + + mvn:org.hibernate/hibernate-core/4.3.0-SNAPSHOT + mvn:org.hibernate/hibernate-entitymanager/4.3.0-SNAPSHOT + mvn:org.hibernate/hibernate-osgi/4.3.0-SNAPSHOT + + mvn:org.hibernate.osgi/unmanaged-jpa/1.0.0 + + diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/pom.xml b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/pom.xml new file mode 100644 index 0000000000..c09f30f155 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/pom.xml @@ -0,0 +1,74 @@ + + 4.0.0 + org.hibernate.osgi + unmanaged-jpa + 1.0.0 + bundle + + + + org.hibernate.javax.persistence + hibernate-jpa-2.1-api + 1.0.0-SNAPSHOT + + + org.osgi + org.osgi.core + 4.3.1 + + + org.osgi + org.osgi.enterprise + 4.2.0 + + + org.apache.karaf.shell + org.apache.karaf.shell.console + 2.3.0 + + + org.hibernate + hibernate-entitymanager + 4.3.0-SNAPSHOT + + + com.h2database + h2 + 1.3.170 + + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + ${pom.groupId}.${pom.artifactId} + ${pom.artifactId} + 1.0.0 + + org.hibernate.osgitest, + org.hibernate.osgitest.entity + + + org.apache.felix.service.command, + org.apache.felix.gogo.commands, + org.apache.karaf.shell.console, + org.apache.karaf.shell.commands, + org.h2, + org.hibernate.proxy, + javassist.util.proxy, + javax.persistence;version="[1.0.0,2.1.0]", + * + + + + + + + diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/java/org/hibernate/osgitest/DataPointService.java b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/java/org/hibernate/osgitest/DataPointService.java new file mode 100644 index 0000000000..de8c960c1f --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/java/org/hibernate/osgitest/DataPointService.java @@ -0,0 +1,41 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * JBoss, Home of Professional Open Source + * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @authors tag. All rights reserved. + * See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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, v. 2.1. + * This program is distributed in the hope that it will be useful, but WITHOUT A + * 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, + * v.2.1 along with this distribution; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ +package org.hibernate.osgitest; + +import java.util.List; + +import org.hibernate.osgitest.entity.DataPoint; + +/** + * @author Brett Meyer + */ +public interface DataPointService { + + public void add(DataPoint dp); + + public void update(DataPoint dp); + + public DataPoint get(long id); + + public List getAll(); + + public void deleteAll(); +} diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/java/org/hibernate/osgitest/DataPointServiceImpl.java b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/java/org/hibernate/osgitest/DataPointServiceImpl.java new file mode 100644 index 0000000000..e4b748daf4 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/java/org/hibernate/osgitest/DataPointServiceImpl.java @@ -0,0 +1,76 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * JBoss, Home of Professional Open Source + * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @authors tag. All rights reserved. + * See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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, v. 2.1. + * This program is distributed in the hope that it will be useful, but WITHOUT A + * 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, + * v.2.1 along with this distribution; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ +package org.hibernate.osgitest; + +import java.util.List; + +import javax.persistence.EntityManager; + +import org.hibernate.osgitest.entity.DataPoint; + +/** + * @author Brett Meyer + */ +public class DataPointServiceImpl implements DataPointService { + + public void add(DataPoint dp) { + EntityManager em = HibernateUtil.getEntityManager(); + em.getTransaction().begin(); + em.persist( dp ); + em.getTransaction().commit(); + em.close(); + } + + public void update(DataPoint dp) { + EntityManager em = HibernateUtil.getEntityManager(); + em.getTransaction().begin(); + em.merge( dp ); + em.getTransaction().commit(); + em.close(); + } + + public DataPoint get(long id) { + EntityManager em = HibernateUtil.getEntityManager(); + em.getTransaction().begin(); + DataPoint dp = (DataPoint) em.createQuery( "from DataPoint dp where dp.id=" + id ).getSingleResult(); + em.getTransaction().commit(); + em.close(); + return dp; + } + + public List getAll() { + EntityManager em = HibernateUtil.getEntityManager(); + em.getTransaction().begin(); + List list = em.createQuery( "from DataPoint" ).getResultList(); + em.getTransaction().commit(); + em.close(); + return list; + } + + public void deleteAll() { + EntityManager em = HibernateUtil.getEntityManager(); + em.getTransaction().begin(); + em.createQuery( "delete from DataPoint" ).executeUpdate(); + em.getTransaction().commit(); + em.close(); + } + +} diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/java/org/hibernate/osgitest/HibernateUtil.java b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/java/org/hibernate/osgitest/HibernateUtil.java new file mode 100644 index 0000000000..66442637b3 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/java/org/hibernate/osgitest/HibernateUtil.java @@ -0,0 +1,57 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * JBoss, Home of Professional Open Source + * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @authors tag. All rights reserved. + * See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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, v. 2.1. + * This program is distributed in the hope that it will be useful, but WITHOUT A + * 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, + * v.2.1 along with this distribution; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ +package org.hibernate.osgitest; + +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; +import javax.persistence.spi.PersistenceProvider; + +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceReference; + +/** + * @author Brett Meyer + */ + +public class HibernateUtil { + + private static EntityManagerFactory emf; + + public static EntityManager getEntityManager() { + return getEntityManagerFactory().createEntityManager(); + } + + private static EntityManagerFactory getEntityManagerFactory() { + if ( emf == null ) { + Bundle thisBundle = FrameworkUtil.getBundle( HibernateUtil.class ); + // Could get this by wiring up OsgiTestBundleActivator as well. + BundleContext context = thisBundle.getBundleContext(); + + ServiceReference serviceReference = context.getServiceReference( PersistenceProvider.class.getName() ); + PersistenceProvider persistenceProvider = (PersistenceProvider) context.getService( serviceReference ); + + emf = persistenceProvider.createEntityManagerFactory( "unmanaged-jpa", null ); + } + return emf; + } +} diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/java/org/hibernate/osgitest/command/AddCommand.java b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/java/org/hibernate/osgitest/command/AddCommand.java new file mode 100644 index 0000000000..c1b906c012 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/java/org/hibernate/osgitest/command/AddCommand.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.hibernate.osgitest.command; + +import org.apache.felix.gogo.commands.Action; +import org.apache.felix.gogo.commands.Argument; +import org.apache.felix.gogo.commands.Command; +import org.apache.felix.service.command.CommandSession; +import org.hibernate.osgitest.DataPointService; +import org.hibernate.osgitest.entity.DataPoint; + +@Command(scope = "dp", name = "addJPA") +public class AddCommand implements Action { + @Argument(index=0, name="Name", required=true, description="Name", multiValued=false) + String name; + + private DataPointService dpService; + + public void setDpService(DataPointService dpService) { + this.dpService = dpService; + } + + public Object execute(CommandSession session) throws Exception { + DataPoint dp = new DataPoint(); + dp.setName( name ); + dpService.add( dp ); + return null; + } + +} diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/java/org/hibernate/osgitest/command/DeleteAllCommand.java b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/java/org/hibernate/osgitest/command/DeleteAllCommand.java new file mode 100644 index 0000000000..0d404ae1a1 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/java/org/hibernate/osgitest/command/DeleteAllCommand.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.hibernate.osgitest.command; + +import org.apache.felix.gogo.commands.Action; +import org.apache.felix.gogo.commands.Command; +import org.apache.felix.service.command.CommandSession; +import org.hibernate.osgitest.DataPointService; + +@Command(scope = "dp", name = "deleteAllJPA") +public class DeleteAllCommand implements Action { +private DataPointService dpService; + + public void setDpService(DataPointService dpService) { + this.dpService = dpService; + } + + public Object execute(CommandSession session) throws Exception { + dpService.deleteAll(); + return null; + } + +} diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/java/org/hibernate/osgitest/command/GetAllCommand.java b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/java/org/hibernate/osgitest/command/GetAllCommand.java new file mode 100644 index 0000000000..6873e46303 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/java/org/hibernate/osgitest/command/GetAllCommand.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.hibernate.osgitest.command; + +import java.util.List; + +import org.apache.felix.gogo.commands.Action; +import org.apache.felix.gogo.commands.Command; +import org.apache.felix.service.command.CommandSession; +import org.hibernate.osgitest.DataPointService; +import org.hibernate.osgitest.entity.DataPoint; + +@Command(scope = "dp", name = "getAllJPA") +public class GetAllCommand implements Action { + private DataPointService dpService; + + public void setDpService(DataPointService dpService) { + this.dpService = dpService; + } + + public Object execute(CommandSession session) throws Exception { + List dps = dpService.getAll(); + for (DataPoint dp : dps) { + System.out.println(dp.getId() + ", " + dp.getName()); + } + return null; + } + +} diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/java/org/hibernate/osgitest/command/UpdateCommand.java b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/java/org/hibernate/osgitest/command/UpdateCommand.java new file mode 100644 index 0000000000..5de8983dd9 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/java/org/hibernate/osgitest/command/UpdateCommand.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.hibernate.osgitest.command; + +import org.apache.felix.gogo.commands.Action; +import org.apache.felix.gogo.commands.Argument; +import org.apache.felix.gogo.commands.Command; +import org.apache.felix.service.command.CommandSession; +import org.hibernate.osgitest.DataPointService; +import org.hibernate.osgitest.entity.DataPoint; + +@Command(scope = "dp", name = "updateJPA") +public class UpdateCommand implements Action { + @Argument(index=0, name="Id", required=true, description="Id", multiValued=false) + String id; + + @Argument(index=1, name="Name", required=true, description="Name", multiValued=false) + String name; + + private DataPointService dpService; + + public void setDpService(DataPointService dpService) { + this.dpService = dpService; + } + + public Object execute(CommandSession session) throws Exception { + DataPoint dp = dpService.get( Long.valueOf( id ) ); + dp.setName( name ); + dpService.update( dp ); + return null; + } + +} diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/java/org/hibernate/osgitest/entity/DataPoint.java b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/java/org/hibernate/osgitest/entity/DataPoint.java new file mode 100644 index 0000000000..2a566d8829 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/java/org/hibernate/osgitest/entity/DataPoint.java @@ -0,0 +1,53 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * JBoss, Home of Professional Open Source + * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @authors tag. All rights reserved. + * See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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, v. 2.1. + * This program is distributed in the hope that it will be useful, but WITHOUT A + * 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, + * v.2.1 along with this distribution; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ +package org.hibernate.osgitest.entity; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +/** + * @author Brett Meyer + */ +@Entity +public class DataPoint { + @Id + @GeneratedValue + private long id; + + private String name; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/resources/META-INF/persistence.xml b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/resources/META-INF/persistence.xml new file mode 100644 index 0000000000..7c7efcae7e --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/resources/META-INF/persistence.xml @@ -0,0 +1,18 @@ + + + + org.hibernate.osgitest.entity.DataPoint + true + + + + + + + + + + + \ No newline at end of file diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/resources/OSGI-INF/blueprint/blueprint.xml b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/resources/OSGI-INF/blueprint/blueprint.xml new file mode 100644 index 0000000000..896737d2e8 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/resources/OSGI-INF/blueprint/blueprint.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/features.xml b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/features.xml new file mode 100644 index 0000000000..e505b7c5a9 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/features.xml @@ -0,0 +1,72 @@ + + + + + karaf-framework + + + mvn:org.apache.geronimo.specs/geronimo-jta_1.1_spec/1.1.1 + + + mvn:org.hibernate.javax.persistence/hibernate-jpa-2.1-api/1.0.0-SNAPSHOT + + + mvn:commons-collections/commons-collections/3.2.1 + mvn:commons-pool/commons-pool/1.5.4 + mvn:commons-dbcp/commons-dbcp/1.4 + mvn:commons-lang/commons-lang/2.6 + wrap:mvn:net.sourceforge.serp/serp/1.13.1 + + mvn:com.h2database/h2/1.3.170 + + + mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.antlr/2.7.7_5 + mvn:org.jboss.javassist/com.springsource.javassist/3.15.0.GA + mvn:org.apache.servicemix.specs/org.apache.servicemix.specs.jsr303-api-1.0.0/2.2.0 + mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.ant/1.8.2_2 + mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.dom4j/1.6.1_5 + + + wrap:mvn:org.hibernate.common/hibernate-commons-annotations/4.0.2.Final + wrap:mvn:org.jboss/jandex/1.1.0.Alpha1 + + + + + + + + mvn:com.fasterxml/classmate/0.5.4 + mvn:org.jboss.logging/jboss-logging/3.1.0.GA + + + + + + + + + + + + mvn:org.hibernate/hibernate-core/4.3.0-SNAPSHOT + + mvn:org.hibernate/hibernate-entitymanager/4.3.0-SNAPSHOT + mvn:org.hibernate/hibernate-envers/4.3.0-SNAPSHOT + + + + + mvn:org.hibernate/hibernate-osgi/4.3.0-SNAPSHOT + + mvn:org.hibernate.osgi/unmanaged-native/1.0.0 + + diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/pom.xml b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/pom.xml new file mode 100644 index 0000000000..bb1b8c1874 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/pom.xml @@ -0,0 +1,82 @@ + + 4.0.0 + org.hibernate.osgi + unmanaged-native + 1.0.0 + bundle + + + + org.hibernate.javax.persistence + hibernate-jpa-2.1-api + 1.0.0-SNAPSHOT + + + org.osgi + org.osgi.core + 4.3.1 + + + org.osgi + org.osgi.enterprise + 4.2.0 + + + org.apache.karaf.shell + org.apache.karaf.shell.console + 2.3.0 + + + org.hibernate + hibernate-core + 4.3.0-SNAPSHOT + + + org.hibernate + hibernate-envers + 4.3.0-SNAPSHOT + + + com.h2database + h2 + 1.3.170 + + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + ${pom.groupId}.${pom.artifactId} + ${pom.artifactId} + 1.0.0 + + org.hibernate.osgitest, + org.hibernate.osgitest.entity + + + org.apache.felix.service.command, + org.apache.felix.gogo.commands, + org.apache.karaf.shell.console, + org.apache.karaf.shell.commands, + org.h2, + org.hibernate, + org.hibernate.cfg, + org.hibernate.service, + org.hibernate.proxy, + javassist.util.proxy, + javax.persistence;version="[1.0.0,2.1.0]", + * + + + + + + + diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/DataPointService.java b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/DataPointService.java new file mode 100644 index 0000000000..ba71b211a6 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/DataPointService.java @@ -0,0 +1,45 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * JBoss, Home of Professional Open Source + * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @authors tag. All rights reserved. + * See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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, v. 2.1. + * This program is distributed in the hope that it will be useful, but WITHOUT A + * 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, + * v.2.1 along with this distribution; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ +package org.hibernate.osgitest; + +import java.util.List; +import java.util.Map; + +import org.hibernate.envers.DefaultRevisionEntity; +import org.hibernate.osgitest.entity.DataPoint; + +/** + * @author Brett Meyer + */ +public interface DataPointService { + + public void add(DataPoint dp); + + public void update(DataPoint dp); + + public DataPoint get(long id); + + public List getAll(); + + public Map getRevisions(long id); + + public void deleteAll(); +} diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/DataPointServiceImpl.java b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/DataPointServiceImpl.java new file mode 100644 index 0000000000..dfa5b66f35 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/DataPointServiceImpl.java @@ -0,0 +1,89 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * JBoss, Home of Professional Open Source + * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @authors tag. All rights reserved. + * See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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, v. 2.1. + * This program is distributed in the hope that it will be useful, but WITHOUT A + * 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, + * v.2.1 along with this distribution; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ +package org.hibernate.osgitest; + +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +import org.hibernate.Session; +import org.hibernate.criterion.Restrictions; +import org.hibernate.envers.AuditReader; +import org.hibernate.envers.AuditReaderFactory; +import org.hibernate.envers.DefaultRevisionEntity; +import org.hibernate.osgitest.entity.DataPoint; + +/** + * @author Brett Meyer + */ +public class DataPointServiceImpl implements DataPointService { + + public void add(DataPoint dp) { + Session s = HibernateUtil.getSession(); + s.getTransaction().begin(); + s.persist( dp ); + s.getTransaction().commit(); + s.close(); + } + + public void update(DataPoint dp) { + Session s = HibernateUtil.getSession(); + s.getTransaction().begin(); + s.update( dp ); + s.getTransaction().commit(); + s.close(); + } + + public DataPoint get(long id) { + Session s = HibernateUtil.getSession(); + s.getTransaction().begin(); + DataPoint dp = (DataPoint) s.createCriteria( DataPoint.class ).add( + Restrictions.eq( "id", id ) ).uniqueResult(); + s.getTransaction().commit(); + s.close(); + return dp; + } + + public List getAll() { + Session s = HibernateUtil.getSession(); + s.getTransaction().begin(); + List list = s.createQuery( "from DataPoint" ).list(); + s.getTransaction().commit(); + s.close(); + return list; + } + + public Map getRevisions(long id) { + Session s = HibernateUtil.getSession(); + AuditReader reader = AuditReaderFactory.get(s); + List revisionNums = reader.getRevisions( DataPoint.class, id ); + return reader.findRevisions( DefaultRevisionEntity.class, new HashSet(revisionNums) ); + } + + public void deleteAll() { + Session s = HibernateUtil.getSession(); + s.getTransaction().begin(); + s.createQuery( "delete from DataPoint" ).executeUpdate(); + s.getTransaction().commit(); + s.close(); + } + +} diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/HibernateUtil.java b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/HibernateUtil.java new file mode 100644 index 0000000000..d44072f157 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/HibernateUtil.java @@ -0,0 +1,53 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * JBoss, Home of Professional Open Source + * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @authors tag. All rights reserved. + * See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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, v. 2.1. + * This program is distributed in the hope that it will be useful, but WITHOUT A + * 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, + * v.2.1 along with this distribution; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ +package org.hibernate.osgitest; + +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceReference; + +/** + * @author Brett Meyer + */ + +public class HibernateUtil { + + private static SessionFactory sf; + + public static Session getSession() { + return getSessionFactory().openSession(); + } + + private static SessionFactory getSessionFactory() { + if ( sf == null ) { + Bundle thisBundle = FrameworkUtil.getBundle( HibernateUtil.class ); + // Could get this by wiring up OsgiTestBundleActivator as well. + BundleContext context = thisBundle.getBundleContext(); + + ServiceReference sr = context.getServiceReference( SessionFactory.class.getName() ); + sf = (SessionFactory) context.getService( sr ); + } + return sf; + } +} diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/command/AddCommand.java b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/command/AddCommand.java new file mode 100644 index 0000000000..9e9844c331 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/command/AddCommand.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.hibernate.osgitest.command; + +import org.apache.felix.gogo.commands.Action; +import org.apache.felix.gogo.commands.Argument; +import org.apache.felix.gogo.commands.Command; +import org.apache.felix.service.command.CommandSession; +import org.hibernate.osgitest.DataPointService; +import org.hibernate.osgitest.entity.DataPoint; + +@Command(scope = "dp", name = "add") +public class AddCommand implements Action { + @Argument(index=0, name="Name", required=true, description="Name", multiValued=false) + String name; + + private DataPointService dpService; + + public void setDpService(DataPointService dpService) { + this.dpService = dpService; + } + + public Object execute(CommandSession session) throws Exception { + DataPoint dp = new DataPoint(); + dp.setName( name ); + dpService.add( dp ); + return null; + } + +} diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/command/DeleteAllCommand.java b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/command/DeleteAllCommand.java new file mode 100644 index 0000000000..d6c4ccf676 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/command/DeleteAllCommand.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.hibernate.osgitest.command; + +import org.apache.felix.gogo.commands.Action; +import org.apache.felix.gogo.commands.Command; +import org.apache.felix.service.command.CommandSession; +import org.hibernate.osgitest.DataPointService; + +@Command(scope = "dp", name = "deleteAll") +public class DeleteAllCommand implements Action { +private DataPointService dpService; + + public void setDpService(DataPointService dpService) { + this.dpService = dpService; + } + + public Object execute(CommandSession session) throws Exception { + dpService.deleteAll(); + return null; + } + +} diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/command/GetAllCommand.java b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/command/GetAllCommand.java new file mode 100644 index 0000000000..ebdac60ea2 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/command/GetAllCommand.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.hibernate.osgitest.command; + +import java.util.List; + +import org.apache.felix.gogo.commands.Action; +import org.apache.felix.gogo.commands.Command; +import org.apache.felix.service.command.CommandSession; +import org.hibernate.osgitest.DataPointService; +import org.hibernate.osgitest.entity.DataPoint; + +@Command(scope = "dp", name = "getAll") +public class GetAllCommand implements Action { + private DataPointService dpService; + + public void setDpService(DataPointService dpService) { + this.dpService = dpService; + } + + public Object execute(CommandSession session) throws Exception { + List dps = dpService.getAll(); + for (DataPoint dp : dps) { + System.out.println(dp.getId() + ", " + dp.getName()); + } + return null; + } + +} diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/command/GetRevisionsCommand.java b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/command/GetRevisionsCommand.java new file mode 100644 index 0000000000..b4573f6200 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/command/GetRevisionsCommand.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.hibernate.osgitest.command; + +import java.util.Map; + +import org.apache.felix.gogo.commands.Action; +import org.apache.felix.gogo.commands.Argument; +import org.apache.felix.gogo.commands.Command; +import org.apache.felix.service.command.CommandSession; +import org.hibernate.envers.DefaultRevisionEntity; +import org.hibernate.osgitest.DataPointService; + +@Command(scope = "dp", name = "getRevisions") +public class GetRevisionsCommand implements Action { + @Argument(index=0, name="Id", required=true, description="Id", multiValued=false) + String id; + + private DataPointService dpService; + + public void setDpService(DataPointService dpService) { + this.dpService = dpService; + } + + public Object execute(CommandSession session) throws Exception { + Map revisions = dpService.getRevisions(Long.valueOf( id )); + for (Number revisionNum : revisions.keySet()) { + DefaultRevisionEntity dre = revisions.get( revisionNum ); + System.out.println(revisionNum + ": " + dre.getId() + ", " + dre.getTimestamp()); + } + return null; + } + +} diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/command/UpdateCommand.java b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/command/UpdateCommand.java new file mode 100644 index 0000000000..f694967444 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/command/UpdateCommand.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.hibernate.osgitest.command; + +import org.apache.felix.gogo.commands.Action; +import org.apache.felix.gogo.commands.Argument; +import org.apache.felix.gogo.commands.Command; +import org.apache.felix.service.command.CommandSession; +import org.hibernate.osgitest.DataPointService; +import org.hibernate.osgitest.entity.DataPoint; + +@Command(scope = "dp", name = "update") +public class UpdateCommand implements Action { + @Argument(index=0, name="Id", required=true, description="Id", multiValued=false) + String id; + + @Argument(index=1, name="Name", required=true, description="Name", multiValued=false) + String name; + + private DataPointService dpService; + + public void setDpService(DataPointService dpService) { + this.dpService = dpService; + } + + public Object execute(CommandSession session) throws Exception { + DataPoint dp = dpService.get( Long.valueOf( id ) ); + dp.setName( name ); + dpService.update( dp ); + return null; + } + +} diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/entity/DataPoint.java b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/entity/DataPoint.java new file mode 100644 index 0000000000..6f7126fd27 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/entity/DataPoint.java @@ -0,0 +1,56 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * JBoss, Home of Professional Open Source + * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @authors tag. All rights reserved. + * See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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, v. 2.1. + * This program is distributed in the hope that it will be useful, but WITHOUT A + * 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, + * v.2.1 along with this distribution; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ +package org.hibernate.osgitest.entity; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +import org.hibernate.envers.Audited; + +/** + * @author Brett Meyer + */ +@Entity +@Audited +public class DataPoint { + @Id + @GeneratedValue + private long id; + + private String name; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/resources/OSGI-INF/blueprint/blueprint.xml b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/resources/OSGI-INF/blueprint/blueprint.xml new file mode 100644 index 0000000000..81cf11eddc --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/resources/OSGI-INF/blueprint/blueprint.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/resources/ehcache.xml b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/resources/ehcache.xml new file mode 100644 index 0000000000..11fddc733d --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/resources/ehcache.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + --> + + + + diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/resources/hibernate.cfg.xml b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/resources/hibernate.cfg.xml new file mode 100644 index 0000000000..278f529f89 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/resources/hibernate.cfg.xml @@ -0,0 +1,46 @@ + + + + + + org.h2.Driver + jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE + sa + + org.hibernate.dialect.H2Dialect + create-drop + + + + + + + + + + + + + diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/resources/pool-one.properties b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/resources/pool-one.properties new file mode 100644 index 0000000000..abaee38558 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/resources/pool-one.properties @@ -0,0 +1,7 @@ +jdbc-0.proxool.alias=pool-one +jdbc-0.proxool.driver-url=jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE +jdbc-0.proxool.driver-class=org.h2.Driver +jdbc-0.user=sa +jdbc-0.password= +jdbc-0.proxool.maximum-connection-count=2 +jdbc-0.proxool.house-keeping-test-sql=select CURRENT_DATE \ No newline at end of file diff --git a/documentation/src/main/docbook/quickstart/tutorials/pom.xml b/documentation/src/main/docbook/quickstart/tutorials/pom.xml index dd3adecac3..9e09ca1c08 100644 --- a/documentation/src/main/docbook/quickstart/tutorials/pom.xml +++ b/documentation/src/main/docbook/quickstart/tutorials/pom.xml @@ -44,6 +44,7 @@ annotations entitymanager envers + osgi From 120dbbb3111c9f5f7c7d314e9a62b520e1933150 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Tue, 21 May 2013 16:24:23 -0500 Subject: [PATCH 09/36] HHH-8257 - More concisely obtaining a JBoss logger --- .../internal/CollectionLoadContext.java | 5 +- .../org/hibernate/internal/CoreLogging.java | 55 +++++++++++++++++++ .../hibernate/jpa/internal/HEMLogging.java | 55 +++++++++++++++++++ .../envers/internal/EnversLogging.java | 55 +++++++++++++++++++ 4 files changed, 167 insertions(+), 3 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/internal/CoreLogging.java create mode 100644 hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/HEMLogging.java create mode 100644 hibernate-envers/src/main/java/org/hibernate/envers/internal/EnversLogging.java diff --git a/hibernate-core/src/main/java/org/hibernate/engine/loading/internal/CollectionLoadContext.java b/hibernate-core/src/main/java/org/hibernate/engine/loading/internal/CollectionLoadContext.java index a23b662f30..e84c637dfa 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/loading/internal/CollectionLoadContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/loading/internal/CollectionLoadContext.java @@ -42,10 +42,10 @@ import org.hibernate.engine.spi.CollectionKey; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.Status; +import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.pretty.MessageHelper; -import org.jboss.logging.Logger; /** * Represents state associated with the processing of a given {@link ResultSet} @@ -58,8 +58,7 @@ import org.jboss.logging.Logger; * @author Steve Ebersole */ public class CollectionLoadContext { - - private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, CollectionLoadContext.class.getName()); + private static final CoreMessageLogger LOG = CoreLogging.messageLogger( CollectionLoadContext.class ); private final LoadContexts loadContexts; private final ResultSet resultSet; diff --git a/hibernate-core/src/main/java/org/hibernate/internal/CoreLogging.java b/hibernate-core/src/main/java/org/hibernate/internal/CoreLogging.java new file mode 100644 index 0000000000..19b1e2be9f --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/internal/CoreLogging.java @@ -0,0 +1,55 @@ +/* + * 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.internal; + +import org.jboss.logging.Logger; + +/** + * Quite sad, really, when you need helpers for generating loggers... + * + * @author Steve Ebersole + */ +public class CoreLogging { + /** + * Disallow instantiation + */ + private CoreLogging() { + } + + public static CoreMessageLogger messageLogger(Class classNeedingLogging) { + return messageLogger( classNeedingLogging.getName() ); + } + + public static CoreMessageLogger messageLogger(String loggerName) { + return Logger.getMessageLogger( CoreMessageLogger.class, loggerName ); + } + + public static Logger logger(Class classNeedingLogging) { + return Logger.getLogger( classNeedingLogging ); + } + + public static Logger logger(String loggerName) { + return Logger.getLogger( loggerName ); + } +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/HEMLogging.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/HEMLogging.java new file mode 100644 index 0000000000..48d4b7fd86 --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/HEMLogging.java @@ -0,0 +1,55 @@ +/* + * 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.internal; + +import org.jboss.logging.Logger; + +/** + * Sad when you need helpers for generating loggers... + * + * @author Steve Ebersole + */ +public class HEMLogging { + /** + * Disallow instantiation + */ + private HEMLogging() { + } + + public static EntityManagerMessageLogger messageLogger(Class classNeedingLogging) { + return messageLogger( classNeedingLogging.getName() ); + } + + public static EntityManagerMessageLogger messageLogger(String loggerName) { + return Logger.getMessageLogger( EntityManagerMessageLogger .class, loggerName ); + } + + public static Logger logger(Class classNeedingLogging) { + return Logger.getLogger( classNeedingLogging ); + } + + public static Logger logger(String loggerName) { + return Logger.getLogger( loggerName ); + } +} diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/EnversLogging.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/EnversLogging.java new file mode 100644 index 0000000000..4dcecee409 --- /dev/null +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/EnversLogging.java @@ -0,0 +1,55 @@ +/* + * 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.envers.internal; + +import org.jboss.logging.Logger; + +/** + * Sad when you need helpers for generating loggers... + * + * @author Steve Ebersole + */ +public class EnversLogging { + /** + * Disallow instantiation + */ + private EnversLogging() { + } + + public static EnversMessageLogger messageLogger(Class classNeedingLogging) { + return messageLogger( classNeedingLogging.getName() ); + } + + public static EnversMessageLogger messageLogger(String loggerName) { + return Logger.getMessageLogger( EnversMessageLogger .class, loggerName ); + } + + public static Logger logger(Class classNeedingLogging) { + return Logger.getLogger( classNeedingLogging ); + } + + public static Logger logger(String loggerName) { + return Logger.getLogger( loggerName ); + } +} From f2073113fca24b4196c651840d899c7184a8bc7c Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Tue, 21 May 2013 17:20:58 -0400 Subject: [PATCH 10/36] HHH-8112 removed variables from POMs -- not supported by docbook --- .../quickstart/tutorials/osgi/managed-jpa/features.xml | 1 - .../docbook/quickstart/tutorials/osgi/managed-jpa/pom.xml | 4 ++-- .../docbook/quickstart/tutorials/osgi/unmanaged-jpa/pom.xml | 4 ++-- .../quickstart/tutorials/osgi/unmanaged-native/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 7 deletions(-) mode change 100644 => 100755 documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/features.xml mode change 100644 => 100755 documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/pom.xml mode change 100644 => 100755 documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/pom.xml mode change 100644 => 100755 documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/pom.xml diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/features.xml b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/features.xml old mode 100644 new mode 100755 index 47ac76c30d..cecf577faf --- a/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/features.xml +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/features.xml @@ -8,7 +8,6 @@ aries.transaction.recoverable = true aries.transaction.timeout = 600 - aries.transaction.howl.logFileDir = ${karaf.data}/txlog aries.transaction.howl.maxLogFiles = 2 aries.transaction.howl.maxBlocksPerFile = 512 aries.transaction.howl.bufferSizeKBytes = 4 diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/pom.xml b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/pom.xml old mode 100644 new mode 100755 index 35f7009ab9..0da564c2b8 --- a/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/pom.xml +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/pom.xml @@ -37,8 +37,8 @@ true - ${pom.groupId}.${pom.artifactId} - ${pom.artifactId} + org.hibernate.osgi.managed-jpa + managed-jpa 1.0.0 org.hibernate.osgitest, diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/pom.xml b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/pom.xml old mode 100644 new mode 100755 index c09f30f155..c89c791e03 --- a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/pom.xml +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/pom.xml @@ -48,8 +48,8 @@ true - ${pom.groupId}.${pom.artifactId} - ${pom.artifactId} + org.hibernate.osgi.unmanaged-jpa + unmanaged-jpa 1.0.0 org.hibernate.osgitest, diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/pom.xml b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/pom.xml old mode 100644 new mode 100755 index bb1b8c1874..539715fb10 --- a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/pom.xml +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/pom.xml @@ -53,8 +53,8 @@ true - ${pom.groupId}.${pom.artifactId} - ${pom.artifactId} + org.hibernate.osgi.unmanaged-native + unmanaged-native 1.0.0 org.hibernate.osgitest, From 286800ec3499de449fae8e54e6a3238dcfe85212 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Tue, 21 May 2013 17:54:14 -0500 Subject: [PATCH 11/36] HHH-8211 - Checkstyle and FindBugs fix-ups --- .../internal/CollectionLoadContext.java | 77 +++++----- .../loading/internal/EntityLoadContext.java | 3 +- .../engine/loading/internal/LoadContexts.java | 34 ++--- .../internal/LoadingCollectionEntry.java | 3 +- .../engine/loading/internal/package-info.java | 28 ++++ .../hibernate/engine/profile/Association.java | 6 + .../org/hibernate/engine/profile/Fetch.java | 22 ++- .../engine/profile/FetchProfile.java | 29 ++-- .../engine/profile/package-info.java | 4 + .../engine/query/spi/FilterQueryPlan.java | 23 ++- .../engine/query/spi/HQLQueryPlan.java | 140 +++++++++++++----- .../query/spi/NamedParameterDescriptor.java | 19 ++- .../engine/query/spi/NativeSQLQueryPlan.java | 70 +++++---- .../query/spi/OrdinalParameterDescriptor.java | 15 +- .../query/spi/ParamLocationRecognizer.java | 12 +- .../engine/query/spi/ParameterMetadata.java | 102 +++++++++++-- .../engine/query/spi/ParameterParser.java | 73 +++++++-- .../engine/query/spi/QueryMetadata.java | 95 ------------ .../engine/query/spi/QueryPlanCache.java | 114 ++++++++++---- .../engine/query/spi/ReturnMetadata.java | 10 +- .../engine/query/spi/package-info.java | 4 + 21 files changed, 571 insertions(+), 312 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/engine/loading/internal/package-info.java create mode 100644 hibernate-core/src/main/java/org/hibernate/engine/profile/package-info.java delete mode 100644 hibernate-core/src/main/java/org/hibernate/engine/query/spi/QueryMetadata.java create mode 100644 hibernate-core/src/main/java/org/hibernate/engine/query/spi/package-info.java diff --git a/hibernate-core/src/main/java/org/hibernate/engine/loading/internal/CollectionLoadContext.java b/hibernate-core/src/main/java/org/hibernate/engine/loading/internal/CollectionLoadContext.java index e84c637dfa..9fd09535cd 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/loading/internal/CollectionLoadContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/loading/internal/CollectionLoadContext.java @@ -62,7 +62,7 @@ public class CollectionLoadContext { private final LoadContexts loadContexts; private final ResultSet resultSet; - private Set localLoadingCollectionKeys = new HashSet(); + private Set localLoadingCollectionKeys = new HashSet(); /** * Creates a collection load context for the given result set. @@ -119,12 +119,13 @@ public class CollectionLoadContext { if ( collection != null ) { if ( collection.wasInitialized() ) { LOG.trace( "Collection already initialized; ignoring" ); - return null; // ignore this row of results! Note the early exit + // ignore this row of results! Note the early exit + return null; } LOG.trace( "Collection not yet initialized; initializing" ); } else { - Object owner = loadContexts.getPersistenceContext().getCollectionOwner( key, persister ); + final Object owner = loadContexts.getPersistenceContext().getCollectionOwner( key, persister ); final boolean newlySavedEntity = owner != null && loadContexts.getPersistenceContext().getEntry( owner ).getStatus() != Status.LOADING; if ( newlySavedEntity ) { @@ -162,7 +163,7 @@ public class CollectionLoadContext { * @param persister The persister for which to complete loading. */ public void endLoadingCollections(CollectionPersister persister) { - SessionImplementor session = getLoadContext().getPersistenceContext().getSession(); + final SessionImplementor session = getLoadContext().getPersistenceContext().getSession(); if ( !loadContexts.hasLoadingCollectionEntries() && localLoadingCollectionKeys.isEmpty() ) { return; @@ -174,17 +175,17 @@ public class CollectionLoadContext { // internal loadingCollections map for matches and store those matches // in a temp collection. the temp collection is then used to "drive" // the #endRead processing. - List matches = null; - Iterator iter = localLoadingCollectionKeys.iterator(); - while ( iter.hasNext() ) { - final CollectionKey collectionKey = (CollectionKey) iter.next(); + List matches = null; + final Iterator itr = localLoadingCollectionKeys.iterator(); + while ( itr.hasNext() ) { + final CollectionKey collectionKey = (CollectionKey) itr.next(); final LoadingCollectionEntry lce = loadContexts.locateLoadingCollectionEntry( collectionKey ); if ( lce == null ) { LOG.loadingCollectionKeyNotFound( collectionKey ); } else if ( lce.getResultSet() == resultSet && lce.getPersister() == persister ) { if ( matches == null ) { - matches = new ArrayList(); + matches = new ArrayList(); } matches.add( lce ); if ( lce.getCollection().getOwner() == null ) { @@ -201,7 +202,7 @@ public class CollectionLoadContext { // todo : i'd much rather have this done from #endLoadingCollection(CollectionPersister,LoadingCollectionEntry)... loadContexts.unregisterLoadingCollectionXRef( collectionKey ); - iter.remove(); + itr.remove(); } } @@ -217,29 +218,35 @@ public class CollectionLoadContext { } } - private void endLoadingCollections(CollectionPersister persister, List matchedCollectionEntries) { + private void endLoadingCollections(CollectionPersister persister, List matchedCollectionEntries) { final boolean debugEnabled = LOG.isDebugEnabled(); if ( matchedCollectionEntries == null ) { - if ( debugEnabled ) LOG.debugf( "No collections were found in result set for role: %s", persister.getRole() ); + if ( debugEnabled ) { + LOG.debugf( "No collections were found in result set for role: %s", persister.getRole() ); + } return; } final int count = matchedCollectionEntries.size(); - if ( debugEnabled ) LOG.debugf("%s collections were found in result set for role: %s", count, persister.getRole()); - - for ( int i = 0; i < count; i++ ) { - LoadingCollectionEntry lce = ( LoadingCollectionEntry ) matchedCollectionEntries.get( i ); - endLoadingCollection( lce, persister ); + if ( debugEnabled ) { + LOG.debugf( "%s collections were found in result set for role: %s", count, persister.getRole() ); } - if ( debugEnabled ) LOG.debugf( "%s collections initialized for role: %s", count, persister.getRole() ); + for ( LoadingCollectionEntry matchedCollectionEntry : matchedCollectionEntries ) { + endLoadingCollection( matchedCollectionEntry, persister ); + } + + if ( debugEnabled ) { + LOG.debugf( "%s collections initialized for role: %s", count, persister.getRole() ); + } } private void endLoadingCollection(LoadingCollectionEntry lce, CollectionPersister persister) { LOG.tracev( "Ending loading collection [{0}]", lce ); final SessionImplementor session = getLoadContext().getPersistenceContext().getSession(); - boolean hasNoQueuedAdds = lce.getCollection().endRead(); // warning: can cause a recursive calls! (proxy initialization) + // warning: can cause a recursive calls! (proxy initialization) + final boolean hasNoQueuedAdds = lce.getCollection().endRead(); if ( persister.getCollectionType().hasHolder() ) { getLoadContext().getPersistenceContext().addCollectionHolder( lce.getCollection() ); @@ -255,13 +262,16 @@ public class CollectionLoadContext { // getLoadContext().getPersistenceContext().getBatchFetchQueue().removeBatchLoadableCollection(ce); // } } - - boolean addToCache = hasNoQueuedAdds && // there were no queued additions - persister.hasCache() && // and the role has a cache - session.getCacheMode().isPutEnabled() && - !ce.isDoremove(); // and this is not a forced initialization during flush + // add to cache if: + boolean addToCache = + // there were no queued additions + hasNoQueuedAdds + // and the role has a cache + && persister.hasCache() + // and this is not a forced initialization during flush + && session.getCacheMode().isPutEnabled() && !ce.isDoremove(); if ( addToCache ) { addCollectionToCache( lce, persister ); } @@ -269,11 +279,11 @@ public class CollectionLoadContext { if ( LOG.isDebugEnabled() ) { LOG.debugf( "Collection fully initialized: %s", - MessageHelper.collectionInfoString(persister, lce.getCollection(), lce.getKey(), session) + MessageHelper.collectionInfoString( persister, lce.getCollection(), lce.getKey(), session ) ); } if ( session.getFactory().getStatistics().isStatisticsEnabled() ) { - session.getFactory().getStatisticsImplementor().loadCollection(persister.getRole()); + session.getFactory().getStatisticsImplementor().loadCollection( persister.getRole() ); } } @@ -302,7 +312,8 @@ public class CollectionLoadContext { // currently this works in conjuction with the check on // DefaultInitializeCollectionEventHandler.initializeCollectionFromCache() (which makes sure to not read from // cache with enabled filters). - return; // EARLY EXIT!!!!! + // EARLY EXIT!!!!! + return; } final Object version; @@ -315,7 +326,7 @@ public class CollectionLoadContext { // about its owner, that owner should be the same instance as associated with the PC, but we do the // resolution against the PC anyway just to be safe since the lookup should not be costly. if ( lce.getCollection() != null ) { - Object linkedOwner = lce.getCollection().getOwner(); + final Object linkedOwner = lce.getCollection().getOwner(); if ( linkedOwner != null ) { final Serializable ownerKey = persister.getOwnerEntityPersister().getIdentifier( linkedOwner, session ); collectionOwner = getLoadContext().getPersistenceContext().getCollectionOwner( ownerKey, persister ); @@ -335,11 +346,11 @@ public class CollectionLoadContext { version = null; } - CollectionCacheEntry entry = new CollectionCacheEntry( lce.getCollection(), persister ); - CacheKey cacheKey = session.generateCacheKey( lce.getKey(), persister.getKeyType(), persister.getRole() ); - boolean put = persister.getCacheAccessStrategy().putFromLoad( + final CollectionCacheEntry entry = new CollectionCacheEntry( lce.getCollection(), persister ); + final CacheKey cacheKey = session.generateCacheKey( lce.getKey(), persister.getKeyType(), persister.getRole() ); + final boolean put = persister.getCacheAccessStrategy().putFromLoad( cacheKey, - persister.getCacheEntryStructure().structure(entry), + persister.getCacheEntryStructure().structure( entry ), session.getTimestamp(), version, factory.getSettings().isMinimalPutsEnabled() && session.getCacheMode()!= CacheMode.REFRESH @@ -360,7 +371,7 @@ public class CollectionLoadContext { @Override - public String toString() { + public String toString() { return super.toString() + ""; } } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/loading/internal/EntityLoadContext.java b/hibernate-core/src/main/java/org/hibernate/engine/loading/internal/EntityLoadContext.java index e84abeabf5..f706fe757f 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/loading/internal/EntityLoadContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/loading/internal/EntityLoadContext.java @@ -41,7 +41,8 @@ public class EntityLoadContext { private final LoadContexts loadContexts; private final ResultSet resultSet; - private final List hydratingEntities = new ArrayList( 20 ); // todo : need map? the prob is a proper key, right? + // todo : need map? the prob is a proper key, right? + private final List hydratingEntities = new ArrayList( 20 ); public EntityLoadContext(LoadContexts loadContexts, ResultSet resultSet) { this.loadContexts = loadContexts; diff --git a/hibernate-core/src/main/java/org/hibernate/engine/loading/internal/LoadContexts.java b/hibernate-core/src/main/java/org/hibernate/engine/loading/internal/LoadContexts.java index 5a6e4a6cf2..3b556e12d3 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/loading/internal/LoadContexts.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/loading/internal/LoadContexts.java @@ -30,24 +30,18 @@ import java.util.IdentityHashMap; import java.util.Map; import java.util.Set; -import org.jboss.logging.Logger; - import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.spi.CollectionKey; import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; -import org.hibernate.internal.util.collections.IdentityMap; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.pretty.MessageHelper; /** * Maps {@link ResultSet result-sets} to specific contextual data related to processing that result set *

- * Implementation note: internally an {@link IdentityMap} is used to maintain the mappings mainly because I'd - * rather not be dependent upon potentially bad {@link Object#equals} and {@link Object#hashCode} implementations on - * the JDBC result sets - *

* Considering the JDBC-redesign work, would further like this contextual info not mapped separately, but available * based on the result set being processed. This would also allow maintaining a single mapping as we could reliably * get notification of the result-set closing... @@ -55,8 +49,7 @@ import org.hibernate.pretty.MessageHelper; * @author Steve Ebersole */ public class LoadContexts { - - private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, LoadContexts.class.getName()); + private static final CoreMessageLogger LOG = CoreLogging.messageLogger( LoadContexts.class ); private final PersistenceContext persistenceContext; private Map collectionLoadContexts; @@ -101,11 +94,11 @@ public class LoadContexts { */ public void cleanup(ResultSet resultSet) { if ( collectionLoadContexts != null ) { - CollectionLoadContext collectionLoadContext = collectionLoadContexts.remove( resultSet ); + final CollectionLoadContext collectionLoadContext = collectionLoadContexts.remove( resultSet ); collectionLoadContext.cleanup(); } if ( entityLoadContexts != null ) { - EntityLoadContext entityLoadContext = entityLoadContexts.remove( resultSet ); + final EntityLoadContext entityLoadContext = entityLoadContexts.remove( resultSet ); entityLoadContext.cleanup(); } } @@ -191,7 +184,7 @@ public class LoadContexts { * @return The loading collection, or null if not found. */ public PersistentCollection locateLoadingCollection(CollectionPersister persister, Serializable ownerKey) { - LoadingCollectionEntry lce = locateLoadingCollectionEntry( new CollectionKey( persister, ownerKey ) ); + final LoadingCollectionEntry lce = locateLoadingCollectionEntry( new CollectionKey( persister, ownerKey ) ); if ( lce != null ) { if ( LOG.isTraceEnabled() ) { LOG.tracef( @@ -246,13 +239,13 @@ public class LoadContexts { if ( !hasRegisteredLoadingCollectionEntries() ) { return; } - xrefLoadingCollectionEntries.remove(key); - } + xrefLoadingCollectionEntries.remove( key ); + } @SuppressWarnings( {"UnusedDeclaration"}) Map getLoadingCollectionXRefs() { - return xrefLoadingCollectionEntries; - } + return xrefLoadingCollectionEntries; + } /** @@ -271,7 +264,7 @@ public class LoadContexts { return null; } LOG.tracev( "Attempting to locate loading collection entry [{0}] in any result-set context", key ); - LoadingCollectionEntry rtn = xrefLoadingCollectionEntries.get( key ); + final LoadingCollectionEntry rtn = xrefLoadingCollectionEntries.get( key ); if ( rtn == null ) { LOG.tracev( "Collection [{0}] not located in load context", key ); } @@ -291,6 +284,13 @@ public class LoadContexts { // Entity load contexts ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // * currently, not yet used... + /** + * Currently unused + * + * @param resultSet The result set + * + * @return The entity load context + */ @SuppressWarnings( {"UnusedDeclaration"}) public EntityLoadContext getEntityLoadContext(ResultSet resultSet) { EntityLoadContext context = null; diff --git a/hibernate-core/src/main/java/org/hibernate/engine/loading/internal/LoadingCollectionEntry.java b/hibernate-core/src/main/java/org/hibernate/engine/loading/internal/LoadingCollectionEntry.java index 27832a7f3f..b25a2d53c9 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/loading/internal/LoadingCollectionEntry.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/loading/internal/LoadingCollectionEntry.java @@ -41,7 +41,7 @@ public class LoadingCollectionEntry { private final Serializable key; private final PersistentCollection collection; - public LoadingCollectionEntry( + LoadingCollectionEntry( ResultSet resultSet, CollectionPersister persister, Serializable key, @@ -68,6 +68,7 @@ public class LoadingCollectionEntry { return collection; } + @Override public String toString() { return getClass().getName() + "@" + Integer.toHexString( hashCode() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/loading/internal/package-info.java b/hibernate-core/src/main/java/org/hibernate/engine/loading/internal/package-info.java new file mode 100644 index 0000000000..efcc552137 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/loading/internal/package-info.java @@ -0,0 +1,28 @@ +/* + * 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 + */ + +/** + * Internal classes used to track loading of data, potentially across multiple ResultSets + */ +package org.hibernate.engine.loading.internal; diff --git a/hibernate-core/src/main/java/org/hibernate/engine/profile/Association.java b/hibernate-core/src/main/java/org/hibernate/engine/profile/Association.java index e0b80e891d..dc06efac3d 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/profile/Association.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/profile/Association.java @@ -34,6 +34,12 @@ public class Association { private final String associationPath; private final String role; + /** + * Constructs a association defining what is to be fetched. + * + * @param owner The entity owning the association + * @param associationPath The path of the association, from the entity + */ public Association(EntityPersister owner, String associationPath) { this.owner = owner; this.associationPath = associationPath; diff --git a/hibernate-core/src/main/java/org/hibernate/engine/profile/Fetch.java b/hibernate-core/src/main/java/org/hibernate/engine/profile/Fetch.java index cca676c3e8..431b1878a1 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/profile/Fetch.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/profile/Fetch.java @@ -23,7 +23,6 @@ */ package org.hibernate.engine.profile; - /** * Models an individual fetch within a profile. * @@ -33,6 +32,12 @@ public class Fetch { private final Association association; private final Style style; + /** + * Constructs a Fetch + * + * @param association The association to be fetched + * @param style How to fetch it + */ public Fetch(Association association, Style style) { this.association = association; this.style = style; @@ -54,7 +59,13 @@ public class Fetch { * needed for other things as well anyway). */ public enum Style { + /** + * Fetch via a join + */ JOIN( "join" ), + /** + * Fetch via a subsequent select + */ SELECT( "select" ); private final String name; @@ -63,10 +74,18 @@ public class Fetch { this.name = name; } + @Override public String toString() { return name; } + /** + * Parses a style given an externalized string representation + * + * @param name The externalized representation + * + * @return The style; {@link #JOIN} is returned if not recognized + */ public static Style parse(String name) { if ( SELECT.name.equals( name ) ) { return SELECT; @@ -78,6 +97,7 @@ public class Fetch { } } + @Override public String toString() { return "Fetch[" + style + "{" + association.getRole() + "}]"; } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/profile/FetchProfile.java b/hibernate-core/src/main/java/org/hibernate/engine/profile/FetchProfile.java index e69cc721bf..7acc915246 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/profile/FetchProfile.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/profile/FetchProfile.java @@ -22,12 +22,11 @@ * Boston, MA 02110-1301 USA */ package org.hibernate.engine.profile; + import java.util.HashMap; import java.util.Map; -import org.jboss.logging.Logger; - -import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.type.BagType; import org.hibernate.type.Type; @@ -41,21 +40,17 @@ import org.hibernate.type.Type; * @author Steve Ebersole */ public class FetchProfile { - - private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, FetchProfile.class.getName()); + private static final CoreMessageLogger LOG = CoreLogging.messageLogger( FetchProfile.class ); private final String name; private Map fetches = new HashMap(); - private boolean containsJoinFetchedCollection = false; - private boolean containsJoinFetchedBag = false; + private boolean containsJoinFetchedCollection; + private boolean containsJoinFetchedBag; private Fetch bagJoinFetch; /** - * A 'fetch profile' is uniquely named within a - * {@link SessionFactoryImplementor SessionFactory}, thus it is also - * uniquely and easily identifiable within that - * {@link SessionFactoryImplementor SessionFactory}. + * Constructs a FetchProfile, supplying its unique name (unique within the SessionFactory). * * @param name The name under which we are bound in the sessionFactory */ @@ -91,7 +86,7 @@ public class FetchProfile { */ public void addFetch(final Fetch fetch) { final String fetchAssociactionRole = fetch.getAssociation().getRole(); - Type associationType = fetch.getAssociation().getOwner().getPropertyType( fetch.getAssociation().getAssociationPath() ); + final Type associationType = fetch.getAssociation().getOwner().getPropertyType( fetch.getAssociation().getAssociationPath() ); if ( associationType.isCollectionType() ) { LOG.tracev( "Handling request to add collection fetch [{0}]", fetchAssociactionRole ); @@ -103,7 +98,8 @@ public class FetchProfile { if ( BagType.class.isInstance( associationType ) ) { if ( containsJoinFetchedCollection ) { LOG.containsJoinFetchedCollection( fetchAssociactionRole ); - return; // EARLY EXIT!!! + // EARLY EXIT!!! + return; } } @@ -144,6 +140,13 @@ public class FetchProfile { return fetches; } + /** + * Obtain the fetch associated with the given role. + * + * @param role The role identifying the fetch + * + * @return The fetch, or {@code null} if a matching one was not found + */ public Fetch getFetchByRole(String role) { return fetches.get( role ); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/profile/package-info.java b/hibernate-core/src/main/java/org/hibernate/engine/profile/package-info.java new file mode 100644 index 0000000000..e9dbece986 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/profile/package-info.java @@ -0,0 +1,4 @@ +/** + * Models the fetch profiles defined by the application + */ +package org.hibernate.engine.profile; diff --git a/hibernate-core/src/main/java/org/hibernate/engine/query/spi/FilterQueryPlan.java b/hibernate-core/src/main/java/org/hibernate/engine/query/spi/FilterQueryPlan.java index de32765bdd..84a2a614e6 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/query/spi/FilterQueryPlan.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/query/spi/FilterQueryPlan.java @@ -1,10 +1,10 @@ /* * Hibernate, Relational Persistence for Idiomatic Java * - * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as + * Copyright (c) 2008, 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 Middleware LLC. + * 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 @@ -20,9 +20,9 @@ * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA - * */ package org.hibernate.engine.query.spi; + import java.io.Serializable; import java.util.Map; @@ -38,12 +38,21 @@ public class FilterQueryPlan extends HQLQueryPlan implements Serializable { private final String collectionRole; + /** + * Constructs a query plan for an HQL filter + * + * @param hql The HQL fragment + * @param collectionRole The collection role being filtered + * @param shallow Is the query shallow? + * @param enabledFilters All enabled filters from the Session + * @param factory The factory + */ public FilterQueryPlan( String hql, - String collectionRole, - boolean shallow, - Map enabledFilters, - SessionFactoryImplementor factory) { + String collectionRole, + boolean shallow, + Map enabledFilters, + SessionFactoryImplementor factory) { super( hql, collectionRole, shallow, enabledFilters, factory ); this.collectionRole = collectionRole; } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/query/spi/HQLQueryPlan.java b/hibernate-core/src/main/java/org/hibernate/engine/query/spi/HQLQueryPlan.java index 6d229c65c6..404000ea4b 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/query/spi/HQLQueryPlan.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/query/spi/HQLQueryPlan.java @@ -32,8 +32,7 @@ import java.util.List; import java.util.Map; import java.util.Set; -import org.jboss.logging.Logger; - +import org.hibernate.Filter; import org.hibernate.HibernateException; import org.hibernate.QueryException; import org.hibernate.ScrollableResults; @@ -47,6 +46,7 @@ import org.hibernate.hql.spi.FilterTranslator; import org.hibernate.hql.spi.ParameterTranslations; import org.hibernate.hql.spi.QueryTranslator; import org.hibernate.hql.spi.QueryTranslatorFactory; +import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.internal.util.collections.EmptyIterator; @@ -60,11 +60,10 @@ import org.hibernate.type.Type; * @author Steve Ebersole */ public class HQLQueryPlan implements Serializable { + private static final CoreMessageLogger LOG = CoreLogging.messageLogger( HQLQueryPlan.class ); // TODO : keep separate notions of QT[] here for shallow/non-shallow queries... - private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, HQLQueryPlan.class.getName()); - private final String sourceQuery; private final QueryTranslator[] translators; private final String[] sqlStrings; @@ -73,17 +72,32 @@ public class HQLQueryPlan implements Serializable { private final ReturnMetadata returnMetadata; private final Set querySpaces; - private final Set enabledFilterNames; + private final Set enabledFilterNames; private final boolean shallow; - public HQLQueryPlan(String hql, boolean shallow, Map enabledFilters, SessionFactoryImplementor factory) { + /** + * Constructs a HQLQueryPlan + * + * @param hql The HQL query + * @param shallow Whether the execution is to be shallow or not + * @param enabledFilters The enabled filters (we only keep the names) + * @param factory The factory + */ + public HQLQueryPlan(String hql, boolean shallow, Map enabledFilters, SessionFactoryImplementor factory) { this( hql, null, shallow, enabledFilters, factory ); } - protected HQLQueryPlan(String hql, String collectionRole, boolean shallow, Map enabledFilters, SessionFactoryImplementor factory){ + @SuppressWarnings("unchecked") + protected HQLQueryPlan( + String hql, + String collectionRole, + boolean shallow, + Map enabledFilters, + SessionFactoryImplementor factory) { this.sourceQuery = hql; this.shallow = shallow; - Set copy = new HashSet(); + + final Set copy = new HashSet(); copy.addAll( enabledFilters.keySet() ); this.enabledFilterNames = java.util.Collections.unmodifiableSet( copy ); @@ -91,8 +105,8 @@ public class HQLQueryPlan implements Serializable { final int length = concreteQueryStrings.length; this.translators = new QueryTranslator[length]; - List sqlStringList = new ArrayList(); - Set combinedQuerySpaces = new HashSet(); + final List sqlStringList = new ArrayList(); + final Set combinedQuerySpaces = new HashSet(); final boolean hasCollectionRole = (collectionRole == null); final Map querySubstitutions = factory.getSettings().getQuerySubstitutions(); @@ -107,7 +121,7 @@ public class HQLQueryPlan implements Serializable { else { translators[i] = queryTranslatorFactory .createFilterTranslator( hql, concreteQueryStrings[i], enabledFilters, factory ); - ( ( FilterTranslator ) translators[i] ).compile( collectionRole, querySubstitutions, shallow ); + ( (FilterTranslator) translators[i] ).compile( collectionRole, querySubstitutions, shallow ); } combinedQuerySpaces.addAll( translators[i].getQuerySpaces() ); sqlStringList.addAll( translators[i].collectSqlStrings() ); @@ -165,20 +179,33 @@ public class HQLQueryPlan implements Serializable { return shallow; } + /** + * Coordinates the efforts to perform a list across all the included query translators. + * + * @param queryParameters The query parameters + * @param session The session + * + * @return The query result list + * + * @throws HibernateException Indicates a problem performing the query + */ + @SuppressWarnings("unchecked") public List performList( QueryParameters queryParameters, - SessionImplementor session) throws HibernateException { + SessionImplementor session) throws HibernateException { if ( LOG.isTraceEnabled() ) { LOG.tracev( "Find: {0}", getSourceQuery() ); queryParameters.traceParameters( session.getFactory() ); } - boolean hasLimit = queryParameters.getRowSelection() != null && - queryParameters.getRowSelection().definesLimits(); - boolean needsLimit = hasLimit && translators.length > 1; - QueryParameters queryParametersToUse; + + final boolean hasLimit = queryParameters.getRowSelection() != null + && queryParameters.getRowSelection().definesLimits(); + final boolean needsLimit = hasLimit && translators.length > 1; + + final QueryParameters queryParametersToUse; if ( needsLimit ) { LOG.needsLimit(); - RowSelection selection = new RowSelection(); + final RowSelection selection = new RowSelection(); selection.setFetchSize( queryParameters.getRowSelection().getFetchSize() ); selection.setTimeout( queryParameters.getRowSelection().getTimeout() ); queryParametersToUse = queryParameters.createCopyUsing( selection ); @@ -187,12 +214,12 @@ public class HQLQueryPlan implements Serializable { queryParametersToUse = queryParameters; } - List combinedResults = new ArrayList(); - IdentitySet distinction = new IdentitySet(); + final List combinedResults = new ArrayList(); + final IdentitySet distinction = new IdentitySet(); int includedCount = -1; translator_loop: for ( QueryTranslator translator : translators ) { - List tmp = translator.list( session, queryParametersToUse ); + final List tmp = translator.list( session, queryParametersToUse ); if ( needsLimit ) { // NOTE : firstRow is zero-based final int first = queryParameters.getRowSelection().getFirstRow() == null @@ -223,9 +250,20 @@ public class HQLQueryPlan implements Serializable { return combinedResults; } + /** + * Coordinates the efforts to perform an iterate across all the included query translators. + * + * @param queryParameters The query parameters + * @param session The session + * + * @return The query result iterator + * + * @throws HibernateException Indicates a problem performing the query + */ + @SuppressWarnings("unchecked") public Iterator performIterate( QueryParameters queryParameters, - EventSource session) throws HibernateException { + EventSource session) throws HibernateException { if ( LOG.isTraceEnabled() ) { LOG.tracev( "Iterate: {0}", getSourceQuery() ); queryParameters.traceParameters( session.getFactory() ); @@ -234,8 +272,8 @@ public class HQLQueryPlan implements Serializable { return EmptyIterator.INSTANCE; } + final boolean many = translators.length > 1; Iterator[] results = null; - boolean many = translators.length > 1; if ( many ) { results = new Iterator[translators.length]; } @@ -248,12 +286,22 @@ public class HQLQueryPlan implements Serializable { } } - return many ? new JoinedIterator(results) : result; + return many ? new JoinedIterator( results ) : result; } + /** + * Coordinates the efforts to perform a scroll across all the included query translators. + * + * @param queryParameters The query parameters + * @param session The session + * + * @return The query result iterator + * + * @throws HibernateException Indicates a problem performing the query + */ public ScrollableResults performScroll( QueryParameters queryParameters, - SessionImplementor session) throws HibernateException { + SessionImplementor session) throws HibernateException { if ( LOG.isTraceEnabled() ) { LOG.tracev( "Iterate: {0}", getSourceQuery() ); queryParameters.traceParameters( session.getFactory() ); @@ -268,6 +316,16 @@ public class HQLQueryPlan implements Serializable { return translators[0].scroll( queryParameters, session ); } + /** + * Coordinates the efforts to perform an execution across all the included query translators. + * + * @param queryParameters The query parameters + * @param session The session + * + * @return The aggregated "affected row" count + * + * @throws HibernateException Indicates a problem performing the execution + */ public int performExecuteUpdate(QueryParameters queryParameters, SessionImplementor session) throws HibernateException { if ( LOG.isTraceEnabled() ) { @@ -285,32 +343,34 @@ public class HQLQueryPlan implements Serializable { } private ParameterMetadata buildParameterMetadata(ParameterTranslations parameterTranslations, String hql) { - long start = System.currentTimeMillis(); - ParamLocationRecognizer recognizer = ParamLocationRecognizer.parseLocations( hql ); - long end = System.currentTimeMillis(); + final long start = System.currentTimeMillis(); + final ParamLocationRecognizer recognizer = ParamLocationRecognizer.parseLocations( hql ); + final long end = System.currentTimeMillis(); + if ( LOG.isTraceEnabled() ) { LOG.tracev( "HQL param location recognition took {0} mills ({1})", ( end - start ), hql ); } int ordinalParamCount = parameterTranslations.getOrdinalParameterCount(); - int[] locations = ArrayHelper.toIntArray( recognizer.getOrdinalParameterLocationList() ); + final int[] locations = ArrayHelper.toIntArray( recognizer.getOrdinalParameterLocationList() ); if ( parameterTranslations.supportsOrdinalParameterMetadata() && locations.length != ordinalParamCount ) { throw new HibernateException( "ordinal parameter mismatch" ); } ordinalParamCount = locations.length; - OrdinalParameterDescriptor[] ordinalParamDescriptors = new OrdinalParameterDescriptor[ordinalParamCount]; + + final OrdinalParameterDescriptor[] ordinalParamDescriptors = new OrdinalParameterDescriptor[ordinalParamCount]; for ( int i = 1; i <= ordinalParamCount; i++ ) { ordinalParamDescriptors[ i - 1 ] = new OrdinalParameterDescriptor( i, - parameterTranslations.supportsOrdinalParameterMetadata() - ? parameterTranslations.getOrdinalParameterExpectedType( i ) - : null, - locations[ i - 1 ] + parameterTranslations.supportsOrdinalParameterMetadata() + ? parameterTranslations.getOrdinalParameterExpectedType( i ) + : null, + locations[ i - 1 ] ); } - Map namedParamDescriptorMap = new HashMap(); - Map map = recognizer.getNamedParameterDescriptionMap(); + final Map namedParamDescriptorMap = new HashMap(); + final Map map = recognizer.getNamedParameterDescriptionMap(); for ( final String name : map.keySet() ) { final ParamLocationRecognizer.NamedParameterDescription description = map.get( name ); namedParamDescriptorMap.put( @@ -325,9 +385,15 @@ public class HQLQueryPlan implements Serializable { } return new ParameterMetadata( ordinalParamDescriptors, namedParamDescriptorMap ); } + + /** + * Access to the underlying translators associated with this query + * + * @return The translators + */ public QueryTranslator[] getTranslators() { - QueryTranslator[] copy = new QueryTranslator[translators.length]; - System.arraycopy(translators, 0, copy, 0, copy.length); + final QueryTranslator[] copy = new QueryTranslator[translators.length]; + System.arraycopy( translators, 0, copy, 0, copy.length ); return copy; } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/query/spi/NamedParameterDescriptor.java b/hibernate-core/src/main/java/org/hibernate/engine/query/spi/NamedParameterDescriptor.java index a2edcc0cdb..05300b7ef7 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/query/spi/NamedParameterDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/query/spi/NamedParameterDescriptor.java @@ -1,10 +1,10 @@ /* * Hibernate, Relational Persistence for Idiomatic Java * - * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as + * Copyright (c) 2008, 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 Middleware LLC. + * 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 @@ -20,9 +20,9 @@ * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA - * */ package org.hibernate.engine.query.spi; + import java.io.Serializable; import org.hibernate.type.Type; @@ -38,6 +38,14 @@ public class NamedParameterDescriptor implements Serializable { private final int[] sourceLocations; private final boolean jpaStyle; + /** + * Constructs a NamedParameterDescriptor + * + * @param name The name of the parameter + * @param expectedType The expected type of the parameter, according to the translator + * @param sourceLocations The locations of the named parameters (aye aye aye) + * @param jpaStyle Was the parameter a JPA style "named parameter"? + */ public NamedParameterDescriptor(String name, Type expectedType, int[] sourceLocations, boolean jpaStyle) { this.name = name; this.expectedType = expectedType; @@ -61,6 +69,11 @@ public class NamedParameterDescriptor implements Serializable { return jpaStyle; } + /** + * Set the parameters expected type + * + * @param type The new expected type + */ public void resetExpectedType(Type type) { this.expectedType = type; } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/query/spi/NativeSQLQueryPlan.java b/hibernate-core/src/main/java/org/hibernate/engine/query/spi/NativeSQLQueryPlan.java index 84867ca1f4..ae13e45808 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/query/spi/NativeSQLQueryPlan.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/query/spi/NativeSQLQueryPlan.java @@ -30,8 +30,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import org.jboss.logging.Logger; - import org.hibernate.HibernateException; import org.hibernate.QueryException; import org.hibernate.action.internal.BulkOperationCleanupAction; @@ -41,6 +39,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.TypedValue; import org.hibernate.event.spi.EventSource; +import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.loader.custom.sql.SQLCustomQuery; @@ -52,10 +51,7 @@ import org.hibernate.type.Type; * @author Steve Ebersole */ public class NativeSQLQueryPlan implements Serializable { - private static final CoreMessageLogger LOG = Logger.getMessageLogger( - CoreMessageLogger.class, - NativeSQLQueryPlan.class.getName() - ); + private static final CoreMessageLogger LOG = CoreLogging.messageLogger( NativeSQLQueryPlan.class ); private final String sourceQuery; private final SQLCustomQuery customQuery; @@ -87,7 +83,7 @@ public class NativeSQLQueryPlan implements Serializable { } private int[] getNamedParameterLocs(String name) throws QueryException { - Object loc = customQuery.getNamedParameterBindPoints().get( name ); + final Object loc = customQuery.getNamedParameterBindPoints().get( name ); if ( loc == null ) { throw new QueryException( "Named parameter does not appear in Query: " + name, @@ -154,60 +150,73 @@ public class NativeSQLQueryPlan implements Serializable { final SessionImplementor session) throws SQLException { if ( namedParams != null ) { // assumes that types are all of span 1 - Iterator iter = namedParams.entrySet().iterator(); + final Iterator iter = namedParams.entrySet().iterator(); int result = 0; while ( iter.hasNext() ) { - Map.Entry e = (Map.Entry) iter.next(); - String name = (String) e.getKey(); - TypedValue typedval = (TypedValue) e.getValue(); - int[] locs = getNamedParameterLocs( name ); - for (int i = 0; i < locs.length; i++) { - LOG.debugf("bindNamedParameters() %s -> %s [%s]", typedval.getValue(), name, locs[i] + start); - typedval.getType().nullSafeSet( ps, typedval.getValue(), - locs[i] + start, session ); + final Map.Entry e = (Map.Entry) iter.next(); + final String name = (String) e.getKey(); + final TypedValue typedval = (TypedValue) e.getValue(); + final int[] locs = getNamedParameterLocs( name ); + for ( int loc : locs ) { + LOG.debugf( "bindNamedParameters() %s -> %s [%s]", typedval.getValue(), name, loc + start ); + typedval.getType().nullSafeSet( + ps, + typedval.getValue(), + loc + start, + session + ); } result += locs.length; } return result; } - return 0; + + return 0; } protected void coordinateSharedCacheCleanup(SessionImplementor session) { - BulkOperationCleanupAction action = new BulkOperationCleanupAction( session, getCustomQuery().getQuerySpaces() ); + final BulkOperationCleanupAction action = new BulkOperationCleanupAction( session, getCustomQuery().getQuerySpaces() ); if ( session.isEventSource() ) { - ( ( EventSource ) session ).getActionQueue().addAction( action ); + ( (EventSource) session ).getActionQueue().addAction( action ); } else { action.getAfterTransactionCompletionProcess().doAfterTransactionCompletion( true, session ); } } - public int performExecuteUpdate(QueryParameters queryParameters, + /** + * Performs the execute query + * + * @param queryParameters The query parameters + * @param session The session + * + * @return The number of affected rows as returned by the JDBC driver + * + * @throws HibernateException Indicates a problem performing the query execution + */ + public int performExecuteUpdate( + QueryParameters queryParameters, SessionImplementor session) throws HibernateException { coordinateSharedCacheCleanup( session ); - if(queryParameters.isCallable()) { + if ( queryParameters.isCallable() ) { throw new IllegalArgumentException("callable not yet supported for native queries"); } int result = 0; PreparedStatement ps; try { - queryParameters.processFilters( this.customQuery.getSQL(), - session ); - String sql = queryParameters.getFilteredSQL(); + queryParameters.processFilters( this.customQuery.getSQL(), session ); + final String sql = queryParameters.getFilteredSQL(); ps = session.getTransactionCoordinator().getJdbcCoordinator().getStatementPreparer().prepareStatement( sql, false ); try { int col = 1; - col += bindPositionalParameters( ps, queryParameters, col, - session ); - col += bindNamedParameters( ps, queryParameters - .getNamedParameters(), col, session ); + col += bindPositionalParameters( ps, queryParameters, col, session ); + col += bindNamedParameters( ps, queryParameters.getNamedParameters(), col, session ); result = session.getTransactionCoordinator().getJdbcCoordinator().getResultSetReturn().executeUpdate( ps ); } finally { @@ -218,7 +227,10 @@ public class NativeSQLQueryPlan implements Serializable { } catch (SQLException sqle) { throw session.getFactory().getSQLExceptionHelper().convert( - sqle, "could not execute native bulk manipulation query", this.sourceQuery ); + sqle, + "could not execute native bulk manipulation query", + this.sourceQuery + ); } return result; diff --git a/hibernate-core/src/main/java/org/hibernate/engine/query/spi/OrdinalParameterDescriptor.java b/hibernate-core/src/main/java/org/hibernate/engine/query/spi/OrdinalParameterDescriptor.java index ee2d5c6415..deb3ddaf8d 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/query/spi/OrdinalParameterDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/query/spi/OrdinalParameterDescriptor.java @@ -1,10 +1,10 @@ /* * Hibernate, Relational Persistence for Idiomatic Java * - * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as + * Copyright (c) 2008, 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 Middleware LLC. + * 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 @@ -20,14 +20,16 @@ * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA - * */ package org.hibernate.engine.query.spi; + import java.io.Serializable; import org.hibernate.type.Type; /** + * Descriptor regarding an ordinal parameter. + * * @author Steve Ebersole */ public class OrdinalParameterDescriptor implements Serializable { @@ -35,6 +37,13 @@ public class OrdinalParameterDescriptor implements Serializable { private final Type expectedType; private final int sourceLocation; + /** + * Constructs an ordinal parameter descriptor. + * + * @param ordinalPosition The ordinal position + * @param expectedType The expected type of the parameter + * @param sourceLocation The location of the parameter + */ public OrdinalParameterDescriptor(int ordinalPosition, Type expectedType, int sourceLocation) { this.ordinalPosition = ordinalPosition; this.expectedType = expectedType; diff --git a/hibernate-core/src/main/java/org/hibernate/engine/query/spi/ParamLocationRecognizer.java b/hibernate-core/src/main/java/org/hibernate/engine/query/spi/ParamLocationRecognizer.java index 8daab1a104..b534668f65 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/query/spi/ParamLocationRecognizer.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/query/spi/ParamLocationRecognizer.java @@ -38,12 +38,14 @@ import org.hibernate.internal.util.collections.ArrayHelper; * @author Steve Ebersole */ public class ParamLocationRecognizer implements ParameterParser.Recognizer { - + /** + * Internal representation of a recognized named parameter + */ public static class NamedParameterDescription { private final boolean jpaStyle; private final List positions = new ArrayList(); - public NamedParameterDescription(boolean jpaStyle) { + NamedParameterDescription(boolean jpaStyle) { this.jpaStyle = jpaStyle; } @@ -71,7 +73,7 @@ public class ParamLocationRecognizer implements ParameterParser.Recognizer { * @return The generated recognizer, with journaled location info. */ public static ParamLocationRecognizer parseLocations(String query) { - ParamLocationRecognizer recognizer = new ParamLocationRecognizer(); + final ParamLocationRecognizer recognizer = new ParamLocationRecognizer(); ParameterParser.parse( query, recognizer ); return recognizer; } @@ -88,8 +90,8 @@ public class ParamLocationRecognizer implements ParameterParser.Recognizer { /** * Returns the list of ordinal parameter locations. The list elements - * are Integers, representing the location for that given ordinal. Thus - * {@link #getOrdinalParameterLocationList()}.elementAt(n) represents the + * are Integers, representing the location for that given ordinal. Thus calling + * {@code getOrdinalParameterLocationList().elementAt(n)} represents the * location for the nth parameter. * * @return The list of ordinal parameter locations. diff --git a/hibernate-core/src/main/java/org/hibernate/engine/query/spi/ParameterMetadata.java b/hibernate-core/src/main/java/org/hibernate/engine/query/spi/ParameterMetadata.java index e1e81fe19d..d9c1ce5f26 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/query/spi/ParameterMetadata.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/query/spi/ParameterMetadata.java @@ -1,10 +1,10 @@ /* * Hibernate, Relational Persistence for Idiomatic Java * - * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as + * Copyright (c) 2008, 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 Middleware LLC. + * 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 @@ -20,9 +20,9 @@ * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA - * */ package org.hibernate.engine.query.spi; + import java.io.Serializable; import java.util.HashMap; import java.util.Map; @@ -37,33 +37,35 @@ import org.hibernate.type.Type; * @author Steve Ebersole */ public class ParameterMetadata implements Serializable { - private static final OrdinalParameterDescriptor[] EMPTY_ORDINALS = new OrdinalParameterDescriptor[0]; private final OrdinalParameterDescriptor[] ordinalDescriptors; - private final Map namedDescriptorMap; + private final Map namedDescriptorMap; /** * Instantiates a ParameterMetadata container. * - * @param ordinalDescriptors - * @param namedDescriptorMap + * @param ordinalDescriptors Descriptors of the ordinal parameters + * @param namedDescriptorMap Descriptors of the named parameters */ - public ParameterMetadata(OrdinalParameterDescriptor[] ordinalDescriptors, Map namedDescriptorMap) { + public ParameterMetadata( + OrdinalParameterDescriptor[] ordinalDescriptors, + Map namedDescriptorMap) { if ( ordinalDescriptors == null ) { this.ordinalDescriptors = EMPTY_ORDINALS; } else { - OrdinalParameterDescriptor[] copy = new OrdinalParameterDescriptor[ ordinalDescriptors.length ]; + final OrdinalParameterDescriptor[] copy = new OrdinalParameterDescriptor[ ordinalDescriptors.length ]; System.arraycopy( ordinalDescriptors, 0, copy, 0, ordinalDescriptors.length ); this.ordinalDescriptors = copy; } + if ( namedDescriptorMap == null ) { - this.namedDescriptorMap = java.util.Collections.EMPTY_MAP; + this.namedDescriptorMap = java.util.Collections.emptyMap(); } else { - int size = ( int ) ( ( namedDescriptorMap.size() / .75 ) + 1 ); - Map copy = new HashMap( size ); + final int size = (int) ( ( namedDescriptorMap.size() / .75 ) + 1 ); + final Map copy = new HashMap( size ); copy.putAll( namedDescriptorMap ); this.namedDescriptorMap = java.util.Collections.unmodifiableMap( copy ); } @@ -73,39 +75,107 @@ public class ParameterMetadata implements Serializable { return ordinalDescriptors.length; } + /** + * Get the descriptor for an ordinal parameter given its position + * + * @param position The position (1 based) + * + * @return The ordinal parameter descriptor + * + * @throws QueryParameterException If the position is out of range + */ public OrdinalParameterDescriptor getOrdinalParameterDescriptor(int position) { if ( position < 1 || position > ordinalDescriptors.length ) { - String error = "Position beyond number of declared ordinal parameters. " + - "Remember that ordinal parameters are 1-based! Position: " + position; - throw new QueryParameterException( error ); + throw new QueryParameterException( + "Position beyond number of declared ordinal parameters. " + + "Remember that ordinal parameters are 1-based! Position: " + position + ); } return ordinalDescriptors[position - 1]; } + /** + * Deprecated. + * + * @param position The position + * + * @return The type + * + * @deprecated Use {@link OrdinalParameterDescriptor#getExpectedType()} from the + * {@link #getOrdinalParameterDescriptor} return instead + */ + @Deprecated public Type getOrdinalParameterExpectedType(int position) { return getOrdinalParameterDescriptor( position ).getExpectedType(); } + /** + * Deprecated. + * + * @param position The position + * + * @return The source location + * + * @deprecated Use {@link OrdinalParameterDescriptor#getSourceLocation()} from the + * {@link #getOrdinalParameterDescriptor} return instead + */ + @Deprecated public int getOrdinalParameterSourceLocation(int position) { return getOrdinalParameterDescriptor( position ).getSourceLocation(); } + /** + * Access to the names of all named parameters + * + * @return The named parameter names + */ public Set getNamedParameterNames() { return namedDescriptorMap.keySet(); } + /** + * Get the descriptor for a named parameter given the name + * + * @param name The name of the parameter to locate + * + * @return The named parameter descriptor + * + * @throws QueryParameterException If the name could not be resolved to a named parameter + */ public NamedParameterDescriptor getNamedParameterDescriptor(String name) { - NamedParameterDescriptor meta = ( NamedParameterDescriptor ) namedDescriptorMap.get( name ); + final NamedParameterDescriptor meta = namedDescriptorMap.get( name ); if ( meta == null ) { throw new QueryParameterException( "could not locate named parameter [" + name + "]" ); } return meta; } + /** + * Deprecated. + * + * @param name The name of the parameter + * + * @return The type + * + * @deprecated Use {@link NamedParameterDescriptor#getExpectedType()} from the + * {@link #getNamedParameterDescriptor} return instead + */ + @Deprecated public Type getNamedParameterExpectedType(String name) { return getNamedParameterDescriptor( name ).getExpectedType(); } + /** + * Deprecated. + * + * @param name The name of the parameter + * + * @return The type + * + * @deprecated Use {@link NamedParameterDescriptor#getSourceLocations()} from the + * {@link #getNamedParameterDescriptor} return instead + */ + @Deprecated public int[] getNamedParameterSourceLocations(String name) { return getNamedParameterDescriptor( name ).getSourceLocations(); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/query/spi/ParameterParser.java b/hibernate-core/src/main/java/org/hibernate/engine/query/spi/ParameterParser.java index b41402d561..a2fb075404 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/query/spi/ParameterParser.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/query/spi/ParameterParser.java @@ -1,10 +1,10 @@ /* * Hibernate, Relational Persistence for Idiomatic Java * - * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as + * Copyright (c) 2008, 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 Middleware LLC. + * 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 @@ -20,9 +20,9 @@ * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA - * */ package org.hibernate.engine.query.spi; + import org.hibernate.QueryException; import org.hibernate.hql.internal.classic.ParserHelper; import org.hibernate.internal.util.StringHelper; @@ -36,12 +36,45 @@ import org.hibernate.internal.util.StringHelper; * @author Steve Ebersole */ public class ParameterParser { - + /** + * Maybe better named a Journaler. Essentially provides a callback contract for things that recognize parameters + */ public static interface Recognizer { + /** + * Called when an output parameter is recognized + * + * @param position The position within the query + */ public void outParameter(int position); + + /** + * Called when an ordinal parameter is recognized + * + * @param position The position within the query + */ public void ordinalParameter(int position); + + /** + * Called when a named parameter is recognized + * + * @param name The recognized parameter name + * @param position The position within the query + */ public void namedParameter(String name, int position); + + /** + * Called when a JPA-style named parameter is recognized + * + * @param name The name of the JPA-style parameter + * @param position The position within the query + */ public void jpaPositionalParameter(String name, int position); + + /** + * Called when a character that is not a parameter (or part of a parameter dfinition) is recognized. + * + * @param character The recognized character + */ public void other(char character); } @@ -64,13 +97,13 @@ public class ParameterParser { * @throws QueryException Indicates unexpected parameter conditions. */ public static void parse(String sqlString, Recognizer recognizer) throws QueryException { - boolean hasMainOutputParameter = startsWithEscapeCallTemplate( sqlString ); + final boolean hasMainOutputParameter = startsWithEscapeCallTemplate( sqlString ); boolean foundMainOutputParam = false; - int stringLength = sqlString.length(); + final int stringLength = sqlString.length(); boolean inQuote = false; for ( int indx = 0; indx < stringLength; indx++ ) { - char c = sqlString.charAt( indx ); + final char c = sqlString.charAt( indx ); if ( inQuote ) { if ( '\'' == c ) { inQuote = false; @@ -88,9 +121,9 @@ public class ParameterParser { else { if ( c == ':' ) { // named parameter - int right = StringHelper.firstIndexOfChar( sqlString, ParserHelper.HQL_SEPARATORS_BITSET, indx + 1 ); - int chopLocation = right < 0 ? sqlString.length() : right; - String param = sqlString.substring( indx + 1, chopLocation ); + final int right = StringHelper.firstIndexOfChar( sqlString, ParserHelper.HQL_SEPARATORS_BITSET, indx + 1 ); + final int chopLocation = right < 0 ? sqlString.length() : right; + final String param = sqlString.substring( indx + 1, chopLocation ); if ( StringHelper.isEmpty( param ) ) { throw new QueryException( "Space is not allowed after parameter prefix ':' [" + sqlString + "]" @@ -103,12 +136,12 @@ public class ParameterParser { // could be either an ordinal or JPA-positional parameter if ( indx < stringLength - 1 && Character.isDigit( sqlString.charAt( indx + 1 ) ) ) { // a peek ahead showed this as an JPA-positional parameter - int right = StringHelper.firstIndexOfChar( sqlString, ParserHelper.HQL_SEPARATORS, indx + 1 ); - int chopLocation = right < 0 ? sqlString.length() : right; - String param = sqlString.substring( indx + 1, chopLocation ); + final int right = StringHelper.firstIndexOfChar( sqlString, ParserHelper.HQL_SEPARATORS, indx + 1 ); + final int chopLocation = right < 0 ? sqlString.length() : right; + final String param = sqlString.substring( indx + 1, chopLocation ); // make sure this "name" is an integral try { - Integer.valueOf( param ); + Integer.valueOf( param ); } catch( NumberFormatException e ) { throw new QueryException( "JPA-style positional param was not an integral ordinal" ); @@ -133,12 +166,19 @@ public class ParameterParser { } } + /** + * Exposed as public solely for use from tests + * + * @param sqlString The SQL string to check + * + * @return true/false + */ public static boolean startsWithEscapeCallTemplate(String sqlString) { if ( ! ( sqlString.startsWith( "{" ) && sqlString.endsWith( "}" ) ) ) { return false; } - int chopLocation = sqlString.indexOf( "call" ); + final int chopLocation = sqlString.indexOf( "call" ); if ( chopLocation <= 0 ) { return false; } @@ -147,7 +187,8 @@ public class ParameterParser { final String fixture = "?=call"; int fixturePosition = 0; boolean matches = true; - for ( int i = 0, max = checkString.length(); i < max; i++ ) { + final int max = checkString.length(); + for ( int i = 0; i < max; i++ ) { final char c = Character.toLowerCase( checkString.charAt( i ) ); if ( Character.isWhitespace( c ) ) { continue; diff --git a/hibernate-core/src/main/java/org/hibernate/engine/query/spi/QueryMetadata.java b/hibernate-core/src/main/java/org/hibernate/engine/query/spi/QueryMetadata.java deleted file mode 100644 index ac906d57fd..0000000000 --- a/hibernate-core/src/main/java/org/hibernate/engine/query/spi/QueryMetadata.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as - * indicated by the @author tags or express copyright attribution - * statements applied by the authors. All third-party contributions are - * distributed under license by Red Hat Middleware LLC. - * - * This copyrighted material is made available to anyone wishing to use, modify, - * copy, or redistribute it subject to the terms and conditions of the GNU - * Lesser General Public License, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this distribution; if not, write to: - * Free Software Foundation, Inc. - * 51 Franklin Street, Fifth Floor - * Boston, MA 02110-1301 USA - * - */ -package org.hibernate.engine.query.spi; -import java.io.Serializable; -import java.util.Set; - -import org.hibernate.type.Type; - -/** - * Defines metadata regarding a translated HQL or native-SQL query. - * - * @author Steve Ebersole - */ -public class QueryMetadata implements Serializable { - private final String sourceQuery; - private final ParameterMetadata parameterMetadata; - private final String[] returnAliases; - private final Type[] returnTypes; - private final Set querySpaces; - - public QueryMetadata( - String sourceQuery, - ParameterMetadata parameterMetadata, - String[] returnAliases, - Type[] returnTypes, - Set querySpaces) { - this.sourceQuery = sourceQuery; - this.parameterMetadata = parameterMetadata; - this.returnAliases = returnAliases; - this.returnTypes = returnTypes; - this.querySpaces = querySpaces; - } - - /** - * Get the source HQL or native-SQL query. - * - * @return The source query. - */ - public String getSourceQuery() { - return sourceQuery; - } - - public ParameterMetadata getParameterMetadata() { - return parameterMetadata; - } - - /** - * Return source query select clause aliases (if any) - * - * @return an array of aliases as strings. - */ - public String[] getReturnAliases() { - return returnAliases; - } - - /** - * An array of types describing the returns of the source query. - * - * @return The return type array. - */ - public Type[] getReturnTypes() { - return returnTypes; - } - - /** - * The set of query spaces affected by this source query. - * - * @return The set of query spaces. - */ - public Set getQuerySpaces() { - return querySpaces; - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/engine/query/spi/QueryPlanCache.java b/hibernate-core/src/main/java/org/hibernate/engine/query/spi/QueryPlanCache.java index d7574e2a85..35c07194e9 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/query/spi/QueryPlanCache.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/query/spi/QueryPlanCache.java @@ -28,17 +28,16 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.Map; import java.util.Set; -import org.jboss.logging.Logger; - +import org.hibernate.Filter; import org.hibernate.MappingException; import org.hibernate.QueryException; import org.hibernate.cfg.Environment; import org.hibernate.engine.query.spi.sql.NativeSQLQuerySpecification; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.FilterImpl; import org.hibernate.internal.util.collections.BoundedConcurrentHashMap; @@ -54,9 +53,8 @@ import org.hibernate.internal.util.config.ConfigurationHelper; * @author Steve Ebersole */ public class QueryPlanCache implements Serializable { + private static final CoreMessageLogger LOG = CoreLogging.messageLogger( QueryPlanCache.class ); - private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, QueryPlanCache.class.getName()); - /** * The default strong reference count. */ @@ -65,11 +63,14 @@ public class QueryPlanCache implements Serializable { * The default soft reference count. */ public static final int DEFAULT_QUERY_PLAN_MAX_COUNT = 2048; + private final SessionFactoryImplementor factory; + /** * the cache of the actual plans... */ private final BoundedConcurrentHashMap queryPlanCache; + /** * simple cache of param metadata based on query string. Ideally, the original "user-supplied query" * string should be used to obtain this metadata (i.e., not the para-list-expanded query string) to avoid @@ -80,6 +81,12 @@ public class QueryPlanCache implements Serializable { */ private final BoundedConcurrentHashMap parameterMetadataCache; + /** + * Constructs the QueryPlanCache to be used by the given SessionFactory + * + * @param factory The SessionFactory + */ + @SuppressWarnings("deprecation") public QueryPlanCache(final SessionFactoryImplementor factory) { this.factory = factory; @@ -135,15 +142,17 @@ public class QueryPlanCache implements Serializable { } private ParameterMetadata buildParameterMetadata(String query){ - ParamLocationRecognizer recognizer = ParamLocationRecognizer.parseLocations( query ); + final ParamLocationRecognizer recognizer = ParamLocationRecognizer.parseLocations( query ); + final int size = recognizer.getOrdinalParameterLocationList().size(); - OrdinalParameterDescriptor[] ordinalDescriptors = new OrdinalParameterDescriptor[ size ]; + final OrdinalParameterDescriptor[] ordinalDescriptors = new OrdinalParameterDescriptor[ size ]; for ( int i = 0; i < size; i++ ) { final Integer position = recognizer.getOrdinalParameterLocationList().get( i ); ordinalDescriptors[i] = new OrdinalParameterDescriptor( i, null, position ); } - Map namedParamDescriptorMap = new HashMap(); - Map map = recognizer.getNamedParameterDescriptionMap(); + + final Map namedParamDescriptorMap = new HashMap(); + final Map map = recognizer.getNamedParameterDescriptionMap(); for ( final String name : map.keySet() ) { final ParamLocationRecognizer.NamedParameterDescription description = map.get( name ); namedParamDescriptorMap.put( @@ -159,9 +168,22 @@ public class QueryPlanCache implements Serializable { return new ParameterMetadata( ordinalDescriptors, namedParamDescriptorMap ); } - public HQLQueryPlan getHQLQueryPlan( String queryString, boolean shallow, Map enabledFilters) + /** + * Get the query plan for the given HQL query, creating it and caching it if not already cached + * + * @param queryString The HQL query string + * @param shallow Whether the execution will be shallow + * @param enabledFilters The filters enabled on the Session + * + * @return The query plan + * + * @throws QueryException Indicates a problem translating the query + * @throws MappingException Indicates a problem translating the query + */ + @SuppressWarnings("unchecked") + public HQLQueryPlan getHQLQueryPlan(String queryString, boolean shallow, Map enabledFilters) throws QueryException, MappingException { - HQLQueryPlanKey key = new HQLQueryPlanKey( queryString, shallow, enabledFilters ); + final HQLQueryPlanKey key = new HQLQueryPlanKey( queryString, shallow, enabledFilters ); HQLQueryPlan value = (HQLQueryPlan) queryPlanCache.get( key ); if ( value == null ) { LOG.tracev( "Unable to locate HQL query plan in cache; generating ({0})", queryString ); @@ -173,37 +195,69 @@ public class QueryPlanCache implements Serializable { return value; } - - - public FilterQueryPlan getFilterQueryPlan(String filterString, String collectionRole, boolean shallow, Map enabledFilters) - throws QueryException, MappingException { - FilterQueryPlanKey key = new FilterQueryPlanKey( filterString, collectionRole, shallow, enabledFilters ); + /** + * Get the query plan for the given collection HQL filter fragment, creating it and caching it if not already cached + * + * @param filterString The HQL filter fragment + * @param collectionRole The collection being filtered + * @param shallow Whether the execution will be shallow + * @param enabledFilters The filters enabled on the Session + * + * @return The query plan + * + * @throws QueryException Indicates a problem translating the query + * @throws MappingException Indicates a problem translating the query + */ + @SuppressWarnings("unchecked") + public FilterQueryPlan getFilterQueryPlan( + String filterString, + String collectionRole, + boolean shallow, + Map enabledFilters) throws QueryException, MappingException { + final FilterQueryPlanKey key = new FilterQueryPlanKey( filterString, collectionRole, shallow, enabledFilters ); FilterQueryPlan value = (FilterQueryPlan) queryPlanCache.get( key ); - if(value == null){ - LOG.tracev( "Unable to locate collection-filter query plan in cache; generating ({0} : {1} )", - collectionRole, filterString ); + if ( value == null ) { + LOG.tracev( + "Unable to locate collection-filter query plan in cache; generating ({0} : {1} )", + collectionRole, + filterString + ); value = new FilterQueryPlan( filterString, collectionRole, shallow, enabledFilters,factory ); queryPlanCache.putIfAbsent( key, value ); - } else { + } + else { LOG.tracev( "Located collection-filter query plan in cache ({0} : {1})", collectionRole, filterString ); } return value; } + /** + * Get the query plan for a native SQL query, creating it and caching it if not already cached + * + * @param spec The native SQL query specification + * + * @return The query plan + * + * @throws QueryException Indicates a problem translating the query + * @throws MappingException Indicates a problem translating the query + */ + @SuppressWarnings("unchecked") public NativeSQLQueryPlan getNativeSQLQueryPlan(final NativeSQLQuerySpecification spec) { NativeSQLQueryPlan value = (NativeSQLQueryPlan) queryPlanCache.get( spec ); - if(value == null){ + if ( value == null ) { LOG.tracev( "Unable to locate native-sql query plan in cache; generating ({0})", spec.getQueryString() ); value = new NativeSQLQueryPlan( spec, factory); queryPlanCache.putIfAbsent( spec, value ); - } else { + } + else { LOG.tracev( "Located native-sql query plan in cache ({0})", spec.getQueryString() ); } return value; } - - //clean up QueryPlanCache when Sessionfactory is closed + /** + * clean up QueryPlanCache when SessionFactory is closed + */ public void cleanup() { LOG.trace( "Cleaning QueryPlan Cache" ); queryPlanCache.clear(); @@ -223,7 +277,7 @@ public class QueryPlanCache implements Serializable { filterKeys = Collections.emptySet(); } else { - Set tmp = new HashSet( + final Set tmp = new HashSet( CollectionHelper.determineProperSizing( enabledFilters ), CollectionHelper.LOAD_FACTOR ); @@ -248,7 +302,7 @@ public class QueryPlanCache implements Serializable { return false; } - final HQLQueryPlanKey that = ( HQLQueryPlanKey ) o; + final HQLQueryPlanKey that = (HQLQueryPlanKey) o; return shallow == that.shallow && filterKeys.equals( that.filterKeys ) @@ -306,8 +360,7 @@ public class QueryPlanCache implements Serializable { return false; } - DynamicFilterKey that = ( DynamicFilterKey ) o; - + final DynamicFilterKey that = (DynamicFilterKey) o; return filterName.equals( that.filterName ) && parameterMetadata.equals( that.parameterMetadata ); @@ -336,7 +389,7 @@ public class QueryPlanCache implements Serializable { this.filterNames = Collections.emptySet(); } else { - Set tmp = new HashSet(); + final Set tmp = new HashSet(); tmp.addAll( enabledFilters.keySet() ); this.filterNames = Collections.unmodifiableSet( tmp ); @@ -358,8 +411,7 @@ public class QueryPlanCache implements Serializable { return false; } - final FilterQueryPlanKey that = ( FilterQueryPlanKey ) o; - + final FilterQueryPlanKey that = (FilterQueryPlanKey) o; return shallow == that.shallow && filterNames.equals( that.filterNames ) && query.equals( that.query ) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/query/spi/ReturnMetadata.java b/hibernate-core/src/main/java/org/hibernate/engine/query/spi/ReturnMetadata.java index bd9c2b1d71..4378bcde0a 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/query/spi/ReturnMetadata.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/query/spi/ReturnMetadata.java @@ -1,10 +1,10 @@ /* * Hibernate, Relational Persistence for Idiomatic Java * - * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as + * Copyright (c) 2008, 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 Middleware LLC. + * 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 @@ -20,21 +20,23 @@ * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA - * */ package org.hibernate.engine.query.spi; + import java.io.Serializable; import org.hibernate.type.Type; /** + * Metadata about the query return(s). + * * @author Steve Ebersole */ public class ReturnMetadata implements Serializable { private final String[] returnAliases; private final Type[] returnTypes; - public ReturnMetadata(String[] returnAliases, Type[] returnTypes) { + ReturnMetadata(String[] returnAliases, Type[] returnTypes) { this.returnAliases = returnAliases; this.returnTypes = returnTypes; } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/query/spi/package-info.java b/hibernate-core/src/main/java/org/hibernate/engine/query/spi/package-info.java new file mode 100644 index 0000000000..24cf9b0273 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/query/spi/package-info.java @@ -0,0 +1,4 @@ +/** + * Defines support for query plans and stored metadata about queries + */ +package org.hibernate.engine.query.spi; From ad103b1c30e8e1be926c33d56a36b37ac86d3b9d Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Tue, 21 May 2013 19:54:41 -0400 Subject: [PATCH 12/36] HHH-8112 added note about JPA 2.1 Aries fork --- .../docbook/quickstart/tutorials/osgi/managed-jpa/features.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/features.xml b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/features.xml index cecf577faf..5c6f625415 100755 --- a/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/features.xml +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/features.xml @@ -18,6 +18,9 @@ mvn:org.hibernate.javax.persistence/hibernate-jpa-2.1-api/1.0.0-SNAPSHOT + mvn:org.apache.aries/org.apache.aries.util/1.0.0 mvn:org.apache.aries.jpa/org.apache.aries.jpa.api/1.0.1-SNAPSHOT mvn:org.apache.aries.jpa/org.apache.aries.jpa.blueprint.aries/1.0.2-SNAPSHOT From 4ee980d9ff4d93556d7050e98aa0443052750db3 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Wed, 22 May 2013 11:12:31 -0500 Subject: [PATCH 13/36] HHH-8211 - Checkstyle and FindBugs fix-ups --- .../test/java/org/hibernate/test/hql/TupleSupportTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/TupleSupportTest.java b/hibernate-core/src/test/java/org/hibernate/test/hql/TupleSupportTest.java index 5e962a1638..f8f4639a61 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/hql/TupleSupportTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/hql/TupleSupportTest.java @@ -30,6 +30,7 @@ import javax.persistence.Id; import java.util.Collections; +import org.hibernate.Filter; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.AvailableSettings; @@ -98,7 +99,7 @@ public class TupleSupportTest extends BaseUnitTestCase { public void testImplicitTupleNotEquals() { final String hql = "from TheEntity e where e.compositeValue <> :p1"; HQLQueryPlan queryPlan = ( (SessionFactoryImplementor) sessionFactory ).getQueryPlanCache() - .getHQLQueryPlan( hql, false, Collections.emptyMap() ); + .getHQLQueryPlan( hql, false, Collections.emptyMap() ); assertEquals( 1, queryPlan.getSqlStrings().length ); System.out.println( " SQL : " + queryPlan.getSqlStrings()[0] ); @@ -109,7 +110,7 @@ public class TupleSupportTest extends BaseUnitTestCase { public void testImplicitTupleNotInList() { final String hql = "from TheEntity e where e.compositeValue not in (:p1,:p2)"; HQLQueryPlan queryPlan = ( (SessionFactoryImplementor) sessionFactory ).getQueryPlanCache() - .getHQLQueryPlan( hql, false, Collections.emptyMap() ); + .getHQLQueryPlan( hql, false, Collections.emptyMap() ); assertEquals( 1, queryPlan.getSqlStrings().length ); System.out.println( " SQL : " + queryPlan.getSqlStrings()[0] ); From f1f8600b548b86defadd57c6b9518adf17200055 Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Mon, 20 May 2013 17:05:41 -0400 Subject: [PATCH 14/36] HHH-8083 @OrderColumn not updated on @OneToMany cascade --- .../internal/CollectionUpdateAction.java | 2 +- .../QueuedOperationCollectionAction.java | 74 +++++++++++++++++++ .../org/hibernate/engine/spi/ActionQueue.java | 35 ++++++++- .../AbstractFlushingEventListener.java | 11 +++ .../AbstractCollectionPersister.java | 10 +++ .../collection/BasicCollectionPersister.java | 6 ++ .../collection/CollectionPersister.java | 10 +++ .../collection/OneToManyPersister.java | 13 +++- .../test/annotations/onetomany/Comment.java | 67 +++++++++++++++++ .../test/annotations/onetomany/Forum.java | 68 +++++++++++++++++ .../annotations/onetomany/OrderByTest.java | 43 ++++++++++- .../test/annotations/onetomany/Post.java | 32 ++++++++ .../test/annotations/onetomany/User.java | 44 +++++++++++ .../GoofyPersisterClassProvider.java | 5 ++ 14 files changed, 413 insertions(+), 7 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/action/internal/QueuedOperationCollectionAction.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/onetomany/Comment.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/onetomany/Forum.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/onetomany/Post.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/onetomany/User.java diff --git a/hibernate-core/src/main/java/org/hibernate/action/internal/CollectionUpdateAction.java b/hibernate-core/src/main/java/org/hibernate/action/internal/CollectionUpdateAction.java index 655bed0eff..083021154c 100644 --- a/hibernate-core/src/main/java/org/hibernate/action/internal/CollectionUpdateAction.java +++ b/hibernate-core/src/main/java/org/hibernate/action/internal/CollectionUpdateAction.java @@ -77,7 +77,7 @@ public final class CollectionUpdateAction extends CollectionAction { if ( !collection.hasQueuedOperations() ) { throw new AssertionFailure( "no queued adds" ); } - //do nothing - we only need to notify the cache... + //do nothing - we only need to notify the cache... } else if ( !affectedByFilters && collection.empty() ) { if ( !emptySnapshot ) { diff --git a/hibernate-core/src/main/java/org/hibernate/action/internal/QueuedOperationCollectionAction.java b/hibernate-core/src/main/java/org/hibernate/action/internal/QueuedOperationCollectionAction.java new file mode 100644 index 0000000000..f45562b6e7 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/action/internal/QueuedOperationCollectionAction.java @@ -0,0 +1,74 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2008-2011, 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.action.internal; + +import java.io.Serializable; + +import org.hibernate.HibernateException; +import org.hibernate.collection.spi.PersistentCollection; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.persister.collection.CollectionPersister; + +/** + * If a collection is extra lazy and has queued ops, we still need to + * process them. Ex: OneToManyPersister needs to insert indexes for List + * collections. See HHH-8083. + * + * @author Brett Meyer + */ +public final class QueuedOperationCollectionAction extends CollectionAction { + + /** + * Constructs a CollectionUpdateAction + * + * @param collection The collection to update + * @param persister The collection persister + * @param id The collection key + * @param session The session + */ + public QueuedOperationCollectionAction( + final PersistentCollection collection, + final CollectionPersister persister, + final Serializable id, + final SessionImplementor session) { + super( persister, collection, id, session ); + } + + @Override + public void execute() throws HibernateException { + final Serializable id = getKey(); + final SessionImplementor session = getSession(); + final CollectionPersister persister = getPersister(); + final PersistentCollection collection = getCollection(); + + persister.processQueuedOps( collection, id, session ); + } +} + + + + + + + diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/ActionQueue.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/ActionQueue.java index a70c2bf3d5..99f8dc6933 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/ActionQueue.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/ActionQueue.java @@ -52,6 +52,7 @@ import org.hibernate.action.internal.EntityDeleteAction; import org.hibernate.action.internal.EntityIdentityInsertAction; import org.hibernate.action.internal.EntityInsertAction; import org.hibernate.action.internal.EntityUpdateAction; +import org.hibernate.action.internal.QueuedOperationCollectionAction; import org.hibernate.action.internal.UnresolvedEntityInsertActions; import org.hibernate.action.spi.AfterTransactionCompletionProcess; import org.hibernate.action.spi.BeforeTransactionCompletionProcess; @@ -63,7 +64,7 @@ import org.hibernate.type.Type; /** * Responsible for maintaining the queue of actions related to events. - *

+ * * The ActionQueue holds the DML operations queued as part of a session's * transactional-write-behind semantics. DML operations are queued here * until a flush forces them to be executed against the database. @@ -91,6 +92,7 @@ public class ActionQueue { // just re-use the same Lists for convenience. private ArrayList collectionCreations; private ArrayList collectionUpdates; + private ArrayList collectionQueuedOps; private ArrayList collectionRemovals; private AfterTransactionCompletionProcessQueue afterTransactionProcesses; @@ -115,6 +117,7 @@ public class ActionQueue { collectionCreations = new ArrayList( INIT_QUEUE_LIST_SIZE ); collectionRemovals = new ArrayList( INIT_QUEUE_LIST_SIZE ); collectionUpdates = new ArrayList( INIT_QUEUE_LIST_SIZE ); + collectionQueuedOps = new ArrayList( INIT_QUEUE_LIST_SIZE ); afterTransactionProcesses = new AfterTransactionCompletionProcessQueue( session ); beforeTransactionProcesses = new BeforeTransactionCompletionProcessQueue( session ); @@ -128,6 +131,7 @@ public class ActionQueue { collectionCreations.clear(); collectionRemovals.clear(); collectionUpdates.clear(); + collectionQueuedOps.clear(); unresolvedInsertions.clear(); } @@ -163,6 +167,11 @@ public class ActionQueue { collectionUpdates.add( action ); } + @SuppressWarnings({ "unchecked" }) + public void addAction(QueuedOperationCollectionAction action) { + collectionQueuedOps.add( action ); + } + @SuppressWarnings({ "unchecked" }) public void addAction(EntityIdentityInsertAction insert) { LOG.tracev( "Adding an EntityIdentityInsertAction for [{0}] object", insert.getEntityName() ); @@ -276,6 +285,8 @@ public class ActionQueue { } executeActions( insertions ); executeActions( updates ); + // do before actions are handled in the other collection queues + executeActions( collectionQueuedOps ); executeActions( collectionRemovals ); executeActions( collectionUpdates ); executeActions( collectionCreations ); @@ -291,6 +302,7 @@ public class ActionQueue { prepareActions( collectionRemovals ); prepareActions( collectionUpdates ); prepareActions( collectionCreations ); + prepareActions( collectionQueuedOps ); } /** @@ -325,6 +337,7 @@ public class ActionQueue { areTablesToUpdated( deletions, tables ) || areTablesToUpdated( collectionUpdates, tables ) || areTablesToUpdated( collectionCreations, tables ) || + areTablesToUpdated( collectionQueuedOps, tables ) || areTablesToUpdated( collectionRemovals, tables ); } @@ -401,6 +414,7 @@ public class ActionQueue { .append( " collectionCreations=" ).append( collectionCreations ) .append( " collectionRemovals=" ).append( collectionRemovals ) .append( " collectionUpdates=" ).append( collectionUpdates ) + .append( " collectionQueuedOps=" ).append( collectionQueuedOps ) .append( " unresolvedInsertDependencies=" ).append( unresolvedInsertions ) .append( "]" ) .toString(); @@ -436,6 +450,7 @@ public class ActionQueue { //sort the updates by fk java.util.Collections.sort( collectionCreations ); java.util.Collections.sort( collectionUpdates ); + java.util.Collections.sort( collectionQueuedOps ); java.util.Collections.sort( collectionRemovals ); } } @@ -472,6 +487,7 @@ public class ActionQueue { public void clearFromFlushNeededCheck(int previousCollectionRemovalSize) { collectionCreations.clear(); collectionUpdates.clear(); + collectionQueuedOps.clear(); updates.clear(); // collection deletions are a special case since update() can add // deletions of collections not loaded by the session. @@ -495,6 +511,7 @@ public class ActionQueue { ! unresolvedInsertions.isEmpty() || deletions.size() > 0 || collectionUpdates.size() > 0 || + collectionQueuedOps.size() > 0 || collectionRemovals.size() > 0 || collectionCreations.size() > 0; } @@ -564,6 +581,13 @@ public class ActionQueue { for ( int i = 0; i < queueSize; i++ ) { oos.writeObject( collectionCreations.get( i ) ); } + + queueSize = collectionQueuedOps.size(); + LOG.tracev( "Starting serialization of [{0}] collectionQueuedOps entries", queueSize ); + oos.writeInt( queueSize ); + for ( int i = 0; i < queueSize; i++ ) { + oos.writeObject( collectionQueuedOps.get( i ) ); + } } /** @@ -640,6 +664,15 @@ public class ActionQueue { action.afterDeserialize( session ); rtn.collectionCreations.add( action ); } + + queueSize = ois.readInt(); + LOG.tracev( "Starting deserialization of [{0}] collectionQueuedOps entries", queueSize ); + rtn.collectionQueuedOps = new ArrayList( queueSize ); + for ( int i = 0; i < queueSize; i++ ) { + CollectionAction action = ( CollectionAction ) ois.readObject(); + action.afterDeserialize( session ); + rtn.collectionQueuedOps.add( action ); + } return rtn; } diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/AbstractFlushingEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/AbstractFlushingEventListener.java index 81f889b50e..1bbe6ba427 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/AbstractFlushingEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/AbstractFlushingEventListener.java @@ -32,6 +32,7 @@ import org.hibernate.HibernateException; import org.hibernate.action.internal.CollectionRecreateAction; import org.hibernate.action.internal.CollectionRemoveAction; import org.hibernate.action.internal.CollectionUpdateAction; +import org.hibernate.action.internal.QueuedOperationCollectionAction; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.internal.Cascade; import org.hibernate.engine.internal.CascadePoint; @@ -294,6 +295,16 @@ public abstract class AbstractFlushingEventListener implements Serializable { ) ); } + if ( !coll.wasInitialized() && coll.hasQueuedOperations() ) { + actionQueue.addAction( + new QueuedOperationCollectionAction( + coll, + ce.getLoadedPersister(), + ce.getLoadedKey(), + session + ) + ); + } } 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 160057e76c..40b4eac66c 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 @@ -1642,6 +1642,16 @@ public abstract class AbstractCollectionPersister protected abstract int doUpdateRows(Serializable key, PersistentCollection collection, SessionImplementor session) throws HibernateException; + + public void processQueuedOps(PersistentCollection collection, Serializable key, SessionImplementor session) + throws HibernateException { + if ( collection.hasQueuedOperations() ) { + doProcessQueuedOps( collection, key, session ); + } + } + + protected abstract void doProcessQueuedOps(PersistentCollection collection, Serializable key, SessionImplementor session) + throws HibernateException; public CollectionMetadata getCollectionMetadata() { return this; diff --git a/hibernate-core/src/main/java/org/hibernate/persister/collection/BasicCollectionPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/collection/BasicCollectionPersister.java index b9250a0b08..4778bb733e 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/collection/BasicCollectionPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/collection/BasicCollectionPersister.java @@ -152,6 +152,12 @@ public class BasicCollectionPersister extends AbstractCollectionPersister { return update.toStatementString(); } + + @Override + protected void doProcessQueuedOps(PersistentCollection collection, Serializable id, SessionImplementor session) + throws HibernateException { + // nothing to do + } /** * Generate the SQL DELETE that deletes a particular row diff --git a/hibernate-core/src/main/java/org/hibernate/persister/collection/CollectionPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/collection/CollectionPersister.java index ecfe83c7d8..0bbd9aa78c 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/collection/CollectionPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/collection/CollectionPersister.java @@ -199,6 +199,16 @@ public interface CollectionPersister extends CollectionDefinition { Serializable key, SessionImplementor session) throws HibernateException; + + /** + * Process queued operations within the PersistentCollection. + */ + public void processQueuedOps( + PersistentCollection collection, + Serializable key, + SessionImplementor session) + throws HibernateException; + /** * Get the name of this collection role (the fully qualified class name, * extended by a "property path") diff --git a/hibernate-core/src/main/java/org/hibernate/persister/collection/OneToManyPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/collection/OneToManyPersister.java index 6cec2e9259..ed4046f38b 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/collection/OneToManyPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/collection/OneToManyPersister.java @@ -181,21 +181,26 @@ public class OneToManyPersister extends AbstractCollectionPersister { public void recreate(PersistentCollection collection, Serializable id, SessionImplementor session) throws HibernateException { super.recreate( collection, id, session ); - writeIndex( collection, id, session ); + writeIndex( collection, collection.entries( this ), id, session ); } @Override public void insertRows(PersistentCollection collection, Serializable id, SessionImplementor session) throws HibernateException { super.insertRows( collection, id, session ); - writeIndex( collection, id, session ); + writeIndex( collection, collection.entries( this ), id, session ); } - private void writeIndex(PersistentCollection collection, Serializable id, SessionImplementor session) { + @Override + protected void doProcessQueuedOps(PersistentCollection collection, Serializable id, SessionImplementor session) + throws HibernateException { + writeIndex( collection, collection.queuedAdditionIterator(), id, session ); + } + + private void writeIndex(PersistentCollection collection, Iterator entries, Serializable id, SessionImplementor session) { // If one-to-many and inverse, still need to create the index. See HHH-5732. if ( isInverse && hasIndex && !indexContainsFormula ) { try { - Iterator entries = collection.entries( this ); if ( entries.hasNext() ) { Expectation expectation = Expectations.appropriateExpectation( getUpdateCheckStyle() ); int i = 0; diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/onetomany/Comment.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/onetomany/Comment.java new file mode 100644 index 0000000000..ee9e1ef1fb --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/onetomany/Comment.java @@ -0,0 +1,67 @@ +package org.hibernate.test.annotations.onetomany; + +import javax.persistence.Column; +import javax.persistence.DiscriminatorColumn; +import javax.persistence.DiscriminatorType; +import javax.persistence.DiscriminatorValue; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Inheritance; +import javax.persistence.InheritanceType; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; + +@Entity(name="Comment") +@Inheritance(strategy = InheritanceType.SINGLE_TABLE) +@DiscriminatorColumn(name = "DTYPE", discriminatorType= DiscriminatorType.STRING, length = 3) +@DiscriminatorValue(value = "WPT") +public class Comment { + + private Long id; + private Post post; + private String name; + private Forum forum; + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + @Column(name = "id", updatable = false, insertable = false) + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + @ManyToOne(optional=true,fetch=FetchType.LAZY) + @JoinColumn(name="FK_PostId", nullable=true, insertable=true,updatable=false) + public Post getPost() { + return post; + } + + public void setPost(Post family) { + this.post = family; + } + + @ManyToOne(optional=true,fetch=FetchType.LAZY) + @JoinColumn(name="FK_ForumId", nullable=true, insertable=true,updatable=false) + public Forum getForum() { + return forum; + } + + public void setForum(Forum forum) { + this.forum = forum; + } + + @Column + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/onetomany/Forum.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/onetomany/Forum.java new file mode 100644 index 0000000000..b0aefbe57f --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/onetomany/Forum.java @@ -0,0 +1,68 @@ +package org.hibernate.test.annotations.onetomany; + +import java.util.ArrayList; +import java.util.List; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import javax.persistence.OrderColumn; + +import org.hibernate.annotations.LazyCollection; +import org.hibernate.annotations.LazyCollectionOption; + +@Entity(name="Forum") +public class Forum{ + + private Long id; + private String name; + protected List posts = new ArrayList(); + protected List users = new ArrayList(); + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + @Column(name = "id", updatable = false, insertable = false) + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + @OneToMany(mappedBy = "forum", cascade = CascadeType.ALL , orphanRemoval = false, fetch = FetchType.LAZY) + @LazyCollection(LazyCollectionOption.EXTRA) + @OrderColumn(name = "idx2") + public List getPosts() { + return posts; + } + + public void setPosts(List children) { + this.posts = children; + } + + @OneToMany(mappedBy = "forum", cascade = CascadeType.ALL , orphanRemoval = true, fetch = FetchType.LAZY) + @LazyCollection(LazyCollectionOption.EXTRA) + @OrderColumn(name = "idx3") + public List getUsers() { + return users; + } + + public void setUsers(List users) { + this.users = users; + } + + @Column + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/onetomany/OrderByTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/onetomany/OrderByTest.java index 4f6366a307..15aa425284 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/onetomany/OrderByTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/onetomany/OrderByTest.java @@ -365,13 +365,54 @@ public class OrderByTest extends BaseCoreFunctionalTestCase { fail(e.getMessage()); } } + + @Test + @TestForIssue( jiraKey = "HHH-8083" ) + public void testInverseIndexCascaded() { + final Session s = openSession(); + s.getTransaction().begin(); + + Forum forum = new Forum(); + forum.setName( "forum1" ); + forum = (Forum) s.merge( forum ); + + s.flush(); + s.clear(); + sessionFactory().getCache().evictEntityRegions(); + + forum = (Forum) s.get( Forum.class, forum.getId() ); + + final Post post = new Post(); + post.setName( "post1" ); + post.setForum( forum ); + forum.getPosts().add( post ); + + final User user = new User(); + user.setName( "john" ); + user.setForum( forum ); + forum.getUsers().add( user ); + + forum = (Forum) s.merge( forum ); + + s.flush(); + s.clear(); + sessionFactory().getCache().evictEntityRegions(); + + forum = (Forum) s.get( Forum.class, forum.getId() ); + + assertEquals( 1, forum.getPosts().size() ); + assertEquals( "post1", forum.getPosts().get( 0 ).getName() ); + assertEquals( 1, forum.getUsers().size() ); + assertEquals( "john", forum.getUsers().get( 0 ).getName() ); + } @Override protected Class[] getAnnotatedClasses() { return new Class[] { Order.class, OrderItem.class, Zoo.class, Tiger.class, Monkey.class, Visitor.class, Box.class, Item.class, - BankAccount.class, Transaction.class + BankAccount.class, Transaction.class, + Comment.class, Forum.class, Post.class, User.class }; } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/onetomany/Post.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/onetomany/Post.java new file mode 100644 index 0000000000..1a22d8ae4c --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/onetomany/Post.java @@ -0,0 +1,32 @@ +package org.hibernate.test.annotations.onetomany; + +import java.util.ArrayList; +import java.util.List; + +import javax.persistence.CascadeType; +import javax.persistence.DiscriminatorValue; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.OneToMany; +import javax.persistence.OrderColumn; + +import org.hibernate.annotations.LazyCollection; +import org.hibernate.annotations.LazyCollectionOption; + +@Entity(name = "Post") +@DiscriminatorValue(value = "WCT") +public class Post extends Comment{ + + protected List comments = new ArrayList(); + + @OneToMany(mappedBy = "post", cascade = CascadeType.ALL , orphanRemoval = false, fetch = FetchType.LAZY) + @LazyCollection(LazyCollectionOption.EXTRA) + @OrderColumn(name = "idx") + public List getComments() { + return comments; + } + + public void setComments(List comments) { + this.comments = comments; + } +} \ No newline at end of file diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/onetomany/User.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/onetomany/User.java new file mode 100644 index 0000000000..e7a15be980 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/onetomany/User.java @@ -0,0 +1,44 @@ +package org.hibernate.test.annotations.onetomany; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; + +@Entity(name="Userx") +public class User { + private Long id; + private String name; + private Forum forum; + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + @Column(name = "id", updatable = false, insertable = false) + public Long getId() { + return id; + } + public void setId(Long id) { + this.id = id; + } + + @Column + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + + @ManyToOne(optional=false,fetch=FetchType.LAZY) + @JoinColumn(name="FK_ForumId", nullable=false, insertable=true,updatable=false) + public Forum getForum() { + return forum; + } + public void setForum(Forum forum) { + this.forum = forum; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java b/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java index cd85045871..911b004260 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java +++ b/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java @@ -849,5 +849,10 @@ public class GoofyPersisterClassProvider implements PersisterClassResolver { public int getBatchSize() { return 0; } + + @Override + public void processQueuedOps(PersistentCollection collection, Serializable key, SessionImplementor session) + throws HibernateException { + } } } From 80b07c028158ce88a41238f98633a0299311fa25 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Wed, 22 May 2013 12:41:37 -0500 Subject: [PATCH 15/36] Update CONTRIBUTING.md Added note about checkstyles --- CONTRIBUTING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0ad0a0323a..de43db075a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -35,6 +35,7 @@ Do yo thing! up the related commits and display them on the Jira issue. * Make sure you have added the necessary tests for your changes. * Run _all_ the tests to assure nothing else was accidentally broken. +* Make sure your source does not violate the checkstyles. _Prior to commiting, if you want to pull in the latest upstream changes (highly appreciated btw), please use rebasing rather than merging. Merging creates From 7678fae6a52b778bf73b50163963a53e1ecf83ca Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Thu, 23 May 2013 15:07:48 -0400 Subject: [PATCH 16/36] HHH-8144 corrected rsync args --- release/release.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/release.gradle b/release/release.gradle index 54affb609b..edeb75ee29 100644 --- a/release/release.gradle +++ b/release/release.gradle @@ -149,7 +149,7 @@ task uploadDocumentation(type:Exec, dependsOn: buildDocumentation) { final String url = 'filemgmt.jboss.org:/docs_htdocs/hibernate/'; executable 'rsync' - args '-rv', '--links', '--protocol=28', "${documentationUploadStagingDir.absolutePath}/", url + args '-avz', '--links', '--protocol=28', "${documentationUploadStagingDir.absolutePath}/", url doFirst { if ( version.endsWith( "SNAPSHOT" ) ) { From 5ea40ce3f5ff3a122ff168bc6b3301cc04b241dd Mon Sep 17 00:00:00 2001 From: Strong Liu Date: Thu, 23 May 2013 16:09:28 -0700 Subject: [PATCH 17/36] HHH-8266 Binding of named-stored-procedure XML element tries to create duplicate --- .../org/hibernate/cfg/AnnotationBinder.java | 17 +++++++++-------- .../java/org/hibernate/cfg/Configuration.java | 18 +++++++++++++++--- .../main/java/org/hibernate/cfg/Mappings.java | 11 +++++++++++ .../hibernate/cfg/annotations/QueryBinder.java | 9 +++++++-- 4 files changed, 42 insertions(+), 13 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java index 875cc3d769..3d982551b6 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java @@ -272,7 +272,7 @@ public final class AnnotationBinder { (List) defaults.get( NamedStoredProcedureQuery.class ); if ( annotations != null ) { for ( NamedStoredProcedureQuery annotation : annotations ) { - bindNamedStoredProcedureQuery( mappings, annotation ); + bindNamedStoredProcedureQuery( mappings, annotation, true ); } } } @@ -281,7 +281,7 @@ public final class AnnotationBinder { (List) defaults.get( NamedStoredProcedureQueries.class ); if ( annotations != null ) { for ( NamedStoredProcedureQueries annotation : annotations ) { - bindNamedStoredProcedureQueries( mappings, annotation ); + bindNamedStoredProcedureQueries( mappings, annotation, true ); } } } @@ -392,26 +392,27 @@ public final class AnnotationBinder { } // NamedStoredProcedureQuery handling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - bindNamedStoredProcedureQuery( mappings, annotatedElement.getAnnotation( NamedStoredProcedureQuery.class ) ); + bindNamedStoredProcedureQuery( mappings, annotatedElement.getAnnotation( NamedStoredProcedureQuery.class ), false ); // NamedStoredProcedureQueries handling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bindNamedStoredProcedureQueries( mappings, - annotatedElement.getAnnotation( NamedStoredProcedureQueries.class ) + annotatedElement.getAnnotation( NamedStoredProcedureQueries.class ), + false ); } - private static void bindNamedStoredProcedureQueries(Mappings mappings, NamedStoredProcedureQueries annotation) { + private static void bindNamedStoredProcedureQueries(Mappings mappings, NamedStoredProcedureQueries annotation, boolean isDefault) { if ( annotation != null ) { for ( NamedStoredProcedureQuery queryAnnotation : annotation.value() ) { - bindNamedStoredProcedureQuery( mappings, queryAnnotation ); + bindNamedStoredProcedureQuery( mappings, queryAnnotation, isDefault ); } } } - private static void bindNamedStoredProcedureQuery(Mappings mappings, NamedStoredProcedureQuery annotation) { + private static void bindNamedStoredProcedureQuery(Mappings mappings, NamedStoredProcedureQuery annotation, boolean isDefault) { if ( annotation != null ) { - QueryBinder.bindNamedStoredProcedureQuery( annotation, mappings ); + QueryBinder.bindNamedStoredProcedureQuery( annotation, mappings, isDefault ); } } 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 83a1949894..ef240e4cb7 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java @@ -263,6 +263,8 @@ public class Configuration implements Serializable { private Set defaultNamedQueryNames; private Set defaultNamedNativeQueryNames; private Set defaultSqlResultSetMappingNames; + private Set defaultNamedProcedure; + private Set defaultNamedGenerators; private Map generatorTables; private Map> uniqueConstraintHoldersByTable; @@ -339,6 +341,7 @@ public class Configuration implements Serializable { defaultNamedQueryNames = new HashSet(); defaultNamedNativeQueryNames = new HashSet(); defaultSqlResultSetMappingNames = new HashSet(); + defaultNamedProcedure = new HashSet( ); defaultNamedGenerators = new HashSet(); uniqueConstraintHoldersByTable = new HashMap>(); jpaIndexHoldersByTable = new HashMap>( ); @@ -2890,16 +2893,25 @@ public class Configuration implements Serializable { public void addNamedProcedureCallDefinition(NamedProcedureCallDefinition definition) throws DuplicateMappingException { final String name = definition.getRegisteredName(); - final NamedProcedureCallDefinition previous = namedProcedureCallMap.put( name, definition ); - if ( previous != null ) { - throw new DuplicateMappingException( "named stored procedure query", name ); + if ( !defaultNamedProcedure.contains( name ) ) { + final NamedProcedureCallDefinition previous = namedProcedureCallMap.put( name, definition ); + if ( previous != null ) { + throw new DuplicateMappingException( "named stored procedure query", name ); + } } } + @Override + public void addDefaultNamedProcedureCallDefinition(NamedProcedureCallDefinition definition) + throws DuplicateMappingException { + addNamedProcedureCallDefinition( definition ); + defaultNamedProcedure.add( definition.getRegisteredName() ); + } @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 ); 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 6ea34e60b1..07c2327777 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/Mappings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/Mappings.java @@ -349,6 +349,17 @@ public interface Mappings { */ public void addNamedProcedureCallDefinition(NamedProcedureCallDefinition definition) throws DuplicateMappingException; + /** + * Adds metadata for a named stored procedure call to this repository. + * + * @param definition The procedure call information + * + * @throws DuplicateMappingException If a query already exists with that name. + */ + public void addDefaultNamedProcedureCallDefinition(NamedProcedureCallDefinition definition) throws DuplicateMappingException; + + + /** * Adds metadata for a named entity graph to this repository * diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/QueryBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/QueryBinder.java index 28f8a0cdd9..ddeefb6ca9 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/QueryBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/QueryBinder.java @@ -318,7 +318,7 @@ public abstract class QueryBinder { } } - public static void bindNamedStoredProcedureQuery(NamedStoredProcedureQuery annotation, Mappings mappings) { + public static void bindNamedStoredProcedureQuery(NamedStoredProcedureQuery annotation, Mappings mappings, boolean isDefault) { if ( annotation == null ) { return; } @@ -328,7 +328,12 @@ public abstract class QueryBinder { } final NamedProcedureCallDefinition def = new NamedProcedureCallDefinition( annotation ); - mappings.addNamedProcedureCallDefinition( def ); + + if(isDefault){ + mappings.addDefaultNamedProcedureCallDefinition( def ); + } else{ + mappings.addNamedProcedureCallDefinition( def ); + } LOG.debugf( "Bound named stored procedure query : %s => %s", def.getRegisteredName(), def.getProcedureName() ); } From a5862017d8a1127c32289767334937adc123cf41 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Fri, 24 May 2013 12:01:54 -0500 Subject: [PATCH 18/36] HHH-7841 - Redesign Loader --- .../loader/entity/AbstractEntityLoader.java | 8 +--- .../persister/entity/NamedQueryLoader.java | 41 +++++++++++-------- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/loader/entity/AbstractEntityLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/entity/AbstractEntityLoader.java index ef798947bf..ebd1fca214 100755 --- a/hibernate-core/src/main/java/org/hibernate/loader/entity/AbstractEntityLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/entity/AbstractEntityLoader.java @@ -57,17 +57,13 @@ public abstract class AbstractEntityLoader extends OuterJoinLoader } - /** - * {@inheritDoc} - */ + @Override public Object load(Serializable id, Object optionalObject, SessionImplementor session) { // this form is deprecated! return load( id, optionalObject, session, LockOptions.NONE ); } - /** - * {@inheritDoc} - */ + @Override public Object load(Serializable id, Object optionalObject, SessionImplementor session, LockOptions lockOptions) { return load( session, id, optionalObject, id, lockOptions ); } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/NamedQueryLoader.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/NamedQueryLoader.java index c6cdae1e38..4c61041cac 100755 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/NamedQueryLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/NamedQueryLoader.java @@ -25,56 +25,65 @@ package org.hibernate.persister.entity; import java.io.Serializable; -import org.jboss.logging.Logger; - import org.hibernate.FlushMode; import org.hibernate.LockOptions; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.internal.AbstractQueryImpl; +import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.loader.entity.UniqueEntityLoader; /** - * Not really a Loader, just a wrapper around a - * named query. + * Not really a Loader, just a wrapper around a named query. Used when the metadata has named a query to use for + * loading an entity (using {@link org.hibernate.annotations.Loader} or {@code }). * * @author Gavin King * @author Steve Ebersole */ public final class NamedQueryLoader implements UniqueEntityLoader { + private static final CoreMessageLogger LOG = CoreLogging.messageLogger( NamedQueryLoader.class ); + private final String queryName; private final EntityPersister persister; - private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, NamedQueryLoader.class.getName()); - + /** + * Constructs the NamedQueryLoader + * + * @param queryName The name of the named query to use + * @param persister The corresponding persister for the entity we are loading + */ public NamedQueryLoader(String queryName, EntityPersister persister) { super(); this.queryName = queryName; this.persister = persister; } + @Override public Object load(Serializable id, Object optionalObject, SessionImplementor session, LockOptions lockOptions) { - if (lockOptions != null) LOG.debug("Ignoring lock-options passed to named query loader"); + if ( lockOptions != null ) { + LOG.debug( "Ignoring lock-options passed to named query loader" ); + } return load( id, optionalObject, session ); } + @Override public Object load(Serializable id, Object optionalObject, SessionImplementor session) { - LOG.debugf("Loading entity: %s using named query: %s", persister.getEntityName(), queryName); + LOG.debugf( "Loading entity: %s using named query: %s", persister.getEntityName(), queryName ); - AbstractQueryImpl query = (AbstractQueryImpl) session.getNamedQuery(queryName); + // IMPL NOTE: essentially we perform the named query (which loads the entity into the PC), and then + // do an internal lookup of the entity from the PC. + + final AbstractQueryImpl query = (AbstractQueryImpl) session.getNamedQuery( queryName ); if ( query.hasNamedParameters() ) { - query.setParameter( - query.getNamedParameters()[0], - id, - persister.getIdentifierType() - ); + query.setParameter( query.getNamedParameters()[0], id, persister.getIdentifierType() ); } else { query.setParameter( 0, id, persister.getIdentifierType() ); } - query.setOptionalId(id); + + query.setOptionalId( id ); query.setOptionalEntityName( persister.getEntityName() ); - query.setOptionalObject(optionalObject); + query.setOptionalObject( optionalObject ); query.setFlushMode( FlushMode.MANUAL ); query.list(); From 875495439ed5cdc63d4af091c9d3ec4c7c32e8d8 Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Sat, 25 May 2013 20:24:53 -0400 Subject: [PATCH 19/36] HHH-8269 DenormalizedTable FK constraint names can be too long --- .../src/main/java/org/hibernate/mapping/DenormalizedTable.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/DenormalizedTable.java b/hibernate-core/src/main/java/org/hibernate/mapping/DenormalizedTable.java index b8f38749eb..b5af82a86d 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/DenormalizedTable.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/DenormalizedTable.java @@ -49,7 +49,7 @@ public class DenormalizedTable extends Table { while ( iter.hasNext() ) { ForeignKey fk = (ForeignKey) iter.next(); createForeignKey( - fk.getName() + Integer.toHexString( getName().hashCode() ), + Constraint.generateName( fk.generatedConstraintNamePrefix(), this, fk.getColumns() ), fk.getColumns(), fk.getReferencedEntityName() ); From 51834421c58d5270762f78e7107730569ebea6ce Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Sat, 25 May 2013 20:29:09 -0400 Subject: [PATCH 20/36] HHH-8273 Incorrect "unique-key" naming comment in docs --- .../src/main/docbook/devguide/en-US/Database_Access.xml | 5 ++--- .../src/main/docbook/manual/en-US/content/toolset_guide.xml | 6 ++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/documentation/src/main/docbook/devguide/en-US/Database_Access.xml b/documentation/src/main/docbook/devguide/en-US/Database_Access.xml index 26862e3ddb..481a7e758c 100644 --- a/documentation/src/main/docbook/devguide/en-US/Database_Access.xml +++ b/documentation/src/main/docbook/devguide/en-US/Database_Access.xml @@ -522,9 +522,8 @@ The and attributes generate constraints on table columns. - The unique-key attribute groups columns in a single, unique key constraint. Currently, the specified value - of the unique-key attribute does not name the constraint in the generated DDL. It only groups the columns in - the mapping file. + The unique-key attribute groups columns in a single, unique key constraint. The attribute overrides + the name of any generated unique key constraint. diff --git a/documentation/src/main/docbook/manual/en-US/content/toolset_guide.xml b/documentation/src/main/docbook/manual/en-US/content/toolset_guide.xml index 64df975afd..dad90530dc 100644 --- a/documentation/src/main/docbook/manual/en-US/content/toolset_guide.xml +++ b/documentation/src/main/docbook/manual/en-US/content/toolset_guide.xml @@ -95,10 +95,8 @@ A unique-key attribute can be used to group columns in - a single, unique key constraint. Currently, the specified value of the - unique-key attribute is not used - to name the constraint in the generated DDL. It is only used to group the columns in - the mapping file. + a single, unique key constraint. The attribute overrides + the name of any generated unique key constraint. From 3bc26b64488e92ef0f2251d6f9c8e329924cc7eb Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Wed, 29 May 2013 12:22:13 -0500 Subject: [PATCH 21/36] HHH-8270 - Support for accessing JPA schema export script files specified by URL --- .../schemagen/GenerationSourceFromScript.java | 18 +- .../schemagen/GenerationTargetToScript.java | 132 +---- .../schemagen/JpaSchemaGenerator.java | 451 ++++++++++++------ .../internal/schemagen/ScriptSourceInput.java | 48 ++ ...ut.java => ScriptSourceInputFromFile.java} | 22 +- ....java => ScriptSourceInputFromReader.java} | 11 +- .../schemagen/ScriptSourceInputFromUrl.java | 74 +++ ...riptInput.java => ScriptTargetOutput.java} | 18 +- .../schemagen/ScriptTargetOutputToFile.java | 79 +++ .../schemagen/ScriptTargetOutputToUrl.java | 82 ++++ .../schemagen/ScriptTargetOutputToWriter.java | 66 +++ .../jpa/internal/schemagen/package-info.java | 7 + .../hibernate/jpa/test/schemagen/Item.java | 69 +++ .../schemagen/JpaSchemaGeneratorTest.java | 159 ++++++ .../test/schemagen/create-script-source.sql | 1 + .../jpa/test/schemagen/drop-script-source.sql | 2 + .../jpa/test/schemagen/load-script-source.sql | 1 + 17 files changed, 928 insertions(+), 312 deletions(-) create mode 100644 hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/ScriptSourceInput.java rename hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/{SqlScriptFileInput.java => ScriptSourceInputFromFile.java} (75%) rename hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/{SqlScriptReaderInput.java => ScriptSourceInputFromReader.java} (83%) create mode 100644 hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/ScriptSourceInputFromUrl.java rename hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/{SqlScriptInput.java => ScriptTargetOutput.java} (77%) create mode 100644 hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/ScriptTargetOutputToFile.java create mode 100644 hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/ScriptTargetOutputToUrl.java create mode 100644 hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/ScriptTargetOutputToWriter.java create mode 100644 hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/package-info.java create mode 100644 hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/schemagen/Item.java create mode 100644 hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/schemagen/JpaSchemaGeneratorTest.java create mode 100644 hibernate-entitymanager/src/test/resources/org/hibernate/jpa/test/schemagen/create-script-source.sql create mode 100644 hibernate-entitymanager/src/test/resources/org/hibernate/jpa/test/schemagen/drop-script-source.sql create mode 100644 hibernate-entitymanager/src/test/resources/org/hibernate/jpa/test/schemagen/load-script-source.sql diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/GenerationSourceFromScript.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/GenerationSourceFromScript.java index 3c45e56a9c..1dda8e3293 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/GenerationSourceFromScript.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/GenerationSourceFromScript.java @@ -23,8 +23,6 @@ */ package org.hibernate.jpa.internal.schemagen; -import java.io.Reader; - import org.hibernate.tool.hbm2ddl.ImportSqlCommandExtractor; /** @@ -33,28 +31,22 @@ import org.hibernate.tool.hbm2ddl.ImportSqlCommandExtractor; * @author Steve Ebersole */ public class GenerationSourceFromScript implements GenerationSource { - private final SqlScriptInput reader; + private final ScriptSourceInput inputSource; private final ImportSqlCommandExtractor scriptCommandExtractor; - public GenerationSourceFromScript(Object scriptSourceSetting, ImportSqlCommandExtractor scriptCommandExtractor) { + public GenerationSourceFromScript(ScriptSourceInput inputSource, ImportSqlCommandExtractor scriptCommandExtractor) { + this.inputSource = inputSource; this.scriptCommandExtractor = scriptCommandExtractor; - - if ( Reader.class.isInstance( scriptSourceSetting ) ) { - reader = new SqlScriptReaderInput( (Reader) scriptSourceSetting ); - } - else { - reader = new SqlScriptFileInput( scriptSourceSetting.toString() ); - } } @Override public Iterable getCommands() { - return reader.read( scriptCommandExtractor ); + return inputSource.read( scriptCommandExtractor ); } @Override public void release() { - reader.release(); + inputSource.release(); } } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/GenerationTargetToScript.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/GenerationTargetToScript.java index 677e38cb07..5dd7cf9668 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/GenerationTargetToScript.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/GenerationTargetToScript.java @@ -23,15 +23,6 @@ */ package org.hibernate.jpa.internal.schemagen; -import javax.persistence.PersistenceException; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.io.Writer; - -import org.jboss.logging.Logger; - -import org.hibernate.jpa.AvailableSettings; import org.hibernate.jpa.SchemaGenAction; /** @@ -40,59 +31,17 @@ import org.hibernate.jpa.SchemaGenAction; * @author Steve Ebersole */ class GenerationTargetToScript implements GenerationTarget { - private static final Logger log = Logger.getLogger( GenerationTargetToScript.class ); - - private final ScriptTargetTarget createScriptTarget; - private final ScriptTargetTarget dropScriptTarget; + private final ScriptTargetOutput createScriptTarget; + private final ScriptTargetOutput dropScriptTarget; private final SchemaGenAction scriptsAction; public GenerationTargetToScript( - Object createScriptTargetSetting, - Object dropScriptTargetSetting, + ScriptTargetOutput createScriptTarget, + ScriptTargetOutput dropScriptTarget, SchemaGenAction scriptsAction) { + this.createScriptTarget = createScriptTarget; + this.dropScriptTarget = dropScriptTarget; this.scriptsAction = scriptsAction; - - if ( scriptsAction.includesCreate() ) { - if ( Writer.class.isInstance( createScriptTargetSetting ) ) { - createScriptTarget = new WriterScriptTarget( (Writer) createScriptTargetSetting ); - } - else { - createScriptTarget = new FileScriptTarget( createScriptTargetSetting.toString() ); - } - } - else { - if ( createScriptTargetSetting != null ) { - // the wording in the spec hints that this maybe should be an error, but does not explicitly - // call out an exception to use. - log.debugf( - "Value was specified for '%s' [%s], but create scripting was not requested", - AvailableSettings.SCHEMA_GEN_SCRIPTS_CREATE_TARGET, - createScriptTargetSetting - ); - } - createScriptTarget = null; - } - - if ( scriptsAction.includesDrop() ) { - if ( Writer.class.isInstance( dropScriptTargetSetting ) ) { - dropScriptTarget = new WriterScriptTarget( (Writer) dropScriptTargetSetting ); - } - else { - dropScriptTarget = new FileScriptTarget( dropScriptTargetSetting.toString() ); - } - } - else { - if ( dropScriptTargetSetting != null ) { - // the wording in the spec hints that this maybe should be an error, but does not explicitly - // call out an exception to use. - log.debugf( - "Value was specified for '%s' [%s], but drop scripting was not requested", - AvailableSettings.SCHEMA_GEN_SCRIPTS_DROP_TARGET, - dropScriptTargetSetting - ); - } - dropScriptTarget = null; - } } @Override @@ -123,73 +72,4 @@ class GenerationTargetToScript implements GenerationTarget { dropScriptTarget.release(); } - /** - * Internal contract for handling Writer/File differences - */ - private static interface ScriptTargetTarget { - public void accept(String command); - public void release(); - } - - private static class WriterScriptTarget implements ScriptTargetTarget { - private final Writer writer; - - public WriterScriptTarget(Writer writer) { - this.writer = writer; - } - - @Override - public void accept(String command) { - try { - writer.write( command ); - writer.flush(); - } - catch (IOException e) { - throw new PersistenceException( "Could not write to target script file", e ); - } - } - - @Override - public void release() { - // nothing to do for a supplied writer - } - - protected Writer writer() { - return writer; - } - } - - private static class FileScriptTarget extends WriterScriptTarget implements ScriptTargetTarget { - public FileScriptTarget(String fileUrl) { - super( toFileWriter( fileUrl ) ); - } - - @Override - public void release() { - try { - writer().close(); - } - catch (IOException e) { - throw new PersistenceException( "Unable to close file writer : " + e.toString() ); - } - } - } - - @SuppressWarnings("ResultOfMethodCallIgnored") - private static Writer toFileWriter(String fileUrl) { - final File file = new File( fileUrl ); - try { - // best effort, since this is very well not allowed in EE environments - file.createNewFile(); - } - catch (Exception e) { - log.debug( "Exception calling File#createNewFile : " + e.toString() ); - } - try { - return new FileWriter( file ); - } - catch (IOException e) { - throw new PersistenceException( "Unable to open specified script target file for writing : " + fileUrl, e ); - } - } } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/JpaSchemaGenerator.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/JpaSchemaGenerator.java index 8f692ecbb9..d05e9e4efe 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/JpaSchemaGenerator.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/JpaSchemaGenerator.java @@ -24,7 +24,11 @@ package org.hibernate.jpa.internal.schemagen; import javax.persistence.PersistenceException; +import java.io.File; import java.io.Reader; +import java.io.Writer; +import java.net.MalformedURLException; +import java.net.URL; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.SQLException; @@ -38,6 +42,7 @@ import java.util.List; import org.jboss.logging.Logger; import org.hibernate.HibernateException; +import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.boot.registry.selector.spi.StrategySelector; import org.hibernate.cfg.Configuration; import org.hibernate.dialect.Dialect; @@ -64,188 +69,328 @@ import org.hibernate.tool.hbm2ddl.ImportSqlCommandExtractor; public class JpaSchemaGenerator { private static final Logger log = Logger.getLogger( JpaSchemaGenerator.class ); + private JpaSchemaGenerator() { + } + public static void performGeneration(Configuration hibernateConfiguration, ServiceRegistry serviceRegistry) { + new Generation( serviceRegistry ).execute( hibernateConfiguration ); + } - // First, determine the actions (if any) to be performed ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + /** + * Defines the process of performing a schema generation + */ + public static class Generation { + private final ServiceRegistry serviceRegistry; + private final ImportSqlCommandExtractor scriptCommandExtractor; + private final ClassLoaderService classLoaderService; - final SchemaGenAction databaseAction = SchemaGenAction.interpret( - hibernateConfiguration.getProperty( AvailableSettings.SCHEMA_GEN_DATABASE_ACTION ) - ); - final SchemaGenAction scriptsAction = SchemaGenAction.interpret( - hibernateConfiguration.getProperty( AvailableSettings.SCHEMA_GEN_SCRIPTS_ACTION ) - ); - - if ( databaseAction == SchemaGenAction.NONE && scriptsAction == SchemaGenAction.NONE ) { - // no actions needed - return; + /** + * Constructs a generation process + * + * @param serviceRegistry The Hibernate service registry to use + */ + public Generation(ServiceRegistry serviceRegistry) { + this.serviceRegistry = serviceRegistry; + this.scriptCommandExtractor = serviceRegistry.getService( ImportSqlCommandExtractor.class ); + this.classLoaderService = serviceRegistry.getService( ClassLoaderService.class ); } + /** + * Perform the generation, as indicated by the settings + * + * @param hibernateConfiguration + */ + public void execute(Configuration hibernateConfiguration) { + // First, determine the actions (if any) to be performed ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // Figure out the JDBC Connection context, if any ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - final JdbcConnectionContext jdbcConnectionContext = determineAppropriateJdbcConnectionContext( - hibernateConfiguration, - serviceRegistry - ); - - try { - final Dialect dialect = determineDialect( jdbcConnectionContext, hibernateConfiguration, serviceRegistry ); - - - // determine sources ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - final List createSourceList = databaseAction.includesCreate() || scriptsAction.includesCreate() - ? buildCreateSourceList( hibernateConfiguration, serviceRegistry, dialect ) - : Collections.emptyList(); - - final List dropSourceList = databaseAction.includesDrop() || scriptsAction.includesDrop() - ? buildDropSourceList( hibernateConfiguration, serviceRegistry, dialect ) - : Collections.emptyList(); - - - // determine targets ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - final GenerationTarget databaseTarget = new GenerationTargetToDatabase( jdbcConnectionContext, databaseAction ); - - final Object createScriptTargetSetting = hibernateConfiguration.getProperties().get( - AvailableSettings.SCHEMA_GEN_SCRIPTS_CREATE_TARGET + final SchemaGenAction databaseAction = SchemaGenAction.interpret( + hibernateConfiguration.getProperty( AvailableSettings.SCHEMA_GEN_DATABASE_ACTION ) ); - final Object dropScriptTargetSetting = hibernateConfiguration.getProperties().get( - AvailableSettings.SCHEMA_GEN_SCRIPTS_DROP_TARGET + final SchemaGenAction scriptsAction = SchemaGenAction.interpret( + hibernateConfiguration.getProperty( AvailableSettings.SCHEMA_GEN_SCRIPTS_ACTION ) ); - final GenerationTarget scriptsTarget = new GenerationTargetToScript( createScriptTargetSetting, dropScriptTargetSetting, scriptsAction ); - final List targets = Arrays.asList( databaseTarget, scriptsTarget ); - - - // See if native Hibernate schema generation has also been requested and warn the user if so... - - final String hbm2ddl = hibernateConfiguration.getProperty( org.hibernate.cfg.AvailableSettings.HBM2DDL_AUTO ); - if ( StringHelper.isNotEmpty( hbm2ddl ) ) { - log.warnf( - "Hibernate hbm2ddl-auto setting was specified [%s] in combination with JPA schema-generation; " + - "combination will likely cause trouble", - hbm2ddl - ); + if ( databaseAction == SchemaGenAction.NONE && scriptsAction == SchemaGenAction.NONE ) { + // no actions needed + return; } - // finally, do the generation + // Figure out the JDBC Connection context, if any ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + final JdbcConnectionContext jdbcConnectionContext = determineAppropriateJdbcConnectionContext( + hibernateConfiguration, + serviceRegistry + ); try { - doGeneration( createSourceList, dropSourceList, targets ); + final Dialect dialect = determineDialect( jdbcConnectionContext, hibernateConfiguration, serviceRegistry ); + + + // determine sources ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + final List createSourceList = databaseAction.includesCreate() || scriptsAction.includesCreate() + ? buildCreateSourceList( hibernateConfiguration, dialect ) + : Collections.emptyList(); + + final List dropSourceList = databaseAction.includesDrop() || scriptsAction.includesDrop() + ? buildDropSourceList( hibernateConfiguration, dialect ) + : Collections.emptyList(); + + + // determine targets ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + final GenerationTarget databaseTarget = new GenerationTargetToDatabase( jdbcConnectionContext, databaseAction ); + + final Object createScriptTargetSetting = hibernateConfiguration.getProperties().get( + AvailableSettings.SCHEMA_GEN_SCRIPTS_CREATE_TARGET + ); + final Object dropScriptTargetSetting = hibernateConfiguration.getProperties().get( + AvailableSettings.SCHEMA_GEN_SCRIPTS_DROP_TARGET + ); + final GenerationTarget scriptsTarget = new GenerationTargetToScript( + interpretScriptTargetSetting( + createScriptTargetSetting, + scriptsAction.includesCreate(), + AvailableSettings.SCHEMA_GEN_SCRIPTS_CREATE_TARGET + ), + interpretScriptTargetSetting( + dropScriptTargetSetting, + scriptsAction.includesDrop(), + AvailableSettings.SCHEMA_GEN_SCRIPTS_DROP_TARGET + ), + scriptsAction + ); + + final List targets = Arrays.asList( databaseTarget, scriptsTarget ); + + + // See if native Hibernate schema generation has also been requested and warn the user if so... + + final String hbm2ddl = hibernateConfiguration.getProperty( org.hibernate.cfg.AvailableSettings.HBM2DDL_AUTO ); + if ( StringHelper.isNotEmpty( hbm2ddl ) ) { + log.warnf( + "Hibernate hbm2ddl-auto setting was specified [%s] in combination with JPA schema-generation; " + + "combination will likely cause trouble", + hbm2ddl + ); + } + + + // finally, do the generation + + try { + doGeneration( createSourceList, dropSourceList, targets ); + } + finally { + releaseTargets( targets ); + releaseSources( createSourceList ); + releaseSources( dropSourceList ); + } } finally { - releaseTargets( targets ); - releaseSources( createSourceList ); - releaseSources( dropSourceList ); + releaseJdbcConnectionContext( jdbcConnectionContext ); } } - finally { - releaseJdbcConnectionContext( jdbcConnectionContext ); - } - } - private static List buildCreateSourceList( - Configuration hibernateConfiguration, - ServiceRegistry serviceRegistry, - Dialect dialect) { - final List generationSourceList = new ArrayList(); - - final boolean createSchemas = ConfigurationHelper.getBoolean( - AvailableSettings.SCHEMA_GEN_CREATE_SCHEMAS, - hibernateConfiguration.getProperties(), - false - ); - if ( createSchemas ) { - generationSourceList.add( new CreateSchemaCommandSource( hibernateConfiguration, dialect ) ); - } - - SchemaGenSource sourceType = SchemaGenSource.interpret( - hibernateConfiguration.getProperty( AvailableSettings.SCHEMA_GEN_CREATE_SOURCE ) - ); - - final Object createScriptSourceSetting = hibernateConfiguration.getProperties().get( - AvailableSettings.SCHEMA_GEN_CREATE_SCRIPT_SOURCE - ); - - if ( sourceType == null ) { - if ( createScriptSourceSetting != null ) { - sourceType = SchemaGenSource.SCRIPT; + private ScriptTargetOutput interpretScriptTargetSetting( + Object scriptTargetSetting, + boolean actionIndicatedScripting, + String settingName) { + if ( actionIndicatedScripting ) { + if ( scriptTargetSetting == null ) { + throw new PersistenceException( "Scripting was requested, but no target was specified" ); + } + if ( Writer.class.isInstance( scriptTargetSetting ) ) { + return new ScriptTargetOutputToWriter( (Writer) scriptTargetSetting ); + } + else { + final String scriptTargetSettingString = scriptTargetSetting.toString(); + try { + final URL url = new URL( scriptTargetSettingString ); + return new ScriptTargetOutputToUrl( url ); + } + catch (MalformedURLException ignore) { + } + return new ScriptTargetOutputToFile( new File( scriptTargetSettingString ) ); + } } else { - sourceType = SchemaGenSource.METADATA; + if ( scriptTargetSetting != null ) { + // the wording in the spec hints that this maybe should be an error, but does not explicitly + // call out an exception to use. + log.debugf( + "Value was specified for '%s' [%s], but scripting action was not requested", + settingName, + scriptTargetSetting + ); + } + return NoOpScriptTargetOutput.INSTANCE; } } - final ImportSqlCommandExtractor scriptCommandExtractor = serviceRegistry.getService( ImportSqlCommandExtractor.class ); + private static class NoOpScriptTargetOutput implements ScriptTargetOutput { + /** + * Singleton access + */ + public static final NoOpScriptTargetOutput INSTANCE = new NoOpScriptTargetOutput(); - if ( sourceType == SchemaGenSource.METADATA ) { - generationSourceList.add( new GenerationSourceFromMetadata( hibernateConfiguration, dialect, true ) ); - } - else if ( sourceType == SchemaGenSource.SCRIPT ) { - generationSourceList.add( new GenerationSourceFromScript( createScriptSourceSetting, scriptCommandExtractor ) ); - } - else if ( sourceType == SchemaGenSource.METADATA_THEN_SCRIPT ) { - generationSourceList.add( new GenerationSourceFromMetadata( hibernateConfiguration, dialect, true ) ); - generationSourceList.add( new GenerationSourceFromScript( createScriptSourceSetting, scriptCommandExtractor ) ); - } - else if ( sourceType == SchemaGenSource.SCRIPT_THEN_METADATA ) { - generationSourceList.add( new GenerationSourceFromScript( createScriptSourceSetting, scriptCommandExtractor ) ); - generationSourceList.add( new GenerationSourceFromMetadata( hibernateConfiguration, dialect, true ) ); + @Override + public void accept(String command) { + } + + @Override + public void release() { + } } - final Object importScriptSetting = hibernateConfiguration.getProperties().get( - AvailableSettings.SCHEMA_GEN_LOAD_SCRIPT_SOURCE - ); - if ( importScriptSetting != null ) { - generationSourceList.add( new ImportScriptSource( importScriptSetting, scriptCommandExtractor ) ); + private List buildCreateSourceList(Configuration hibernateConfiguration, Dialect dialect) { + final List generationSourceList = new ArrayList(); + + // If we are asked to perform CREATE SCHEMA commands do them first ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + final boolean createSchemas = ConfigurationHelper.getBoolean( + AvailableSettings.SCHEMA_GEN_CREATE_SCHEMAS, + hibernateConfiguration.getProperties(), + false + ); + if ( createSchemas ) { + generationSourceList.add( new CreateSchemaCommandSource( hibernateConfiguration, dialect ) ); + } + + + // Next figure out the intended sources of generation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + SchemaGenSource sourceType = SchemaGenSource.interpret( + hibernateConfiguration.getProperty( AvailableSettings.SCHEMA_GEN_CREATE_SOURCE ) + ); + + final Object createScriptSourceSetting = hibernateConfiguration.getProperties().get( + AvailableSettings.SCHEMA_GEN_CREATE_SCRIPT_SOURCE + ); + + if ( sourceType == null ) { + if ( createScriptSourceSetting != null ) { + sourceType = SchemaGenSource.SCRIPT; + } + else { + sourceType = SchemaGenSource.METADATA; + } + } + + final boolean includesScripts = sourceType != SchemaGenSource.METADATA; + if ( includesScripts && createScriptSourceSetting == null ) { + throw new PersistenceException( + "Schema generation configuration indicated to include CREATE scripts, but no script was specified" + ); + } + final ScriptSourceInput scriptSourceInput = includesScripts + ? interpretScriptSourceSetting( createScriptSourceSetting ) + : null; + + if ( sourceType == SchemaGenSource.METADATA ) { + generationSourceList.add( new GenerationSourceFromMetadata( hibernateConfiguration, dialect, true ) ); + } + else if ( sourceType == SchemaGenSource.SCRIPT ) { + generationSourceList.add( new GenerationSourceFromScript( scriptSourceInput, scriptCommandExtractor ) ); + } + else if ( sourceType == SchemaGenSource.METADATA_THEN_SCRIPT ) { + generationSourceList.add( new GenerationSourceFromMetadata( hibernateConfiguration, dialect, true ) ); + generationSourceList.add( new GenerationSourceFromScript( scriptSourceInput, scriptCommandExtractor ) ); + } + else if ( sourceType == SchemaGenSource.SCRIPT_THEN_METADATA ) { + generationSourceList.add( new GenerationSourceFromScript( scriptSourceInput, scriptCommandExtractor ) ); + generationSourceList.add( new GenerationSourceFromMetadata( hibernateConfiguration, dialect, true ) ); + } + + + // finally, see if there is an import script specified ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + final Object importScriptSetting = hibernateConfiguration.getProperties().get( + AvailableSettings.SCHEMA_GEN_LOAD_SCRIPT_SOURCE + ); + if ( importScriptSetting != null ) { + final ScriptSourceInput importScriptInput = interpretScriptSourceSetting( importScriptSetting ); + generationSourceList.add( new ImportScriptSource( importScriptInput, scriptCommandExtractor ) ); + } + + return generationSourceList; } - return generationSourceList; - } - - private static List buildDropSourceList( - Configuration hibernateConfiguration, - ServiceRegistry serviceRegistry, - Dialect dialect) { - final List generationSourceList = new ArrayList(); - - SchemaGenSource sourceType = SchemaGenSource.interpret( - hibernateConfiguration.getProperty( AvailableSettings.SCHEMA_GEN_DROP_SOURCE ) - ); - - final Object dropScriptSourceSetting = hibernateConfiguration.getProperties().get( - AvailableSettings.SCHEMA_GEN_DROP_SCRIPT_SOURCE - ); - - if ( sourceType == null ) { - if ( dropScriptSourceSetting != null ) { - sourceType = SchemaGenSource.SCRIPT; + private ScriptSourceInput interpretScriptSourceSetting(Object scriptSourceSetting) { + if ( Reader.class.isInstance( scriptSourceSetting ) ) { + return new ScriptSourceInputFromReader( (Reader) scriptSourceSetting ); } else { - sourceType = SchemaGenSource.METADATA; + final String scriptSourceSettingString = scriptSourceSetting.toString(); + log.debugf( "Attempting to resolve script source setting : %s", scriptSourceSettingString ); + + // setting could be either: + // 1) string URL representation (i.e., "file://...") + // 2) relative file path (resource lookup) + // 3) absolute file path + + log.trace( "Trying as URL..." ); + // ClassLoaderService.locateResource() first tries the given resource name as url form... + final URL url = classLoaderService.locateResource( scriptSourceSettingString ); + if ( url != null ) { + return new ScriptSourceInputFromUrl( url ); + } + + // assume it is a File path + final File file = new File( scriptSourceSettingString ); + return new ScriptSourceInputFromFile( file ); } } - final ImportSqlCommandExtractor scriptCommandExtractor = serviceRegistry.getService( ImportSqlCommandExtractor.class ); + private List buildDropSourceList(Configuration hibernateConfiguration, Dialect dialect) { + final List generationSourceList = new ArrayList(); - if ( sourceType == SchemaGenSource.METADATA ) { - generationSourceList.add( new GenerationSourceFromMetadata( hibernateConfiguration, dialect, false ) ); - } - else if ( sourceType == SchemaGenSource.SCRIPT ) { - generationSourceList.add( new GenerationSourceFromScript( dropScriptSourceSetting, scriptCommandExtractor ) ); - } - else if ( sourceType == SchemaGenSource.METADATA_THEN_SCRIPT ) { - generationSourceList.add( new GenerationSourceFromMetadata( hibernateConfiguration, dialect, false ) ); - generationSourceList.add( new GenerationSourceFromScript( dropScriptSourceSetting, scriptCommandExtractor ) ); - } - else if ( sourceType == SchemaGenSource.SCRIPT_THEN_METADATA ) { - generationSourceList.add( new GenerationSourceFromScript( dropScriptSourceSetting, scriptCommandExtractor ) ); - generationSourceList.add( new GenerationSourceFromMetadata( hibernateConfiguration, dialect, false ) ); - } + SchemaGenSource sourceType = SchemaGenSource.interpret( + hibernateConfiguration.getProperty( AvailableSettings.SCHEMA_GEN_DROP_SOURCE ) + ); - return generationSourceList; + final Object dropScriptSourceSetting = hibernateConfiguration.getProperties().get( + AvailableSettings.SCHEMA_GEN_DROP_SCRIPT_SOURCE + ); + + if ( sourceType == null ) { + if ( dropScriptSourceSetting != null ) { + sourceType = SchemaGenSource.SCRIPT; + } + else { + sourceType = SchemaGenSource.METADATA; + } + } + + + final boolean includesScripts = sourceType != SchemaGenSource.METADATA; + if ( includesScripts && dropScriptSourceSetting == null ) { + throw new PersistenceException( + "Schema generation configuration indicated to include CREATE scripts, but no script was specified" + ); + } + final ScriptSourceInput scriptSourceInput = includesScripts + ? interpretScriptSourceSetting( dropScriptSourceSetting ) + : null; + + if ( sourceType == SchemaGenSource.METADATA ) { + generationSourceList.add( new GenerationSourceFromMetadata( hibernateConfiguration, dialect, false ) ); + } + else if ( sourceType == SchemaGenSource.SCRIPT ) { + generationSourceList.add( new GenerationSourceFromScript( scriptSourceInput, scriptCommandExtractor ) ); + } + else if ( sourceType == SchemaGenSource.METADATA_THEN_SCRIPT ) { + generationSourceList.add( new GenerationSourceFromMetadata( hibernateConfiguration, dialect, false ) ); + generationSourceList.add( new GenerationSourceFromScript( scriptSourceInput, scriptCommandExtractor ) ); + } + else if ( sourceType == SchemaGenSource.SCRIPT_THEN_METADATA ) { + generationSourceList.add( new GenerationSourceFromScript( scriptSourceInput, scriptCommandExtractor ) ); + generationSourceList.add( new GenerationSourceFromMetadata( hibernateConfiguration, dialect, false ) ); + } + + return generationSourceList; + } } private static JdbcConnectionContext determineAppropriateJdbcConnectionContext( @@ -383,7 +528,7 @@ public class JpaSchemaGenerator { private static Dialect determineDialectBasedOnJdbcMetadata( JdbcConnectionContext jdbcConnectionContext, ServiceRegistry serviceRegistry) { - DialectResolver dialectResolver = serviceRegistry.getService( DialectResolver.class ); + final DialectResolver dialectResolver = serviceRegistry.getService( DialectResolver.class ); try { final DatabaseMetaData databaseMetaData = jdbcConnectionContext.getJdbcConnection().getMetaData(); final Dialect dialect = dialectResolver.resolveDialect( databaseMetaData ); @@ -508,18 +653,12 @@ public class JpaSchemaGenerator { } private static class ImportScriptSource implements GenerationSource { - private final SqlScriptInput sourceReader; + private final ScriptSourceInput sourceReader; private final ImportSqlCommandExtractor scriptCommandExtractor; - public ImportScriptSource(Object scriptSourceSetting, ImportSqlCommandExtractor scriptCommandExtractor) { + public ImportScriptSource(ScriptSourceInput sourceReader, ImportSqlCommandExtractor scriptCommandExtractor) { + this.sourceReader = sourceReader; this.scriptCommandExtractor = scriptCommandExtractor; - - if ( Reader.class.isInstance( scriptSourceSetting ) ) { - sourceReader = new SqlScriptReaderInput( (Reader) scriptSourceSetting ); - } - else { - sourceReader = new SqlScriptFileInput( scriptSourceSetting.toString() ); - } } @Override diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/ScriptSourceInput.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/ScriptSourceInput.java new file mode 100644 index 0000000000..fac700f86e --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/ScriptSourceInput.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.internal.schemagen; + +import org.hibernate.tool.hbm2ddl.ImportSqlCommandExtractor; + +/** + * Contract for hiding the differences between a passed Reader, File or URL in terms of how we read input + * scripts. + * + * @author Steve Ebersole + */ +public interface ScriptSourceInput { + /** + * Read the abstracted script, using the given extractor to split up the input into individual commands. + * + * @param commandExtractor The extractor for individual commands within the input. + * + * @return The scripted commands + */ + public Iterable read(ImportSqlCommandExtractor commandExtractor); + + /** + * Release this input. + */ + public void release(); +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/SqlScriptFileInput.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/ScriptSourceInputFromFile.java similarity index 75% rename from hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/SqlScriptFileInput.java rename to hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/ScriptSourceInputFromFile.java index 6ef61de161..61bf58289c 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/SqlScriptFileInput.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/ScriptSourceInputFromFile.java @@ -32,16 +32,21 @@ import java.io.Reader; import org.jboss.logging.Logger; /** - * SqlScriptInput implementation for File references. A reader is opened here and then explicitly closed on + * ScriptSourceInput implementation for File references. A reader is opened here and then explicitly closed on * {@link #release}. * * @author Steve Ebersole */ -class SqlScriptFileInput extends SqlScriptReaderInput implements SqlScriptInput { - private static final Logger log = Logger.getLogger( SqlScriptFileInput.class ); +public class ScriptSourceInputFromFile extends ScriptSourceInputFromReader implements ScriptSourceInput { + private static final Logger log = Logger.getLogger( ScriptSourceInputFromFile.class ); - public SqlScriptFileInput(String fileUrl) { - super( toFileReader( fileUrl ) ); + /** + * Constructs a ScriptSourceInputFromFile + * + * @param file The file to read from + */ + public ScriptSourceInputFromFile(File file) { + super( toFileReader( file ) ); } @Override @@ -55,10 +60,9 @@ class SqlScriptFileInput extends SqlScriptReaderInput implements SqlScriptInput } @SuppressWarnings("ResultOfMethodCallIgnored") - private static Reader toFileReader(String fileUrl) { - final File file = new File( fileUrl ); + private static Reader toFileReader(File file) { if ( ! file.exists() ) { - log.warnf( "Specified schema generation script file [%s] did not exist for reading", fileUrl ); + log.warnf( "Specified schema generation script file [%s] did not exist for reading", file ); return new Reader() { @Override public int read(char[] cbuf, int off, int len) throws IOException { @@ -76,7 +80,7 @@ class SqlScriptFileInput extends SqlScriptReaderInput implements SqlScriptInput } catch (IOException e) { throw new PersistenceException( - "Unable to open specified script target file [" + fileUrl + "] for reading", + "Unable to open specified script target file [" + file + "] for reading", e ); } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/SqlScriptReaderInput.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/ScriptSourceInputFromReader.java similarity index 83% rename from hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/SqlScriptReaderInput.java rename to hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/ScriptSourceInputFromReader.java index 5b0bb7db48..da5f230730 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/SqlScriptReaderInput.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/ScriptSourceInputFromReader.java @@ -30,14 +30,19 @@ import java.util.Collections; import org.hibernate.tool.hbm2ddl.ImportSqlCommandExtractor; /** - * SqlScriptInput implementation for explicitly given Readers. The readers are not released by this class. + * ScriptSourceInput implementation for explicitly given Readers. The readers are not released by this class. * * @author Steve Ebersole */ -class SqlScriptReaderInput implements SqlScriptInput { +public class ScriptSourceInputFromReader implements ScriptSourceInput { private final Reader reader; - public SqlScriptReaderInput(Reader reader) { + /** + * Constructs a ScriptSourceInputFromReader + * + * @param reader The reader to read from + */ + public ScriptSourceInputFromReader(Reader reader) { this.reader = reader; } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/ScriptSourceInputFromUrl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/ScriptSourceInputFromUrl.java new file mode 100644 index 0000000000..9434e56f4e --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/ScriptSourceInputFromUrl.java @@ -0,0 +1,74 @@ +/* + * 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.internal.schemagen; + +import javax.persistence.PersistenceException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.URL; + +import org.jboss.logging.Logger; + +/** + * ScriptSourceInput implementation for URL references. A reader is opened here and then explicitly closed on + * {@link #release}. + * + * @author Christian Beikov + * @author Steve Ebersole + */ +public class ScriptSourceInputFromUrl extends ScriptSourceInputFromReader implements ScriptSourceInput { + private static final Logger log = Logger.getLogger( ScriptSourceInputFromFile.class ); + + /** + * Constructs a ScriptSourceInputFromUrl instance + * + * @param url The url to read from + */ + public ScriptSourceInputFromUrl(URL url) { + super( toReader( url ) ); + } + + @Override + public void release() { + try { + reader().close(); + } + catch (IOException e) { + log.warn( "Unable to close file reader for generation script source" ); + } + } + + private static Reader toReader(URL url) { + try { + return new InputStreamReader( url.openStream() ); + + } + catch (IOException e) { + throw new PersistenceException( + "Unable to open specified script source url [" + url + "] for reading" + ); + } + } +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/SqlScriptInput.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/ScriptTargetOutput.java similarity index 77% rename from hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/SqlScriptInput.java rename to hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/ScriptTargetOutput.java index 4d96f35d5d..0df131c508 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/SqlScriptInput.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/ScriptTargetOutput.java @@ -23,14 +23,22 @@ */ package org.hibernate.jpa.internal.schemagen; -import org.hibernate.tool.hbm2ddl.ImportSqlCommandExtractor; - /** - * Contract for handling Reader/File differences + * Contract for hiding the differences between a passed Writer, File or URL in terms of how we write output + * scripts. * * @author Steve Ebersole */ -public interface SqlScriptInput { - public Iterable read(ImportSqlCommandExtractor commandExtractor); +public interface ScriptTargetOutput { + /** + * Accept the given command and write it to the abstracted script + * + * @param command The command + */ + public void accept(String command); + + /** + * Release this output + */ public void release(); } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/ScriptTargetOutputToFile.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/ScriptTargetOutputToFile.java new file mode 100644 index 0000000000..29f5757fdb --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/ScriptTargetOutputToFile.java @@ -0,0 +1,79 @@ +/* + * 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.internal.schemagen; + +import javax.persistence.PersistenceException; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; + +import org.jboss.logging.Logger; + +import org.hibernate.jpa.internal.HEMLogging; + +/** + * ScriptTargetOutput implementation for writing to supplied File references + * + * @author Steve Ebersole + */ +public class ScriptTargetOutputToFile extends ScriptTargetOutputToWriter implements ScriptTargetOutput { + private static final Logger log = HEMLogging.logger( ScriptTargetOutputToFile.class ); + + /** + * Constructs a ScriptTargetOutputToFile + * + * @param file The file to write to + */ + public ScriptTargetOutputToFile(File file) { + super( toFileWriter( file ) ); + } + + @Override + public void release() { + try { + writer().close(); + } + catch (IOException e) { + throw new PersistenceException( "Unable to close file writer : " + e.toString() ); + } + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + static Writer toFileWriter(File file) { + try { + // best effort, since this is very well not allowed in EE environments + file.createNewFile(); + } + catch (Exception e) { + log.debug( "Exception calling File#createNewFile : " + e.toString() ); + } + try { + return new FileWriter( file ); + } + catch (IOException e) { + throw new PersistenceException( "Unable to open specified script target file for writing : " + file, e ); + } + } +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/ScriptTargetOutputToUrl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/ScriptTargetOutputToUrl.java new file mode 100644 index 0000000000..0f964c0d80 --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/ScriptTargetOutputToUrl.java @@ -0,0 +1,82 @@ +/* + * 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.internal.schemagen; + +import javax.persistence.PersistenceException; +import java.io.File; +import java.io.IOException; +import java.io.Writer; +import java.net.URISyntaxException; +import java.net.URL; + +import org.jboss.logging.Logger; + +import org.hibernate.jpa.internal.HEMLogging; + +/** + * ScriptTargetOutput implementation for writing to supplied URL references + * + * @author Steve Ebersole + */ +public class ScriptTargetOutputToUrl extends ScriptTargetOutputToWriter implements ScriptTargetOutput { + private static final Logger log = HEMLogging.logger( ScriptTargetOutputToUrl.class ); + + /** + * Constructs a ScriptTargetOutputToUrl + * + * @param url The url to write to + */ + public ScriptTargetOutputToUrl(URL url) { + super( toWriter( url ) ); + } + + @Override + public void release() { + try { + writer().close(); + } + catch (IOException e) { + throw new PersistenceException( "Unable to close file writer : " + e.toString() ); + } + } + + + private static Writer toWriter(URL url) { + log.debug( "Attempting to resolve writer for URL : " + url ); + // technically only "strings corresponding to file URLs" are supported, which I take to mean URLs whose + // protocol is "file" + try { + return ScriptTargetOutputToFile.toFileWriter( new File( url.toURI() ) ); + } + catch (URISyntaxException e) { + throw new PersistenceException( + String.format( + "Could not convert specified URL[%s] to a File reference", + url + ), + e + ); + } + } +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/ScriptTargetOutputToWriter.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/ScriptTargetOutputToWriter.java new file mode 100644 index 0000000000..af80540c4e --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/ScriptTargetOutputToWriter.java @@ -0,0 +1,66 @@ +/* + * 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.internal.schemagen; + +import javax.persistence.PersistenceException; +import java.io.IOException; +import java.io.Writer; + +/** + * ScriptTargetOutput implementation for supplied Writer references + * + * @author Steve Ebersole + */ +public class ScriptTargetOutputToWriter implements ScriptTargetOutput { + private final Writer writer; + + /** + * Constructs a ScriptTargetOutputToWriter + * + * @param writer The writer to write to + */ + public ScriptTargetOutputToWriter(Writer writer) { + this.writer = writer; + } + + @Override + public void accept(String command) { + try { + writer.write( command ); + writer.flush(); + } + catch (IOException e) { + throw new PersistenceException( "Could not write to target script file", e ); + } + } + + @Override + public void release() { + // nothing to do for a supplied writer + } + + protected Writer writer() { + return writer; + } +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/package-info.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/package-info.java new file mode 100644 index 0000000000..21da20a909 --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/package-info.java @@ -0,0 +1,7 @@ +/** + * Support for JPA 2.1 defined database schema generation. The main class in this package is + * {@link org.hibernate.jpa.internal.schemagen.JpaSchemaGenerator}. + *

+ * @see org.hibernate.jpa.SchemaGenAction + */ +package org.hibernate.jpa.internal.schemagen; diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/schemagen/Item.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/schemagen/Item.java new file mode 100644 index 0000000000..f968abcfab --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/schemagen/Item.java @@ -0,0 +1,69 @@ +/* + * 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.schemagen; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import java.io.Serializable; + +/** + * Test entity + * + * @author Christian Beikov + * @author Steve Ebersole + */ +@Entity(name = "Item") +public class Item implements Serializable { + + private String name; + private String descr; + + public Item() { + } + + public Item(String name, String desc) { + this.name = name; + this.descr = desc; + } + + @Column(length = 200) + public String getDescr() { + return descr; + } + + public void setDescr(String desc) { + this.descr = desc; + } + + @Id + @Column(length = 30) + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/schemagen/JpaSchemaGeneratorTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/schemagen/JpaSchemaGeneratorTest.java new file mode 100644 index 0000000000..e38869fce7 --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/schemagen/JpaSchemaGeneratorTest.java @@ -0,0 +1,159 @@ +/* + * 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.schemagen; + +import java.net.URL; +import java.util.Map; + +import javax.persistence.EntityManagerFactory; + +import org.hibernate.jpa.AvailableSettings; +import org.hibernate.jpa.boot.spi.Bootstrap; +import org.hibernate.jpa.boot.spi.EntityManagerFactoryBuilder; +import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor; +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; +import org.hibernate.testing.TestForIssue; +import org.junit.Assert; +import org.junit.Test; + +/** + * Basic tests for JPA 2.1 schema export + * + * @author Christian Beikov + * @author Steve Ebersole + */ +public class JpaSchemaGeneratorTest extends BaseEntityManagerFunctionalTestCase { + private static final String LOAD_SQL = "org/hibernate/jpa/test/schemagen/load-script-source.sql"; + private static final String CREATE_SQL = "org/hibernate/jpa/test/schemagen/create-script-source.sql"; + private static final String DROP_SQL = "org/hibernate/jpa/test/schemagen/drop-script-source.sql"; + + private static int schemagenNumber = 0; + + @Override + public Class[] getAnnotatedClasses() { + return new Class[] { Item.class }; + } + + @SuppressWarnings("unchecked") + @Test + @TestForIssue(jiraKey = "HHH-8271") + public void testSqlLoadScriptSourceClasspath() throws Exception { + Map settings = buildSettings(); + settings.put( AvailableSettings.SCHEMA_GEN_DATABASE_ACTION, "drop-and-create" ); + settings.put( AvailableSettings.SCHEMA_GEN_LOAD_SCRIPT_SOURCE, LOAD_SQL ); + doTest( settings ); + } + + + @SuppressWarnings("unchecked") + @Test + @TestForIssue(jiraKey = "HHH-8271") + public void testSqlLoadScriptSourceUrl() throws Exception { + Map settings = buildSettings(); + settings.put( AvailableSettings.SCHEMA_GEN_DATABASE_ACTION, "drop-and-create" ); + settings.put( AvailableSettings.SCHEMA_GEN_LOAD_SCRIPT_SOURCE, getResourceUrlString( LOAD_SQL ) ); + doTest( settings ); + } + + private String getResourceUrlString(String resource) { + final URL url = getClass().getClassLoader().getResource( resource ); + if ( url == null ) { + throw new RuntimeException( "Unable to locate requested resource [" + resource + "]" ); + } + return url.toString(); + } + + @SuppressWarnings("unchecked") + @Test + @TestForIssue(jiraKey = "HHH-8271") + public void testSqlCreateScriptSourceClasspath() throws Exception { + Map settings = buildSettings(); + settings.put( AvailableSettings.SCHEMA_GEN_DATABASE_ACTION, "drop-and-create" ); + settings.put( AvailableSettings.SCHEMA_GEN_CREATE_SOURCE, "metadata-then-script" ); + settings.put( AvailableSettings.SCHEMA_GEN_CREATE_SCRIPT_SOURCE, CREATE_SQL ); + doTest( settings ); + } + + @SuppressWarnings("unchecked") + @Test + @TestForIssue(jiraKey = "HHH-8271") + public void testSqlCreateScriptSourceUrl() throws Exception { + Map settings = buildSettings(); + settings.put( AvailableSettings.SCHEMA_GEN_DATABASE_ACTION, "drop-and-create" ); + settings.put( AvailableSettings.SCHEMA_GEN_CREATE_SOURCE, "metadata-then-script" ); + settings.put( AvailableSettings.SCHEMA_GEN_CREATE_SCRIPT_SOURCE, getResourceUrlString( CREATE_SQL ) ); + doTest( settings ); + } + + + @SuppressWarnings("unchecked") + @Test + @TestForIssue(jiraKey = "HHH-8271") + public void testSqlDropScriptSourceClasspath() throws Exception { + Map settings = buildSettings(); + settings.put( AvailableSettings.SCHEMA_GEN_DROP_SOURCE, "metadata-then-script" ); + settings.put( AvailableSettings.SCHEMA_GEN_DATABASE_ACTION, "drop" ); + settings.put( AvailableSettings.SCHEMA_GEN_DROP_SCRIPT_SOURCE, DROP_SQL ); + doTest( settings ); + } + + @SuppressWarnings("unchecked") + @Test + @TestForIssue(jiraKey = "HHH-8271") + public void testSqlDropScriptSourceUrl() throws Exception { + Map settings = buildSettings(); + settings.put( AvailableSettings.SCHEMA_GEN_DROP_SOURCE, "metadata-then-script" ); + settings.put( AvailableSettings.SCHEMA_GEN_DATABASE_ACTION, "drop" ); + settings.put( AvailableSettings.SCHEMA_GEN_DROP_SCRIPT_SOURCE, getResourceUrlString( DROP_SQL ) ); + doTest( settings ); + } + + @SuppressWarnings("unchecked") + private void doTest(Map settings) { + // We want a fresh db after emf close + // Unfortunately we have to use this dirty hack because the db seems not to be closed otherwise + settings.put( "hibernate.connection.url", "jdbc:h2:mem:db-schemagen" + schemagenNumber++ + + ";MVCC=TRUE;LOCK_TIMEOUT=10000" ); + EntityManagerFactoryBuilder emfb = Bootstrap.getEntityManagerFactoryBuilder( buildPersistenceUnitDescriptor(), + settings ); + + EntityManagerFactory emf = emfb.build(); + + Assert.assertNotNull( emf.createEntityManager().find( Item.class, "schemagen-test" ) ); + + emf.close(); + emfb.cancel(); + } + + private PersistenceUnitDescriptor buildPersistenceUnitDescriptor() { + return new TestingPersistenceUnitDescriptorImpl( getClass().getSimpleName() ); + } + + /* Disable hibernate schema export */ + @Override + protected boolean createSchema() { + return false; + } + +} \ No newline at end of file diff --git a/hibernate-entitymanager/src/test/resources/org/hibernate/jpa/test/schemagen/create-script-source.sql b/hibernate-entitymanager/src/test/resources/org/hibernate/jpa/test/schemagen/create-script-source.sql new file mode 100644 index 0000000000..49fb00c312 --- /dev/null +++ b/hibernate-entitymanager/src/test/resources/org/hibernate/jpa/test/schemagen/create-script-source.sql @@ -0,0 +1 @@ +INSERT INTO Item(name) VALUES('schemagen-test'); diff --git a/hibernate-entitymanager/src/test/resources/org/hibernate/jpa/test/schemagen/drop-script-source.sql b/hibernate-entitymanager/src/test/resources/org/hibernate/jpa/test/schemagen/drop-script-source.sql new file mode 100644 index 0000000000..1fe051373b --- /dev/null +++ b/hibernate-entitymanager/src/test/resources/org/hibernate/jpa/test/schemagen/drop-script-source.sql @@ -0,0 +1,2 @@ +create table Item ( name varchar(30) not null, descr varchar(200), primary key (name)); +INSERT INTO Item(name) VALUES('schemagen-test'); diff --git a/hibernate-entitymanager/src/test/resources/org/hibernate/jpa/test/schemagen/load-script-source.sql b/hibernate-entitymanager/src/test/resources/org/hibernate/jpa/test/schemagen/load-script-source.sql new file mode 100644 index 0000000000..49fb00c312 --- /dev/null +++ b/hibernate-entitymanager/src/test/resources/org/hibernate/jpa/test/schemagen/load-script-source.sql @@ -0,0 +1 @@ +INSERT INTO Item(name) VALUES('schemagen-test'); From 9699ce41c2d2e807483fed999396d0955dfcfd55 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Wed, 29 May 2013 12:48:01 -0500 Subject: [PATCH 22/36] 4.3.0.Beta3 --- build.gradle | 2 +- changelog.txt | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index d208a294e0..ad2b6c6e6e 100644 --- a/build.gradle +++ b/build.gradle @@ -26,7 +26,7 @@ buildscript { } } -ext.hibernateTargetVersion = '4.3.0-SNAPSHOT' +ext.hibernateTargetVersion = '4.3.0.Beta3' ext.javaLanguageLevel = "1.6" task wrapper(type: Wrapper) { diff --git a/changelog.txt b/changelog.txt index 07d9b6127b..ed1449b3ee 100644 --- a/changelog.txt +++ b/changelog.txt @@ -5,6 +5,53 @@ match the actual issue resolution (i.e. a bug might not be a bug). Please refer to the particular case on JIRA using the issue tracking number to learn more about each case. +Changes in version 4.3.0.Beta3 (2013.05.29) +------------------------------------------------------------------------------------------------------------------------ +https://hibernate.atlassian.net/browse/HHH/fixforversion/13451 + +** Sub-task + * [HHH-8144] - Create a 'release' task that performs all tasks needed for doing a release + +** Bug + * [HHH-2664] - full join not working + * [HHH-5465] - HQL left join fetch of an element collection following a left join fetch of a one-to-one relationship causes NullPointerException + * [HHH-6813] - @Id @OneToOne cause NullPointerException during query + * [HHH-8083] - @OrderColumn not updated on @OneToMany cascade + * [HHH-8219] - Protect against JDK 7 API usage + * [HHH-8220] - pom dependencies scope changed from compile to runtime + * [HHH-8225] - EMF cannot be created, closed, then re-created in OSGi + * [HHH-8233] - exclude-unlisted-classes is not excluding unlisted classes in root + * [HHH-8236] - Update to hibernate-commons-annotations 4.0.2.Final + * [HHH-8241] - Reusing of scanner instance is impossible with Apache Aries JPA + * [HHH-8250] - DefaultInitializeCollectionEventListener not finding collections in cache + * [HHH-8254] - throw HibernateException when transaction is rolledback by a reaper thread + * [HHH-8266] - Binding of named-stored-procedure XML element tries to create duplicate + * [HHH-8269] - DenormalizedTable FK constraint names can be too long + * [HHH-8270] - Support for accessing JPA schema export script files specified by URL + * [HHH-8271] - Handling of javax.persistence.sql-load-script-source + * [HHH-8273] - Incorrect "unique-key" naming comment in docs + +** Improvement + * [HHH-6875] - @OrderBy on @ElementCollection of basic type should "order by value" + * [HHH-7214] - DiscriminatorValue + * [HHH-7582] - TableGenerator does not distinguish between different tenants (MultiTenant Schema based) + * [HHH-8211] - Checkstyle and FindBugs fix-ups + * [HHH-8217] - Make generated constraint names short and non-random + * [HHH-8226] - table synonyms cannot find columns on Oracle + * [HHH-8231] - Pass along IOException as cause when trying to open script outputs + * [HHH-8238] - OsgiJtaPlatform null pointer exception + * [HHH-8257] - More concisely obtaining a JBoss logger + +** Task + * [HHH-8218] - Update to final versions of BV 1.1 and HV 5 + * [HHH-8222] - Implement @NamedStoredProcedureQuery binding + * [HHH-8223] - Implement @NamedEntityGraph binding + * [HHH-8232] - Upgrade to Gradle 1.6 + * [HHH-8235] - Drop database profiles upstream + * [HHH-8246] - Implement XML binding of NamedStoredProcedureQuery + * [HHH-8247] - Implement XML binding of NamedEntityGraph + + Changes in version 4.3.0.Beta2 (2013.05.02) ------------------------------------------------------------------------------------------------------------------------ https://hibernate.atlassian.net/browse/HHH/fixforversion/13052 From d874bc4737c50174bbfffd8dd4c09219e27c4e82 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Wed, 29 May 2013 13:42:26 -0500 Subject: [PATCH 23/36] 4.3.0.Beta3 --- build.gradle | 2 +- release/release.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index ad2b6c6e6e..d208a294e0 100644 --- a/build.gradle +++ b/build.gradle @@ -26,7 +26,7 @@ buildscript { } } -ext.hibernateTargetVersion = '4.3.0.Beta3' +ext.hibernateTargetVersion = '4.3.0-SNAPSHOT' ext.javaLanguageLevel = "1.6" task wrapper(type: Wrapper) { diff --git a/release/release.gradle b/release/release.gradle index edeb75ee29..280d448965 100644 --- a/release/release.gradle +++ b/release/release.gradle @@ -134,7 +134,7 @@ task buildDocumentation(type: Task, dependsOn: [rootProject.project( 'documentat ant.symlink( action: 'single', link: "${currentSymLinkContainerDir.absolutePath}/orm", - resource: "${versionedDocumentationDir.absolutePath}" + resource: "../orm/${majorMinorVersion}" ) } } From af5e8c386994657a8ed385e992e8004692bda009 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Fri, 31 May 2013 01:14:18 -0500 Subject: [PATCH 24/36] HHH-7841 - Introduce LoadPlan --- .../LoadQueryAliasResolutionContextImpl.java | 3 +- .../plan/internal/LoadPlanBuildingHelper.java | 2 - .../loader/plan/spi/AbstractFetchOwner.java | 8 +- .../plan/spi/AbstractFetchOwnerDelegate.java | 51 ++++ .../spi/AbstractSingularAttributeFetch.java | 4 +- .../loader/plan/spi/CollectionFetch.java | 7 +- .../plan/spi/CompositeElementGraph.java | 7 +- .../loader/plan/spi/CompositeFetch.java | 11 +- .../plan/spi/CompositeFetchOwnerDelegate.java | 114 ++++++--- .../loader/plan/spi/CompositeIndexGraph.java | 7 +- .../plan/spi/EntityFetchOwnerDelegate.java | 177 ++++++++++--- .../org/hibernate/loader/plan/spi/Fetch.java | 6 +- .../hibernate/loader/plan/spi/FetchOwner.java | 8 +- .../loader/plan/spi/FetchOwnerDelegate.java | 52 ++-- .../AbstractLoadPlanBuilderStrategy.java | 127 +++++++--- ...atedCompositeIdResultSetProcessorTest.java | 232 ++++++++++++++++++ 16 files changed, 665 insertions(+), 151 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractFetchOwnerDelegate.java create mode 100644 hibernate-core/src/test/java/org/hibernate/loader/NonEncapsulatedCompositeIdResultSetProcessorTest.java 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 193a7e42f5..cd6f42ae80 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 @@ -225,7 +225,8 @@ public class LoadQueryAliasResolutionContextImpl implements LoadQueryAliasResolu else { throw new NotYetImplementedException( "Cannot determine LHS alias for FetchOwner." ); } - final String[] aliasedLhsColumnNames = StringHelper.qualify( lhsAlias, currentFetch.getColumnNames() ); + + final String[] aliasedLhsColumnNames = currentFetch.toSqlSelectFragments( lhsAlias ); final String rhsAlias; if ( EntityReference.class.isInstance( currentFetch ) ) { rhsAlias = resolveEntityTableAlias( (EntityReference) currentFetch ); 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 a22fb8171e..1cc9e9c40c 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 @@ -25,7 +25,6 @@ package org.hibernate.loader.plan.internal; import org.hibernate.LockMode; import org.hibernate.engine.FetchStrategy; -import org.hibernate.loader.plan.spi.AbstractFetchOwner; import org.hibernate.loader.plan.spi.CollectionFetch; import org.hibernate.loader.plan.spi.CompositeFetch; import org.hibernate.loader.plan.spi.EntityFetch; @@ -33,7 +32,6 @@ 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 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 83e01a9ebf..b078d46cc1 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 @@ -108,17 +108,17 @@ public abstract class AbstractFetchOwner extends AbstractPlanNode implements Fet @Override public boolean isNullable(Fetch fetch) { - return getFetchOwnerDelegate().isNullable( fetch ); + return getFetchOwnerDelegate().locateFetchMetadata( fetch ).isNullable(); } @Override public Type getType(Fetch fetch) { - return getFetchOwnerDelegate().getType( fetch ); + return getFetchOwnerDelegate().locateFetchMetadata( fetch ).getType(); } @Override - public String[] getColumnNames(Fetch fetch) { - return getFetchOwnerDelegate().getColumnNames( fetch ); + public String[] toSqlSelectFragments(Fetch fetch, String alias) { + return getFetchOwnerDelegate().locateFetchMetadata( fetch ).toSqlSelectFragments( alias ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractFetchOwnerDelegate.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractFetchOwnerDelegate.java new file mode 100644 index 0000000000..673ce80268 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractFetchOwnerDelegate.java @@ -0,0 +1,51 @@ +/* + * 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.HashMap; +import java.util.Map; + +/** + * Base implementation of FetchOwnerDelegate providing caching of FetchMetadata. + * + * @author Steve Ebersole + */ +public abstract class AbstractFetchOwnerDelegate implements FetchOwnerDelegate { + private Map fetchMetadataMap; + + @Override + public FetchMetadata locateFetchMetadata(Fetch fetch) { + FetchMetadata metadata = fetchMetadataMap == null ? null : fetchMetadataMap.get( fetch.getOwnerPropertyName() ); + if ( metadata == null ) { + if ( fetchMetadataMap == null ) { + fetchMetadataMap = new HashMap(); + } + metadata = buildFetchMetadata( fetch ); + fetchMetadataMap.put( fetch.getOwnerPropertyName(), metadata ); + } + return metadata; + } + + protected abstract FetchMetadata buildFetchMetadata(Fetch fetch); +} 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 7382e08c29..e3d07dc3ab 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 @@ -92,8 +92,8 @@ public abstract class AbstractSingularAttributeFetch extends AbstractFetchOwner } @Override - public String[] getColumnNames() { - return owner.getColumnNames( this ); + public String[] toSqlSelectFragments(String alias) { + return owner.toSqlSelectFragments( this, alias ); } @Override 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 242daf427c..4f8816e7d1 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,11 +28,8 @@ 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; /** * @author Steve Ebersole @@ -82,8 +79,8 @@ public class CollectionFetch extends AbstractCollectionReference implements Fetc } @Override - public String[] getColumnNames() { - return getOwner().getColumnNames( this ); + public String[] toSqlSelectFragments(String alias) { + return getOwner().toSqlSelectFragments( this, alias ); } @Override 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 29314ac755..c45c47ef2a 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 @@ -42,7 +42,12 @@ public class CompositeElementGraph extends AbstractFetchOwner implements Fetchab this.fetchOwnerDelegate = new CompositeFetchOwnerDelegate( sessionFactory, (CompositeType) collectionPersister.getElementType(), - ( (QueryableCollection) collectionPersister ).getElementColumnNames() + new CompositeFetchOwnerDelegate.PropertyMappingDelegate() { + @Override + public String[] toSqlSelectFragments(String alias) { + return ( (QueryableCollection) collectionPersister ).getElementColumnNames( alias ); + } + } ); } 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 0a6c449bea..764cc4cd21 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 @@ -41,7 +41,7 @@ import org.hibernate.type.CompositeType; * @author Gail Badner */ public class CompositeFetch extends AbstractSingularAttributeFetch { - public static final FetchStrategy FETCH_PLAN = new FetchStrategy( FetchTiming.IMMEDIATE, FetchStyle.JOIN ); + private static final FetchStrategy FETCH_PLAN = new FetchStrategy( FetchTiming.IMMEDIATE, FetchStyle.JOIN ); private final FetchOwnerDelegate delegate; @@ -54,13 +54,18 @@ public class CompositeFetch extends AbstractSingularAttributeFetch { */ public CompositeFetch( SessionFactoryImplementor sessionFactory, - FetchOwner owner, + final FetchOwner owner, String ownerProperty) { super( sessionFactory, owner, ownerProperty, FETCH_PLAN ); this.delegate = new CompositeFetchOwnerDelegate( sessionFactory, (CompositeType) getOwner().getType( this ), - getOwner().getColumnNames( this ) + new CompositeFetchOwnerDelegate.PropertyMappingDelegate() { + @Override + public String[] toSqlSelectFragments(String alias) { + return owner.toSqlSelectFragments( CompositeFetch.this, alias ); + } + } ); } 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 index 7782b10354..2785d75c28 100644 --- 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 @@ -26,7 +26,6 @@ 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; @@ -36,72 +35,109 @@ import org.hibernate.type.Type; * owned sub-attribute fetch. * * @author Gail Badner + * @author Steve Ebersole */ -public class CompositeFetchOwnerDelegate implements FetchOwnerDelegate { +public class CompositeFetchOwnerDelegate extends AbstractFetchOwnerDelegate implements FetchOwnerDelegate { private final SessionFactoryImplementor sessionFactory; private final CompositeType compositeType; - private final String[] columnNames; + private final PropertyMappingDelegate propertyMappingDelegate; /** - * Constructs a {@link CompositeFetchOwnerDelegate}. + * Constructs a CompositeFetchOwnerDelegate + * * @param sessionFactory - the session factory. * @param compositeType - the composite type. - * @param columnNames - the column names used by sub-attribute fetches. + * @param propertyMappingDelegate - delegate for handling property mapping */ public CompositeFetchOwnerDelegate( SessionFactoryImplementor sessionFactory, CompositeType compositeType, - String[] columnNames) { + PropertyMappingDelegate propertyMappingDelegate) { this.sessionFactory = sessionFactory; this.compositeType = compositeType; - this.columnNames = columnNames; + this.propertyMappingDelegate = propertyMappingDelegate; + } + + public static interface PropertyMappingDelegate { + public String[] toSqlSelectFragments(String alias); } @Override - public boolean isNullable(Fetch fetch) { - return compositeType.getPropertyNullability()[ determinePropertyIndex( fetch ) ]; - } + protected FetchMetadata buildFetchMetadata(Fetch fetch) { + int subIndex = -1; + int selectFragmentRangeStart = 0; + int selectFragmentRangeEnd = -1; - @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 ); + for ( int i = 0; i < compositeType.getPropertyNames().length; i++ ) { + final Type type = compositeType.getSubtypes()[i]; + final int typeColSpan = type.getColumnSpan( sessionFactory ); if ( compositeType.getPropertyNames()[ i ].equals( fetch.getOwnerPropertyName() ) ) { + // fount it! + subIndex = i; + selectFragmentRangeEnd = selectFragmentRangeStart + typeColSpan; break; } - begin += columnSpan; + selectFragmentRangeStart += typeColSpan; } - 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 ) { + if ( subIndex < 0 ) { throw new WalkingException( String.format( "Owner property [%s] not found in composite properties [%s]", fetch.getOwnerPropertyName(), - Arrays.asList( subAttributeNames ) + Arrays.asList( compositeType.getPropertyNames() ) ) ); } - return subAttributeIndex; + + return new FetchMetadataImpl( + compositeType, + subIndex, + propertyMappingDelegate, + selectFragmentRangeStart, + selectFragmentRangeEnd + ); + + // todo : we really need a PropertyMapping delegate which can encapsulate both the PropertyMapping and the path + } + + private static class FetchMetadataImpl implements FetchMetadata { + private final CompositeType compositeType; + private final int index; + private final PropertyMappingDelegate propertyMappingDelegate; + private final int selectFragmentRangeStart; + private final int selectFragmentRangeEnd; + + public FetchMetadataImpl( + CompositeType compositeType, + int index, + PropertyMappingDelegate propertyMappingDelegate, + int selectFragmentRangeStart, + int selectFragmentRangeEnd) { + this.compositeType = compositeType; + this.index = index; + this.propertyMappingDelegate = propertyMappingDelegate; + this.selectFragmentRangeStart = selectFragmentRangeStart; + this.selectFragmentRangeEnd = selectFragmentRangeEnd; + } + + @Override + public boolean isNullable() { + return compositeType.getPropertyNullability()[ index ]; + } + + @Override + public Type getType() { + return compositeType.getSubtypes()[ index ]; + } + + @Override + public String[] toSqlSelectFragments(String alias) { + return Arrays.copyOfRange( + propertyMappingDelegate.toSqlSelectFragments( alias ), + selectFragmentRangeStart, + selectFragmentRangeEnd + ); + } } } 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 7f8e29aaed..b623368988 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 @@ -41,7 +41,12 @@ public class CompositeIndexGraph extends AbstractFetchOwner implements Fetchable this.fetchOwnerDelegate = new CompositeFetchOwnerDelegate( sessionFactory, (CompositeType) collectionPersister.getIndexType(), - ( (QueryableCollection) collectionPersister ).getIndexColumnNames() + new CompositeFetchOwnerDelegate.PropertyMappingDelegate() { + @Override + public String[] toSqlSelectFragments(String alias) { + return ( (QueryableCollection) collectionPersister ).getIndexColumnNames( alias ); + } + } ); } 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 index f8a599aad6..0cbc6c509d 100644 --- 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 @@ -23,10 +23,14 @@ */ package org.hibernate.loader.plan.spi; -import org.hibernate.engine.internal.JoinHelper; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.persister.entity.OuterJoinLoadable; -import org.hibernate.type.AssociationType; +import org.hibernate.persister.entity.Queryable; +import org.hibernate.persister.walking.spi.WalkingException; +import org.hibernate.type.CompositeType; import org.hibernate.type.Type; /** @@ -34,8 +38,9 @@ import org.hibernate.type.Type; * an owned attribute fetch. * * @author Gail Badner + * @author Steve Ebersole */ -public class EntityFetchOwnerDelegate implements FetchOwnerDelegate { +public class EntityFetchOwnerDelegate extends AbstractFetchOwnerDelegate implements FetchOwnerDelegate { private final EntityPersister entityPersister; /** @@ -48,35 +53,149 @@ public class EntityFetchOwnerDelegate implements FetchOwnerDelegate { } @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) { - // TODO: cache this info - final OuterJoinLoadable outerJoinLoadable = (OuterJoinLoadable) entityPersister; - Type fetchType = getType( fetch ); - if ( fetchType.isAssociationType() ) { - return JoinHelper.getLHSColumnNames( - (AssociationType) fetchType, - determinePropertyIndex( fetch ), - outerJoinLoadable, - outerJoinLoadable.getFactory() - ); + protected FetchMetadata buildFetchMetadata(Fetch fetch) { + final Integer propertyIndex = entityPersister.getEntityMetamodel().getPropertyIndexOrNull( + fetch.getOwnerPropertyName() + ); + if ( propertyIndex == null ) { + // possibly it is part of the identifier; but that's only possible if the identifier is composite + final Type idType = entityPersister.getIdentifierType(); + if ( CompositeType.class.isInstance( idType ) ) { + final CompositeType cidType = (CompositeType) idType; + if ( entityPersister.hasIdentifierProperty() ) { + // encapsulated composite id case; this *should have* been handled as part of building the fetch... + throw new WalkingException( + "Expecting different FetchOwnerDelegate type for encapsulated composite id case" + ); + } + else { + // non-encapsulated composite id case... + return new NonEncapsulatedIdentifierAttributeFetchMetadata( + entityPersister, + cidType, + fetch.getOwnerPropertyName() + ); + } + } } else { - return outerJoinLoadable.getPropertyColumnNames( determinePropertyIndex( fetch ) ); + return new NonIdentifierAttributeFetchMetadata( + entityPersister, + fetch.getOwnerPropertyName(), + propertyIndex.intValue() + ); + } + + throw new WalkingException( + "Could not locate metadata about given fetch [" + fetch + "] in its owning persister" + ); + } + + private class NonIdentifierAttributeFetchMetadata implements FetchMetadata { + private final EntityPersister entityPersister; + private final String attributeName; + private final int propertyIndex; + + private Type attributeType; + + public NonIdentifierAttributeFetchMetadata( + EntityPersister entityPersister, + String attributeName, + int propertyIndex) { + this.entityPersister = entityPersister; + this.attributeName = attributeName; + this.propertyIndex = propertyIndex; + } + + @Override + public boolean isNullable() { + return entityPersister.getPropertyNullability()[ propertyIndex ]; + } + + @Override + public Type getType() { + if ( attributeType == null ) { + attributeType = entityPersister.getPropertyTypes()[ propertyIndex ]; + } + return attributeType; + } + + @Override + public String[] toSqlSelectFragments(String alias) { +// final Type type = getType(); +// final OuterJoinLoadable outerJoinLoadable = (OuterJoinLoadable) entityPersister; + final Queryable queryable = (Queryable) entityPersister; + + return queryable.toColumns( alias, attributeName ); +// if ( type.isAssociationType() ) { +// return JoinHelper.getLHSColumnNames( +// (AssociationType) type, +// propertyIndex, +// outerJoinLoadable, +// outerJoinLoadable.getFactory() +// ); +// } +// else { +// return outerJoinLoadable.getPropertyColumnNames( propertyIndex ); +// } } } - private int determinePropertyIndex(Fetch fetch) { - // TODO: cache this info - return entityPersister.getEntityMetamodel().getPropertyIndex( fetch.getOwnerPropertyName() ); + private class NonEncapsulatedIdentifierAttributeFetchMetadata implements FetchMetadata { + private final EntityPersister entityPersister; + private final String attributeName; + + // virtually final fields + private Type type; + private int selectFragmentRangeStart; + private int selectFragmentRangeEnd; + + + public NonEncapsulatedIdentifierAttributeFetchMetadata( + EntityPersister entityPersister, + CompositeType cidType, + String attributeName) { + this.entityPersister = entityPersister; + this.attributeName = attributeName; + + this.selectFragmentRangeStart = 0; + Type subType; + boolean foundIt = false; + for ( int i = 0; i < cidType.getPropertyNames().length; i++ ) { + subType = cidType.getSubtypes()[i]; + if ( cidType.getPropertyNames()[i].equals( attributeName ) ) { + // found it! + foundIt = true; + this.type = subType; + break; + } + selectFragmentRangeStart += subType.getColumnSpan( entityPersister.getFactory() ); + } + + if ( !foundIt ) { + throw new WalkingException( "Could not find " ); + } + + selectFragmentRangeEnd = selectFragmentRangeStart + type.getColumnSpan( entityPersister.getFactory() ); + } + + @Override + public boolean isNullable() { + return false; + } + + @Override + public Type getType() { + return type; + } + + @Override + public String[] toSqlSelectFragments(String alias) { + return Arrays.copyOfRange( + ( (Queryable) entityPersister ).toColumns( alias, attributeName ), + selectFragmentRangeStart, + selectFragmentRangeEnd + ); + } } } 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 8eed47bf01..017a78e1e8 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 @@ -60,11 +60,11 @@ public interface Fetch extends CopyableFetch { public boolean isNullable(); /** - * Gets the column names used for this fetch. + * Generates the SQL select fragments for this fetch. A select fragment is the column and formula references. * - * @return the column names used for this fetch. + * @return the select fragments */ - public String[] getColumnNames(); + public String[] toSqlSelectFragments(String alias); /** * Gets the fetch strategy for this fetch. 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 81ac8685a4..4d0f77fbea 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 @@ -76,13 +76,15 @@ public interface FetchOwner { public boolean isNullable(Fetch fetch); /** - * Returns the column names used for loading the specified fetch. + * Generates the SQL select fragments for the specified fetch. A select fragment is the column and formula + * references. * * @param fetch - the owned fetch. + * @param alias The table alias to apply to the fragments (used to qualify column references) * - * @return the column names used for loading the specified fetch. + * @return the select fragments */ - public String[] getColumnNames(Fetch fetch); + public String[] toSqlSelectFragments(Fetch fetch, String alias); /** * 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 index 31916efe06..a6b25a91dc 100644 --- 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 @@ -31,31 +31,39 @@ import org.hibernate.type.Type; * @author Gail Badner */ public interface FetchOwnerDelegate { + public static interface FetchMetadata { + /** + * Is the fetch nullable? + * + * @return true, if the fetch is nullable; false, otherwise. + */ + public boolean isNullable(); + + /** + * Returns the type of the fetched attribute + * + * @return the type of the fetched attribute. + */ + public Type getType(); + + /** + * Generates the SQL select fragments for the specified fetch. A select fragment is the column and formula + * references. + * + * @param alias The table alias to apply to the fragments (used to qualify column references) + * + * @return the select fragments + */ + public String[] toSqlSelectFragments(String alias); + } /** - * Is the specified fetch nullable? + * Locate the metadata for the specified Fetch. Allows easier caching of the resolved information. * - * @param fetch - the owned fetch. + * @param fetch The fetch for which to locate metadata * - * @return true, if the fetch is nullable; false, otherwise. + * @return The metadata; never {@code null}, rather an exception is thrown if the information for the fetch cannot + * be located. */ - public boolean isNullable(Fetch fetch); - - /** - * Returns the type of the specified fetch. - * - * @param fetch - the owned fetch. - * - * @return the type of the specified fetch. - */ - public Type getType(Fetch fetch); - - /** - * Returns the column names used for loading the specified fetch. - * - * @param fetch - the owned fetch. - * - * @return the column names used for loading the specified fetch. - */ - public String[] getColumnNames(Fetch fetch); + public FetchMetadata locateFetchMetadata(Fetch fetch); } \ No newline at end of file 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 817d7db847..c95900e719 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 @@ -43,6 +43,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.internal.util.StringHelper; import org.hibernate.loader.PropertyPath; import org.hibernate.loader.plan.spi.AbstractFetchOwner; +import org.hibernate.loader.plan.spi.AbstractFetchOwnerDelegate; import org.hibernate.loader.plan.spi.CollectionFetch; import org.hibernate.loader.plan.spi.CollectionReference; import org.hibernate.loader.plan.spi.CollectionReturn; @@ -573,16 +574,17 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder @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 super.getType( fetch ); + return getFetchOwnerDelegate().locateFetchMetadata( fetch ).getType(); + } + + @Override + public boolean isNullable(Fetch fetch) { + return getFetchOwnerDelegate().locateFetchMetadata( fetch ).isNullable(); + } + + @Override + public String[] toSqlSelectFragments(Fetch fetch, String alias) { + return getFetchOwnerDelegate().locateFetchMetadata( fetch ).toSqlSelectFragments( alias ); } @Override @@ -620,39 +622,42 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder final EntityReference entityReference) { super( sessionFactory, entityReference ); this.propertyPath = ( (FetchOwner) entityReference ).getPropertyPath(); - final boolean isCompositeType = entityReference.getEntityPersister().getIdentifierType().isComponentType(); - this.delegate = new FetchOwnerDelegate() { - @Override - public boolean isNullable(Fetch fetch) { - if ( !isCompositeType ) { - throw new IllegalStateException( "Non-composite ID cannot have fetches." ); - } - return true; - } + this.delegate = new AbstractFetchOwnerDelegate() { + final boolean isCompositeType = entityReference.getEntityPersister().getIdentifierType().isComponentType(); @Override - public Type getType(Fetch fetch) { + protected FetchMetadata buildFetchMetadata(Fetch fetch) { if ( !isCompositeType ) { - throw new IllegalStateException( "Non-composite ID cannot have fetches." ); + throw new WalkingException( "Non-composite identifier cannot be a fetch owner" ); } + if ( !fetch.getOwnerPropertyName().equals( entityReference.getEntityPersister().getIdentifierPropertyName() ) ) { throw new IllegalArgumentException( String.format( "Fetch owner property name [%s] is not the same as the identifier prop" + - fetch.getOwnerPropertyName(), + fetch.getOwnerPropertyName(), entityReference.getEntityPersister().getIdentifierPropertyName() ) ); } - return entityReference.getEntityPersister().getIdentifierType(); - } - @Override - public String[] getColumnNames(Fetch fetch) { - if ( !isCompositeType ) { - throw new IllegalStateException( "Non-composite ID cannot have fetches." ); - } - return ( (Loadable) entityReference.getEntityPersister() ).getIdentifierColumnNames(); + return new FetchMetadata() { + @Override + public boolean isNullable() { + return false; + } + + @Override + public Type getType() { + return entityReference.getEntityPersister().getIdentifierType(); + } + + @Override + public String[] toSqlSelectFragments(String alias) { + // should not ever be called iiuc... + throw new WalkingException( "Should not be called" ); + } + }; } }; } @@ -681,14 +686,63 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder private final PropertyPath propertyPath; private final FetchOwnerDelegate fetchOwnerDelegate; - public NonEncapsulatedIdentifierAttributeCollector(SessionFactoryImplementor sessionfactory, EntityReference entityReference) { + public NonEncapsulatedIdentifierAttributeCollector( + final SessionFactoryImplementor sessionfactory, + final EntityReference entityReference) { super( sessionfactory, entityReference ); this.propertyPath = ( (FetchOwner) entityReference ).getPropertyPath().append( "" ); - this.fetchOwnerDelegate = new CompositeFetchOwnerDelegate( - entityReference.getEntityPersister().getFactory(), - (CompositeType) entityReference.getEntityPersister().getIdentifierType(), - ( (Loadable) entityReference.getEntityPersister() ).getIdentifierColumnNames() - ); + this.fetchOwnerDelegate = new AbstractFetchOwnerDelegate() { + final boolean isCompositeType = entityReference.getEntityPersister().getIdentifierType().isComponentType(); + final CompositeType idType = (CompositeType) entityReference.getEntityPersister().getIdentifierType(); + + + @Override + protected FetchMetadata buildFetchMetadata(Fetch fetch) { + if ( !isCompositeType ) { + throw new WalkingException( "Non-composite identifier cannot be a fetch owner" ); + } + + final int subPropertyIndex = locateSubPropertyIndex( idType, fetch.getOwnerPropertyName() ); + + return new FetchMetadata() { + final Type subType = idType.getSubtypes()[ subPropertyIndex ]; + + @Override + public boolean isNullable() { + return false; + } + + @Override + public Type getType() { + return subType; + } + + @Override + public String[] toSqlSelectFragments(String alias) { + // should not ever be called iiuc... + throw new WalkingException( "Should not be called" ); + } + }; + } + + private int locateSubPropertyIndex(CompositeType idType, String ownerPropertyName) { + for ( int i = 0; i < idType.getPropertyNames().length; i++ ) { + if ( ownerPropertyName.equals( idType.getPropertyNames()[i] ) ) { + return i; + } + } + // does not bode well if we get here... + throw new IllegalStateException( + String.format( + "Unable to locate fetched attribute [%s] as part of composite identifier [%s]", + ownerPropertyName, + getPropertyPath().getFullPath() + ) + + ); + } + + }; } @Override @@ -710,6 +764,7 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder protected FetchOwnerDelegate getFetchOwnerDelegate() { return fetchOwnerDelegate; } + } private static class IdentifierDescriptionImpl implements IdentifierDescription { diff --git a/hibernate-core/src/test/java/org/hibernate/loader/NonEncapsulatedCompositeIdResultSetProcessorTest.java b/hibernate-core/src/test/java/org/hibernate/loader/NonEncapsulatedCompositeIdResultSetProcessorTest.java new file mode 100644 index 0000000000..bd05014d50 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/loader/NonEncapsulatedCompositeIdResultSetProcessorTest.java @@ -0,0 +1,232 @@ +/* + * 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 org.hibernate.LockOptions; +import org.hibernate.Session; +import org.hibernate.Transaction; +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.type.Type; + +import org.junit.Test; + +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.hibernate.test.onetoone.formula.Address; +import org.hibernate.test.onetoone.formula.Person; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * @author Steve Ebersole + */ +public class NonEncapsulatedCompositeIdResultSetProcessorTest extends BaseCoreFunctionalTestCase { + + @Override + protected String[] getMappings() { + return new String[] { "onetoone/formula/Person.hbm.xml" }; + } + + @Test + public void testCompositeIdWithKeyManyToOne() throws Exception { + final String personId = "John Doe"; + + Person p = new Person(); + p.setName( personId ); + final Address a = new Address(); + a.setPerson( p ); + p.setAddress( a ); + a.setType( "HOME" ); + a.setStreet( "Main St" ); + a.setState( "Sweet Home Alabama" ); + a.setZip( "3181" ); + + Session s = openSession(); + Transaction t = s.beginTransaction(); + s.persist( p ); + t.commit(); + s.close(); + + final EntityPersister personPersister = sessionFactory().getEntityPersister( Person.class.getName() ); + final EntityPersister addressPersister = sessionFactory().getEntityPersister( Address.class.getName() ); + + { + final List results = getResults( + addressPersister, + new Callback() { + @Override + public void bind(PreparedStatement ps) throws SQLException { + ps.setString( 1, personId ); + ps.setString( 2, "HOME" ); + } + + @Override + public QueryParameters getQueryParameters() { + QueryParameters qp = new QueryParameters(); + qp.setPositionalParameterTypes( new Type[] { addressPersister.getIdentifierType() } ); + qp.setPositionalParameterValues( new Object[] { a } ); + qp.setOptionalObject( a ); + qp.setOptionalEntityName( addressPersister.getEntityName() ); + qp.setOptionalId( a ); + qp.setLockOptions( LockOptions.NONE ); + return qp; + } + + } + ); + assertEquals( 1, results.size() ); + Object result = results.get( 0 ); + assertNotNull( result ); + } + + // test loading the Person (the entity with normal id def, but mixed composite fk to Address) + { + final List results = getResults( + personPersister, + new Callback() { + @Override + public void bind(PreparedStatement ps) throws SQLException { + ps.setString( 1, personId ); + } + + @Override + public QueryParameters getQueryParameters() { + QueryParameters qp = new QueryParameters(); + qp.setPositionalParameterTypes( new Type[] { personPersister.getIdentifierType() } ); + qp.setPositionalParameterValues( new Object[] { personId } ); + qp.setOptionalObject( null ); + qp.setOptionalEntityName( personPersister.getEntityName() ); + qp.setOptionalId( personId ); + 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 + s = openSession(); + s.beginTransaction(); + s.createQuery( "delete Address" ).executeUpdate(); + s.createQuery( "delete Person" ).executeUpdate(); + s.getTransaction().commit(); + s.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 { + System.out.println( "SQL : " + sql ); + 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(); + } +} From ab11440331d2704d3e4f6d9d7ebc9a6dbc0e6e47 Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Tue, 21 May 2013 18:03:37 -0400 Subject: [PATCH 25/36] HHH-8112 osgi devguide --- .../docbook/devguide/en-US/Author_Group.xml | 6 + .../en-US/Hibernate_Development_Guide.xml | 1 + .../devguide/en-US/chapters/osgi/OSGi.xml | 402 ++++++++++++++++++ .../osgi/extras/extension_point_blueprint.xml | 11 + 4 files changed, 420 insertions(+) create mode 100644 documentation/src/main/docbook/devguide/en-US/chapters/osgi/OSGi.xml create mode 100644 documentation/src/main/docbook/devguide/en-US/chapters/osgi/extras/extension_point_blueprint.xml diff --git a/documentation/src/main/docbook/devguide/en-US/Author_Group.xml b/documentation/src/main/docbook/devguide/en-US/Author_Group.xml index 802ab8ce43..deec4f9b46 100644 --- a/documentation/src/main/docbook/devguide/en-US/Author_Group.xml +++ b/documentation/src/main/docbook/devguide/en-US/Author_Group.xml @@ -50,6 +50,12 @@ Badner + + + Brett + Meyer + + diff --git a/documentation/src/main/docbook/devguide/en-US/Hibernate_Development_Guide.xml b/documentation/src/main/docbook/devguide/en-US/Hibernate_Development_Guide.xml index b856833e63..71d0d4ec5e 100644 --- a/documentation/src/main/docbook/devguide/en-US/Hibernate_Development_Guide.xml +++ b/documentation/src/main/docbook/devguide/en-US/Hibernate_Development_Guide.xml @@ -52,6 +52,7 @@ + diff --git a/documentation/src/main/docbook/devguide/en-US/chapters/osgi/OSGi.xml b/documentation/src/main/docbook/devguide/en-US/chapters/osgi/OSGi.xml new file mode 100644 index 0000000000..07a107d48b --- /dev/null +++ b/documentation/src/main/docbook/devguide/en-US/chapters/osgi/OSGi.xml @@ -0,0 +1,402 @@ + + + + + OSGi + + + The Open Services Gateway initiative (OSGi) specification describes a dynamic, modularized system. "Bundles" + (components) can be installed, activated, deactivated, and uninstalled during runtime, without requiring + a system restart. OSGi frameworks manage bundles' dependencies, packages, and classes. The framework + is also in charge of ClassLoading, managing visibility of packages between bundles. Further, service + registry and discovery is provided through a "whiteboard" pattern. + + + + OSGi environments present numerous, unique challenges. Most notably, the dynamic nature of available + bundles during runtime can require significant architectural considerations. Also, + architectures must allow the OSGi-specific ClassLoading and service registration/discovery. + + + + +

+ OSGi Specification and Environment + + + Hibernate targets the OSGi 4.3 spec or later. It was necessary to start with 4.3, over 4.2, due to our + dependency on OSGi's BundleWiring for entity/mapping scanning. + + + + Hibernate supports three types of configurations within OSGi. + + + + Container-Managed JPA: + + + Unmanaged JPA: + + + Unmanaged Native: + + + +
+ +
+ hibernate-osgi + + + Rather than embed OSGi capabilities into hibernate-core, hibernate-entitymanager, and sub-modules, + hibernate-osgi was created. It's purposefully separated, isolating all OSGi dependencies. It provides an + OSGi-specific ClassLoader (aggregates the container's CL with core and entitymanager CLs), JPA persistence + provider, SF/EMF bootstrapping, entities/mappings scanner, and service management. + +
+ +
+ Container-Managed JPA + + + The Enterprise OSGi specification includes container-managed JPA. The container is responsible for + discovering persistence units and creating the EntityManagerFactory (one EMF per PU). + It uses the JPA provider (hibernate-osgi) that has registered itself with the OSGi + PersistenceProvider service. + + + + Quickstart tutorial project, demonstrating a container-managed JPA client bundle: + managed-jpa + + +
+ Client bundle imports + + Your client bundle's manifest will need to import, at a minimum, + + + javax.persistence + + + + org.hibernate.proxy and javassist.util.proxy, due to + Hibernate's ability to return proxies for lazy initialization + + + + +
+ +
+ JPA 2.1 + + + No Enterprise OSGi JPA container currently supports JPA 2.1 (the spec is not yet released). For + testing, the managed-jpa example makes use of + Brett's fork of Aries. To work + with Hibernate 4.3, clone the fork and build Aries JPA. + +
+ +
+ DataSource + + Typical Enterprise OSGi JPA usage includes a DataSource installed in the container. The client + bundle's persistence.xml uses the DataSource through JNDI. For an example, + see the QuickStart's DataSource: + datasource-h2.xml + The DataSource is then called out in + + persistence.xml's jta-data-source. + +
+ +
+ Bundle Ordering + + Hibernate currently requires fairly specific bundle activation ordering. See the managed-jpa + QuickStart's + features.xml + for the best supported sequence. + +
+ +
+ Obtaining an EntityManger + + The easiest, and most supported, method of obtaining an EntityManager utilizes OSGi's + blueprint.xml. The container takes the name of your persistence unit, then injects + an EntityManager instance into your given bean attribute. See the + dpService bean in the managed-jpa QuickStart's + blueprint.xml + for an example. + +
+
+ +
+ Unmanaged JPA + + + Hibernate also supports the use of JPA through hibernate-entitymanager, unmanaged by the OSGi + container. The client bundle is responsible for managing the EntityManagerFactory and EntityManagers. + + + + Quickstart tutorial project, demonstrating an unmanaged JPA client bundle: + unmanaged-jpa + + +
+ Client bundle imports + + Your client bundle's manifest will need to import, at a minimum, + + + javax.persistence + + + + org.hibernate.proxy and javassist.util.proxy, due to + Hibernate's ability to return proxies for lazy initialization + + + + + JDBC driver package (example: org.h2) + + + + + org.osgi.framework, necessary to discover the EMF (described below) + + + + +
+ +
+ Bundle Ordering + + Hibernate currently requires fairly specific bundle activation ordering. See the unmanaged-jpa + QuickStart's + features.xml + for the best supported sequence. + +
+ +
+ Obtaining an EntityMangerFactory + + hibernate-osgi registers an OSGi service, using the JPA PersistenceProvider interface + name, that bootstraps and creates an EntityManagerFactory specific for OSGi + environments. It is VITAL that your EMF be obtained through the service, rather than creating it + manually. The service handles the OSGi ClassLoader, discovered extension points, scanning, etc. Manually + creating an EntityManagerFactory is guaranteed to NOT work during runtime! + + + For an example on how to discover and use the service, see the unmanaged-jpa + QuickStart's + HibernateUtil.java. + +
+
+ +
+ Unmanaged Native + + + Native Hibernate use is also supported. The client bundle is responsible for managing the + SessionFactory and Sessions. + + + + Quickstart tutorial project, demonstrating an unmanaged native client bundle: + unmanaged-native + + +
+ Client bundle imports + + Your client bundle's manifest will need to import, at a minimum, + + + javax.persistence + + + + org.hibernate.proxy and javassist.util.proxy, due to + Hibernate's ability to return proxies for lazy initialization + + + + + JDBC driver package (example: org.h2) + + + + + org.osgi.framework, necessary to discover the SF (described below) + + + + + org.hibernate.* packages, as necessary (ex: cfg, criterion, service, etc.) + + + + +
+ +
+ Bundle Ordering + + Hibernate currently requires fairly specific bundle activation ordering. See the unmanaged-native + QuickStart's + features.xml + for the best supported sequence. + +
+ +
+ Obtaining an SessionFactory + + hibernate-osgi registers an OSGi service, using the SessionFactory interface + name, that bootstraps and creates an SessionFactory specific for OSGi + environments. It is VITAL that your SF be obtained through the service, rather than creating it + manually. The service handles the OSGi ClassLoader, discovered extension points, scanning, etc. Manually + creating an SessionFactory is guaranteed to NOT work during runtime! + + + For an example on how to discover and use the service, see the unmanaged-native + QuickStart's + HibernateUtil.java. + +
+
+ +
+ Optional Modules + + + The unmanaged-native + QuickStart project demonstrates the use of optional Hibernate modules. Each module adds additional + dependency bundles that must first be activated + (see features.xml). + As of ORM 4.2, Envers is fully supported. Support for C3P0, Proxool, EhCache, and Infinispan were added in + 4.3, however none of their 3rd party libraries currently work in OSGi (lots of ClassLoader problems, etc.). + We're tracking the issues in JIRA. + +
+ +
+ Extension Points + + + Multiple contracts exist to allow applications to integrate with and extend Hibernate capabilities. Most + apps utilize JDK services to provide their implementations. hibernate-osgi supports the same + extensions through OSGi services. Implement and register them in any of the three configurations. + hibernate-osgi will discover and integrate them during EMF/SF bootstrapping. Supported extension points + are as follows. The specified interface should be used during service registration. + + + + org.hibernate.integrator.spi.Integrator (as of 4.2) + + + org.hibernate.boot.registry.selector.StrategyRegistrationProvider (as of 4.3) + + + org.hibernate.metamodel.spi.TypeContributor (as of 4.3) + + + JTA's javax.transaction.TransactionManager and + javax.transaction.UserTransaction (as of 4.2), however these are typically + provided by the OSGi container. + + + + + + The easiest way to register extension point implementations is through a blueprint.xml + file. Add OSGI-INF/blueprint/blueprint.xml to your classpath. Envers' blueprint + is a great example: + + + + Example extension point registrations in blueprint.xml + + + + + Extension points can also be registered programmatically with + BundleContext#registerService, typically within your + BundleActivator#start. + +
+ +
+ Caveats + + + + + Technically, multiple persistence units are supported by Enterprise OSGi JPA and unmanaged + Hibernate JPA use. However, we cannot currently support this in OSGi. In Hibernate 4, only one + instance of the OSGi-specific ClassLoader is used per Hibernate bundle, mainly due to heavy use of + static TCCL utilities. We hope to support one OSGi ClassLoader per persistence unit in + Hibernate 5. + + + + + Scanning is supported to find non-explicitly listed entities and mappings. However, they MUST be + in the same bundle as your persistence unit (fairly typical anyway). Our OSGi ClassLoader only + considers the "requesting bundle" (hence the requirement on using services to create EMF/SF), + rather than attempting to scan all available bundles. This is primarily for versioning + considerations, collision protections, etc. + + + + + Currently, Hibernate OSGi is primarily tested using Apache Karaf and Apache Aries JPA. Additional + testing is needed with Equinox, Gemini, and other container providers. + + + + + Hibernate ORM has many dependencies that do not currently provide OSGi manifests. + The QuickStart tutorials make heavy use of 3rd party bundles (SpringSource, ServiceMix) or the + wrap:... operator. + + + + + As previously mentioned, bundle activation is currently order specific. See the QuickStart + tutorials' features.xml for example sequences. + + + + + The environment should be considered STATIC. Hibernate currently does not support the ability + to dynamically add and remove client bundles during runtime. Doing so is typically + catastrophic. We hope to better support at least partially-dynamic environments in + Hibernate 5. + + + + + No Enterprise OSGi JPA container currently supports JPA 2.1 (the spec is not yet released). For + testing, the managed-jpa example makes use of + Brett's fork of Aries. To work + with Hibernate 4.3, clone the fork and build Aries JPA. + + + +
+ + \ No newline at end of file diff --git a/documentation/src/main/docbook/devguide/en-US/chapters/osgi/extras/extension_point_blueprint.xml b/documentation/src/main/docbook/devguide/en-US/chapters/osgi/extras/extension_point_blueprint.xml new file mode 100644 index 0000000000..944eedde9a --- /dev/null +++ b/documentation/src/main/docbook/devguide/en-US/chapters/osgi/extras/extension_point_blueprint.xml @@ -0,0 +1,11 @@ + + + + + + + + + \ No newline at end of file From adcd61f0e1b1446313f55b267d108b62e9928688 Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Wed, 22 May 2013 16:28:15 -0400 Subject: [PATCH 26/36] HHH-8112 quickstart tutorial chapter --- .../docbook/quickstart/en-US/Author_Group.xml | 8 +- .../en-US/Hibernate_Getting_Started_Guide.xml | 3 +- .../en-US/content/tutorial_osgi.xml | 104 ++++++++++++++++++ .../OSGI-INF/blueprint/blueprint.xml | 10 +- .../OSGI-INF/blueprint/blueprint.xml | 9 +- .../OSGI-INF/blueprint/blueprint.xml | 9 +- 6 files changed, 138 insertions(+), 5 deletions(-) create mode 100644 documentation/src/main/docbook/quickstart/en-US/content/tutorial_osgi.xml diff --git a/documentation/src/main/docbook/quickstart/en-US/Author_Group.xml b/documentation/src/main/docbook/quickstart/en-US/Author_Group.xml index 77d800c8ec..1ebb7bcf73 100644 --- a/documentation/src/main/docbook/quickstart/en-US/Author_Group.xml +++ b/documentation/src/main/docbook/quickstart/en-US/Author_Group.xml @@ -31,6 +31,12 @@ Warski + + + Brett + Meyer + + @@ -52,4 +58,4 @@ - \ No newline at end of file + diff --git a/documentation/src/main/docbook/quickstart/en-US/Hibernate_Getting_Started_Guide.xml b/documentation/src/main/docbook/quickstart/en-US/Hibernate_Getting_Started_Guide.xml index 4b987cec14..ee9d89d04b 100644 --- a/documentation/src/main/docbook/quickstart/en-US/Hibernate_Getting_Started_Guide.xml +++ b/documentation/src/main/docbook/quickstart/en-US/Hibernate_Getting_Started_Guide.xml @@ -43,5 +43,6 @@ + - \ No newline at end of file + diff --git a/documentation/src/main/docbook/quickstart/en-US/content/tutorial_osgi.xml b/documentation/src/main/docbook/quickstart/en-US/content/tutorial_osgi.xml new file mode 100644 index 0000000000..fa95407e08 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/en-US/content/tutorial_osgi.xml @@ -0,0 +1,104 @@ + + + + OSGi Tutorial + + + Hibernate targets the OSGi 4.3 spec or later and supports three types + of configurations. + + + + Container-Managed JPA + + + Unmanaged JPA + + + Unmanaged Native + + + + + + For more details about OSGi, the three configurations, hibernate-osgi, extensions points, and caveats, please + see the OSGi chapter of the Developer's Guide! + + +
+ + Project Overview + + + Each configuration has a QuickStart project located within the download bundle (under osgi). + The bundles can be used as-is within Apache Karaf. Feel free to use them as literal + "quick start" bundle templates. + +
+ +
+ + Project Structure + + + + osgi/datasource-h2.xml: Enterprise OSGi JPA usage can include a DataSource installed in the container. + The client bundle's persistence.xml references the DataSource through JNDI. For an + example, see how managed-jpa's persistence.xml calls out the + jta-data-source. + + + osgi/[project]/features.xml: This is arguably the most important "quick start" material. It defines + a single Karaf feature ("hibernate-test") that demonstrates the necessary 3rd party libraries and + bundle activation ordering. + + + osgi/[project]/pom.xml: The POM includes typical compile-time dependencies (JPA, OSGi Core, + OSGi Enterprise), as well as OSGi manifest data. + + + osgi/[project]/src/main/resources/OSGI-INF/blueprint/blueprint.xml: + The Blueprint includes container-managed EntityManager + injection (for managed-jpa), as well as demonstrations showing how to register + your custom implementations of Hibernate extension points. + + + osgi/[project]/src/main/resources/META-INF/persistence.xml or + osgi/[project]/src/main/resources/hibernate.cfg.xml: Note that the configurations + are no different than typical uses of Hibernate! + + + osgi/[project]/src/main/java/org/hibernate/osgitest/HibernateUtil.java: Demonstrates how to create an + EntityManagerFactory (JPA) or SessionFactory (Native) using hibernate-osgi's services. Note that in + managed-jpa, this is replaced by DataPointServiceImpl#entityManager, injected by + blueprint.xml (described above). + + +
+ +
+ + TODOs + + + + If using managed-jpa, features.xml will need the path to + datasource-h2.xml updated. + + +
+ +
+ + Karaf Commands + + + All three bundles include Karaf Commands that can be used directly on the Karaf command line to test + basic persistence operations ("dp:add [name]", "dp:getall", "dp:deleteall", etc.). I leave them in the + QuickStarts as a useful sanity check. + +
+ +
diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/resources/OSGI-INF/blueprint/blueprint.xml b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/resources/OSGI-INF/blueprint/blueprint.xml index d7f2a95a47..529a4e1790 100644 --- a/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/resources/OSGI-INF/blueprint/blueprint.xml +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/src/main/resources/OSGI-INF/blueprint/blueprint.xml @@ -23,13 +23,21 @@ xmlns:jpa="http://aries.apache.org/xmlns/jpa/v1.0.0" xmlns:tx="http://aries.apache.org/xmlns/transactions/v1.0.0"> + - + + + + diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/resources/OSGI-INF/blueprint/blueprint.xml b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/resources/OSGI-INF/blueprint/blueprint.xml index 896737d2e8..caea8e855f 100644 --- a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/resources/OSGI-INF/blueprint/blueprint.xml +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/src/main/resources/OSGI-INF/blueprint/blueprint.xml @@ -22,9 +22,16 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - + + + + diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/resources/OSGI-INF/blueprint/blueprint.xml b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/resources/OSGI-INF/blueprint/blueprint.xml index 81cf11eddc..8fa894121f 100644 --- a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/resources/OSGI-INF/blueprint/blueprint.xml +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/resources/OSGI-INF/blueprint/blueprint.xml @@ -22,9 +22,16 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - + + + + From 04f3249a1287cea26f4a28ee1f69adbc9abfee4f Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Mon, 3 Jun 2013 17:19:38 -0400 Subject: [PATCH 27/36] HHH-8275 union-subclass generates bad alter table for unique constraint --- .../org/hibernate/mapping/DenormalizedTable.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/DenormalizedTable.java b/hibernate-core/src/main/java/org/hibernate/mapping/DenormalizedTable.java index b5af82a86d..b02805b1e6 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/DenormalizedTable.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/DenormalizedTable.java @@ -87,14 +87,12 @@ public class DenormalizedTable extends Table { @Override public Iterator getUniqueKeyIterator() { - //wierd implementation because of hacky behavior - //of Table.sqlCreateString() which modifies the - //list of unique keys by side-effect on some - //dialects - Map uks = new HashMap(); - uks.putAll( getUniqueKeys() ); - uks.putAll( includedTable.getUniqueKeys() ); - return uks.values().iterator(); + Iterator iter = includedTable.getUniqueKeyIterator(); + while ( iter.hasNext() ) { + UniqueKey uk = (UniqueKey) iter.next(); + createUniqueKey( uk.getColumns() ); + } + return getUniqueKeys().values().iterator(); } @Override From e2922ca5f503c690861bae886c43ae3c08cf135c Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Wed, 5 Jun 2013 18:35:36 -0400 Subject: [PATCH 28/36] HHH-8265 Documented the reasoning for importing o.h.proxy and javassist.util.proxy in the quickstarts. Added a load proxy test. --- .../devguide/en-US/chapters/osgi/OSGi.xml | 9 ++-- .../tutorials/osgi/managed-jpa/.gitignore | 1 + .../tutorials/osgi/managed-jpa/pom.xml | 3 +- .../tutorials/osgi/unmanaged-jpa/.gitignore | 1 + .../tutorials/osgi/unmanaged-jpa/pom.xml | 3 +- .../osgi/unmanaged-native/.gitignore | 1 + .../tutorials/osgi/unmanaged-native/pom.xml | 3 +- .../hibernate/osgitest/DataPointService.java | 2 + .../osgitest/DataPointServiceImpl.java | 13 ++++++ .../osgitest/command/GetCommand.java | 43 +++++++++++++++++++ .../osgitest/command/LoadCommand.java | 43 +++++++++++++++++++ .../hibernate/osgitest/entity/DataPoint.java | 4 +- .../OSGI-INF/blueprint/blueprint.xml | 10 +++++ 13 files changed, 129 insertions(+), 7 deletions(-) create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/.gitignore create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/.gitignore create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/.gitignore create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/command/GetCommand.java create mode 100644 documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/command/LoadCommand.java diff --git a/documentation/src/main/docbook/devguide/en-US/chapters/osgi/OSGi.xml b/documentation/src/main/docbook/devguide/en-US/chapters/osgi/OSGi.xml index 07a107d48b..95f9d4dc71 100644 --- a/documentation/src/main/docbook/devguide/en-US/chapters/osgi/OSGi.xml +++ b/documentation/src/main/docbook/devguide/en-US/chapters/osgi/OSGi.xml @@ -84,7 +84,8 @@ org.hibernate.proxy and javassist.util.proxy, due to - Hibernate's ability to return proxies for lazy initialization + Hibernate's ability to return proxies for lazy initialization (Javassist enhancement + occurs on the entity's ClassLoader during runtime). @@ -162,7 +163,8 @@ org.hibernate.proxy and javassist.util.proxy, due to - Hibernate's ability to return proxies for lazy initialization + Hibernate's ability to return proxies for lazy initialization (Javassist enhancement + occurs on the entity's ClassLoader during runtime) @@ -230,7 +232,8 @@ org.hibernate.proxy and javassist.util.proxy, due to - Hibernate's ability to return proxies for lazy initialization + Hibernate's ability to return proxies for lazy initialization (Javassist enhancement + occurs on the entity's ClassLoader during runtime) diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/.gitignore b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/.gitignore new file mode 100644 index 0000000000..ea8c4bf7f3 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/.gitignore @@ -0,0 +1 @@ +/target diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/pom.xml b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/pom.xml index 0da564c2b8..8be58e1b26 100755 --- a/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/pom.xml +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/pom.xml @@ -49,9 +49,10 @@ org.apache.felix.gogo.commands, org.apache.karaf.shell.console, org.apache.karaf.shell.commands, + javax.persistence;version="[1.0.0,2.1.0]", + org.hibernate.proxy, javassist.util.proxy, - javax.persistence;version="[1.0.0,2.1.0]", * META-INF/persistence.xml diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/.gitignore b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/.gitignore new file mode 100644 index 0000000000..ea8c4bf7f3 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/.gitignore @@ -0,0 +1 @@ +/target diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/pom.xml b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/pom.xml index c89c791e03..32435ef2a9 100755 --- a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/pom.xml +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/pom.xml @@ -61,9 +61,10 @@ org.apache.karaf.shell.console, org.apache.karaf.shell.commands, org.h2, + javax.persistence;version="[1.0.0,2.1.0]", + org.hibernate.proxy, javassist.util.proxy, - javax.persistence;version="[1.0.0,2.1.0]", * diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/.gitignore b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/.gitignore new file mode 100644 index 0000000000..ea8c4bf7f3 --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/.gitignore @@ -0,0 +1 @@ +/target diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/pom.xml b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/pom.xml index 539715fb10..4b8cde19c4 100755 --- a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/pom.xml +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/pom.xml @@ -69,9 +69,10 @@ org.hibernate, org.hibernate.cfg, org.hibernate.service, + javax.persistence;version="[1.0.0,2.1.0]", + org.hibernate.proxy, javassist.util.proxy, - javax.persistence;version="[1.0.0,2.1.0]", * diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/DataPointService.java b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/DataPointService.java index ba71b211a6..12fb79a95a 100644 --- a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/DataPointService.java +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/DataPointService.java @@ -37,6 +37,8 @@ public interface DataPointService { public DataPoint get(long id); + public DataPoint load(long id); + public List getAll(); public Map getRevisions(long id); diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/DataPointServiceImpl.java b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/DataPointServiceImpl.java index dfa5b66f35..660e415260 100644 --- a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/DataPointServiceImpl.java +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/DataPointServiceImpl.java @@ -24,6 +24,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import org.hibernate.Hibernate; import org.hibernate.Session; import org.hibernate.criterion.Restrictions; import org.hibernate.envers.AuditReader; @@ -62,6 +63,18 @@ public class DataPointServiceImpl implements DataPointService { return dp; } + // Test lazy loading (mainly to make sure the proxy classes work in OSGi) + public DataPoint load(long id) { + Session s = HibernateUtil.getSession(); + s.getTransaction().begin(); + DataPoint dp = (DataPoint) s.load( DataPoint.class, new Long(id) ); + // initialize + dp.getName(); + s.getTransaction().commit(); + s.close(); + return dp; + } + public List getAll() { Session s = HibernateUtil.getSession(); s.getTransaction().begin(); diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/command/GetCommand.java b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/command/GetCommand.java new file mode 100644 index 0000000000..0597d709dd --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/command/GetCommand.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.hibernate.osgitest.command; + +import org.apache.felix.gogo.commands.Action; +import org.apache.felix.gogo.commands.Argument; +import org.apache.felix.gogo.commands.Command; +import org.apache.felix.service.command.CommandSession; +import org.hibernate.osgitest.DataPointService; +import org.hibernate.osgitest.entity.DataPoint; + +@Command(scope = "dp", name = "get") +public class GetCommand implements Action { + @Argument(index = 0, name = "Id", required = true, description = "Id", multiValued = false) + String id; + + private DataPointService dpService; + + public void setDpService(DataPointService dpService) { + this.dpService = dpService; + } + + public Object execute(CommandSession session) throws Exception { + DataPoint dp = dpService.get( Long.valueOf( id ) ); + System.out.println( dp.getId() + ", " + dp.getName() ); + return null; + } + +} diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/command/LoadCommand.java b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/command/LoadCommand.java new file mode 100644 index 0000000000..3451a986ba --- /dev/null +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/command/LoadCommand.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.hibernate.osgitest.command; + +import org.apache.felix.gogo.commands.Action; +import org.apache.felix.gogo.commands.Argument; +import org.apache.felix.gogo.commands.Command; +import org.apache.felix.service.command.CommandSession; +import org.hibernate.osgitest.DataPointService; +import org.hibernate.osgitest.entity.DataPoint; + +@Command(scope = "dp", name = "load") +public class LoadCommand implements Action { + @Argument(index = 0, name = "Id", required = true, description = "Id", multiValued = false) + String id; + + private DataPointService dpService; + + public void setDpService(DataPointService dpService) { + this.dpService = dpService; + } + + public Object execute(CommandSession session) throws Exception { + DataPoint dp = dpService.load( Long.valueOf( id ) ); + System.out.println( dp.getId() + ", " + dp.getName() ); + return null; + } + +} diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/entity/DataPoint.java b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/entity/DataPoint.java index 6f7126fd27..2e5ef72422 100644 --- a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/entity/DataPoint.java +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/java/org/hibernate/osgitest/entity/DataPoint.java @@ -20,6 +20,8 @@ */ package org.hibernate.osgitest.entity; +import java.io.Serializable; + import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; @@ -31,7 +33,7 @@ import org.hibernate.envers.Audited; */ @Entity @Audited -public class DataPoint { +public class DataPoint implements Serializable { @Id @GeneratedValue private long id; diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/resources/OSGI-INF/blueprint/blueprint.xml b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/resources/OSGI-INF/blueprint/blueprint.xml index 8fa894121f..2070e1d49e 100644 --- a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/resources/OSGI-INF/blueprint/blueprint.xml +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/src/main/resources/OSGI-INF/blueprint/blueprint.xml @@ -43,6 +43,16 @@ + + + + + + + + + + From d49f68f1e33a85da9035b7bd184fcee9ca520d62 Mon Sep 17 00:00:00 2001 From: ouzned Date: Fri, 31 May 2013 23:05:02 +0200 Subject: [PATCH 29/36] HHH-8283 JdbcSQLException with CompositeCustomType and java.util.Date --- .../org/hibernate/jpa/internal/QueryImpl.java | 12 +- .../basic/BasicCriteriaUsageTest.java | 47 ++++-- .../jpa/test/criteria/basic/Date3Type.java | 145 ++++++++++++++++++ .../jpa/test/criteria/basic/Payment.java | 55 +++++++ 4 files changed, 244 insertions(+), 15 deletions(-) create mode 100644 hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/basic/Date3Type.java create mode 100644 hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/basic/Payment.java diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/QueryImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/QueryImpl.java index 80d37d0633..3f660d844b 100755 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/QueryImpl.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/QueryImpl.java @@ -58,6 +58,7 @@ import org.hibernate.jpa.internal.util.ConfigurationHelper; import org.hibernate.jpa.internal.util.LockModeTypeHelper; import org.hibernate.jpa.spi.AbstractEntityManagerImpl; import org.hibernate.jpa.spi.AbstractQueryImpl; +import org.hibernate.type.CompositeCustomType; import static javax.persistence.TemporalType.DATE; import static javax.persistence.TemporalType.TIME; @@ -102,7 +103,7 @@ public class QueryImpl extends AbstractQueryImpl implements TypedQuery, for ( String name : (Set) parameterMetadata.getNamedParameterNames() ) { final NamedParameterDescriptor descriptor = parameterMetadata.getNamedParameterDescriptor( name ); Class javaType = namedParameterTypeRedefinition.get( name ); - if ( javaType != null && mightNeedRedefinition( javaType ) ) { + if ( javaType != null && mightNeedRedefinition( javaType, descriptor.getExpectedType().getClass() ) ) { descriptor.resetExpectedType( sfi().getTypeResolver().heuristicType( javaType.getName() ) ); @@ -135,13 +136,12 @@ public class QueryImpl extends AbstractQueryImpl implements TypedQuery, return (SessionFactoryImplementor) getEntityManager().getFactory().getSessionFactory(); } - private boolean mightNeedRedefinition(Class javaType) { - // for now, only really no for dates/times/timestamps - return java.util.Date.class.isAssignableFrom( javaType ); + private boolean mightNeedRedefinition(Class javaType, Class expectedType) { + // only redefine dates/times/timestamps that are not wrapped in a CompositeCustomType + return java.util.Date.class.isAssignableFrom( javaType ) + && !CompositeCustomType.class.isAssignableFrom( expectedType ); } - - private static class ParameterRegistrationImpl implements ParameterRegistration { private final org.hibernate.Query query; diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/basic/BasicCriteriaUsageTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/basic/BasicCriteriaUsageTest.java index 3673c464cf..f14b86804d 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/basic/BasicCriteriaUsageTest.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/basic/BasicCriteriaUsageTest.java @@ -23,27 +23,33 @@ */ package org.hibernate.jpa.test.criteria.basic; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + import javax.persistence.EntityManager; +import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.ParameterExpression; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import javax.persistence.metamodel.SingularAttribute; -import org.junit.Test; - import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import org.hibernate.testing.TestForIssue; +import org.junit.Test; /** * @author Steve Ebersole */ public class BasicCriteriaUsageTest extends BaseEntityManagerFunctionalTestCase { + @Override public Class[] getAnnotatedClasses() { - return new Class[] { Wall.class }; + return new Class[] { Wall.class, Payment.class }; } @Test @@ -53,9 +59,7 @@ public class BasicCriteriaUsageTest extends BaseEntityManagerFunctionalTestCase CriteriaQuery criteria = em.getCriteriaBuilder().createQuery( Wall.class ); Root from = criteria.from( Wall.class ); ParameterExpression param = em.getCriteriaBuilder().parameter( String.class ); - SingularAttribute colorAttribute = em.getMetamodel() - .entity( Wall.class ) - .getDeclaredSingularAttribute( "color" ); + SingularAttribute colorAttribute = em.getMetamodel().entity( Wall.class ).getDeclaredSingularAttribute( "color" ); assertNotNull( "metamodel returned null singular attribute", colorAttribute ); Predicate predicate = em.getCriteriaBuilder().equal( from.get( colorAttribute ), param ); criteria.where( predicate ); @@ -74,4 +78,29 @@ public class BasicCriteriaUsageTest extends BaseEntityManagerFunctionalTestCase em.getTransaction().commit(); em.close(); } + + @Test + @TestForIssue(jiraKey = "HHH-8283") + public void testDateCompositeCustomType() { + Payment payment = new Payment(); + payment.setAmount( new BigDecimal( 1000 ) ); + payment.setDate( new Date() ); + + EntityManager em = getOrCreateEntityManager(); + em.getTransaction().begin(); + em.persist( payment ); + + CriteriaQuery criteria = em.getCriteriaBuilder().createQuery( Payment.class ); + Root rp = criteria.from( Payment.class ); + Predicate predicate = em.getCriteriaBuilder().equal( rp.get( Payment_.date ), new Date() ); + criteria.where( predicate ); + + TypedQuery q = em.createQuery( criteria ); + List payments = q.getResultList(); + + assertEquals( 1, payments.size() ); + + em.getTransaction().commit(); + em.close(); + } } diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/basic/Date3Type.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/basic/Date3Type.java new file mode 100644 index 0000000000..91a613b282 --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/basic/Date3Type.java @@ -0,0 +1,145 @@ +package org.hibernate.jpa.test.criteria.basic; + +import java.io.Serializable; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; + +import org.hibernate.HibernateException; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.type.StandardBasicTypes; +import org.hibernate.type.Type; +import org.hibernate.usertype.CompositeUserType; + +/** + * @author Francois Gerodez + */ +public class Date3Type implements CompositeUserType { + + @Override + public String[] getPropertyNames() { + return new String[] { "year", "month", "day" }; + } + + @Override + public Type[] getPropertyTypes() { + return new Type[] { StandardBasicTypes.INTEGER, StandardBasicTypes.INTEGER, StandardBasicTypes.INTEGER }; + } + + @Override + public Object getPropertyValue(Object component, int property) throws HibernateException { + Date date = (Date) component; + Calendar c = GregorianCalendar.getInstance(); + c.setTime( date ); + + switch ( property ) { + case 0: + return c.get( Calendar.YEAR ); + case 1: + return c.get( Calendar.MONTH ); + case 2: + return c.get( Calendar.DAY_OF_MONTH ); + } + + throw new HibernateException( "Invalid property provided" ); + } + + @Override + public void setPropertyValue(Object component, int property, Object value) throws HibernateException { + Date date = (Date) component; + Calendar c = GregorianCalendar.getInstance(); + c.setTime( date ); + + switch ( property ) { + case 0: + c.set( Calendar.YEAR, (Integer) value ); + case 1: + c.set( Calendar.MONTH, (Integer) value ); + case 2: + c.set( Calendar.DAY_OF_MONTH, (Integer) value ); + default: + throw new HibernateException( "Invalid property provided" ); + } + } + + @Override + public Class returnedClass() { + return Date.class; + } + + @Override + public boolean equals(Object x, Object y) throws HibernateException { + if ( x == y ) + return true; + if ( x == null || y == null ) + return false; + Date dx = (Date) x; + Date dy = (Date) y; + + return dx.equals( dy ); + } + + @Override + public int hashCode(Object x) throws HibernateException { + Date dx = (Date) x; + return dx.hashCode(); + } + + @Override + public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throws HibernateException, SQLException { + Date date = new Date(); + Calendar c = GregorianCalendar.getInstance(); + c.setTime( date ); + + Integer year = StandardBasicTypes.INTEGER.nullSafeGet( rs, names[0], session ); + Integer month = StandardBasicTypes.INTEGER.nullSafeGet( rs, names[1], session ); + Integer day = StandardBasicTypes.INTEGER.nullSafeGet( rs, names[2], session ); + + c.set( year, month, day ); + + return date; + } + + @Override + public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws HibernateException, SQLException { + Date date = new Date(); + Calendar c = GregorianCalendar.getInstance(); + c.setTime( date ); + + StandardBasicTypes.INTEGER.nullSafeSet( st, c.get( Calendar.YEAR ), index, session ); + StandardBasicTypes.INTEGER.nullSafeSet( st, c.get( Calendar.MONTH ), index + 1, session ); + StandardBasicTypes.INTEGER.nullSafeSet( st, c.get( Calendar.DAY_OF_MONTH ), index + 2, session ); + } + + @Override + public Object deepCopy(Object value) throws HibernateException { + if ( value == null ) + return null; + + Date date = (Date) value; + return date.clone(); + } + + @Override + public boolean isMutable() { + return true; + } + + @Override + public Serializable disassemble(Object value, SessionImplementor session) throws HibernateException { + return (Serializable) deepCopy( value ); + } + + @Override + public Object assemble(Serializable cached, SessionImplementor session, Object owner) throws HibernateException { + return deepCopy( cached ); + } + + @Override + public Object replace(Object original, Object target, SessionImplementor session, Object owner) throws HibernateException { + return deepCopy( original ); // TODO: improve + } +} diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/basic/Payment.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/basic/Payment.java new file mode 100644 index 0000000000..0566b2c588 --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/basic/Payment.java @@ -0,0 +1,55 @@ +package org.hibernate.jpa.test.criteria.basic; + +import java.math.BigDecimal; +import java.util.Date; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.Column; + +import org.hibernate.annotations.Columns; +import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; + +/** + * @author Francois Gerodez + */ +@Entity +@Table(name = "crit_basic_payment") +@TypeDef(name = "paymentDate", typeClass = Date3Type.class) +public class Payment { + + private Long id; + private BigDecimal amount; + private Date date; + + @Id + @GeneratedValue + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public BigDecimal getAmount() { + return amount; + } + + public void setAmount(BigDecimal amount) { + this.amount = amount; + } + + @Type(type = "paymentDate") + @Columns(columns = { @Column(name = "YEARPAYMENT"), @Column(name = "MONTHPAYMENT"), @Column(name = "DAYPAYMENT") }) + public Date getDate() { + return date; + } + + public void setDate(Date date) { + this.date = date; + } +} From d283a6fc77c3c2a83960c1da19a8ef1715fa17be Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Fri, 7 Jun 2013 11:19:37 -0400 Subject: [PATCH 30/36] HHH-8112 Added additional caveats to OSGi devguide --- .../devguide/en-US/chapters/osgi/OSGi.xml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/documentation/src/main/docbook/devguide/en-US/chapters/osgi/OSGi.xml b/documentation/src/main/docbook/devguide/en-US/chapters/osgi/OSGi.xml index 95f9d4dc71..d9a5e28059 100644 --- a/documentation/src/main/docbook/devguide/en-US/chapters/osgi/OSGi.xml +++ b/documentation/src/main/docbook/devguide/en-US/chapters/osgi/OSGi.xml @@ -364,6 +364,23 @@ considerations, collision protections, etc. + + + Some containers (ex: Aries) always return true for + PersistenceUnitInfo#excludeUnlistedClasses, + even if your persistence.xml explicitly has exclude-unlisted-classes set + to false. They claim it's to protect JPA providers from having to implement + scanning ("we handle it for you"), even though we still want to support it in many cases. The work + around is to set hibernate.archive.autodetection to, for example, + hbm,class. This tells hibernate to ignore the excludeUnlistedClasses value and + scan for *.hbm.xml and entities regardless. + + + + + Scanning does not currently support annotated packages on package-info.java. + + Currently, Hibernate OSGi is primarily tested using Apache Karaf and Apache Aries JPA. Additional From 4ed8006e5ab320a6a8855316cacde24b9c3a078f Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Fri, 7 Jun 2013 13:05:36 -0400 Subject: [PATCH 31/36] HHH-8255 made a few hibernate-core imports optional in the manifest --- hibernate-core/hibernate-core.gradle | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/hibernate-core/hibernate-core.gradle b/hibernate-core/hibernate-core.gradle index 72f3c5c9b1..af0679f456 100644 --- a/hibernate-core/hibernate-core.gradle +++ b/hibernate-core/hibernate-core.gradle @@ -64,7 +64,14 @@ jar { // Temporarily support JTA 1.1 -- Karaf and other frameworks still // use it. Without this, the plugin generates [1.2,2). // build.gradle adds javax.transaction for all modules - 'javax.transaction.xa;version="[1.1,2)"' + 'javax.transaction.xa;version="[1.1,2)"', + // optionals + 'javax.management;resolution:=optional', + 'javax.naming.event;resolution:=optional', + 'javax.naming.spi;resolution:=optional', + 'org.apache.tools.ant;resolution:=optional', + 'org.apache.tools.ant.taskdefs;resolution:=optional', + 'org.apache.tools.ant.types;resolution:=optional' // TODO: Uncomment once EntityManagerFactoryBuilderImpl no longer // uses ClassLoaderServiceImpl. From b4ab20a97b472d4dfa2fe3a89da61363dc3e293b Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Fri, 7 Jun 2013 14:29:35 -0400 Subject: [PATCH 32/36] HHH-8117 Add package export versions in OSGi manifests --- build.gradle | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index d208a294e0..d498532418 100644 --- a/build.gradle +++ b/build.gradle @@ -73,6 +73,9 @@ subprojects { subProject -> group = 'org.hibernate' version = rootProject.hibernateTargetVersion + // The OSGi manifest exported package versions need to be only the numerical release -- no "SNAPSHOT" or "Final" + exportPackageVersion = version.replaceAll("-SNAPSHOT", ""); + exportPackageVersion = exportPackageVersion.replaceAll(".Final", ""); // minimize changes, at least for now (gradle uses 'build' by default).. buildDir = "target" @@ -232,7 +235,7 @@ subprojects { subProject -> privatePackages.add( packageName ); } else { - exportPackages.add( packageName ); + exportPackages.add( packageName + ";version=\"" + exportPackageVersion + "\"" ); } } } From f2d435ddc1342c154a0b10fd47b6bea862aa9ac9 Mon Sep 17 00:00:00 2001 From: Lukasz Antoniak Date: Tue, 11 Jun 2013 09:08:56 -0700 Subject: [PATCH 33/36] HHH-8174 - Envers support for @NotFound --- .../main/docbook/devguide/en-US/Envers.xml | 8 +- .../ToOneRelationMetadataGenerator.java | 18 +-- .../entities/EntityConfiguration.java | 103 +++++----------- .../entities/RelationDescription.java | 37 +++++- .../mapper/relation/ToOneEntityLoader.java | 3 +- .../mapper/relation/ToOneIdMapper.java | 64 +++++----- .../envers/internal/tools/MappingTools.java | 15 +++ .../ManyToManyNotAuditedNullEntity.java | 111 ++++++++++++++++++ .../ManyToOneNotAuditedNullEntity.java | 110 +++++++++++++++++ .../OneToManyNotAuditedNullEntity.java | 111 ++++++++++++++++++ .../integration/proxy/ProxyIdentifier.java | 90 +++++++++++--- 11 files changed, 531 insertions(+), 139 deletions(-) create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/entities/manytomany/unidirectional/ManyToManyNotAuditedNullEntity.java create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/entities/manytoone/unidirectional/ManyToOneNotAuditedNullEntity.java create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/entities/onetomany/OneToManyNotAuditedNullEntity.java diff --git a/documentation/src/main/docbook/devguide/en-US/Envers.xml b/documentation/src/main/docbook/devguide/en-US/Envers.xml index acdba3762c..8842ceb6a6 100644 --- a/documentation/src/main/docbook/devguide/en-US/Envers.xml +++ b/documentation/src/main/docbook/devguide/en-US/Envers.xml @@ -380,8 +380,12 @@ If you want to audit a relation, where the target entity is not audited (that is the case for example with dictionary-like entities, which don't change and don't have to be audited), just annotate it with - @Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED). Then, when reading historic - versions of your entity, the relation will always point to the "current" related entity. + @Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED). Then, while reading historic + versions of your entity, the relation will always point to the "current" related entity. By default Envers + throws javax.persistence.EntityNotFoundException when "current" entity does not + exist in the database. Apply @NotFound(action = NotFoundAction.IGNORE) annotation + to silence the exception and assign null value instead. Hereby solution causes implicit eager loading + of to-one relations. diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/ToOneRelationMetadataGenerator.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/ToOneRelationMetadataGenerator.java index b18179dc39..aae903eabf 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/ToOneRelationMetadataGenerator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/ToOneRelationMetadataGenerator.java @@ -77,10 +77,8 @@ public final class ToOneRelationMetadataGenerator { // Storing information about this relation mainGenerator.getEntitiesConfigurations().get( entityName ).addToOneRelation( - propertyAuditingData.getName(), - referencedEntityName, - relMapper, - insertable + propertyAuditingData.getName(), referencedEntityName, relMapper, + insertable, MappingTools.ignoreNotFound( value ) ); // If the property isn't insertable, checking if this is not a "fake" bidirectional many-to-one relationship, @@ -154,10 +152,8 @@ public final class ToOneRelationMetadataGenerator { // Storing information about this relation mainGenerator.getEntitiesConfigurations().get( entityName ).addToOneNotOwningRelation( - propertyAuditingData.getName(), - owningReferencePropertyName, - referencedEntityName, - ownedIdMapper + propertyAuditingData.getName(), owningReferencePropertyName, referencedEntityName, + ownedIdMapper, MappingTools.ignoreNotFound( value ) ); // Adding mapper for the id @@ -191,10 +187,8 @@ public final class ToOneRelationMetadataGenerator { // Storing information about this relation mainGenerator.getEntitiesConfigurations().get( entityName ).addToOneRelation( - propertyAuditingData.getName(), - referencedEntityName, - relMapper, - insertable + propertyAuditingData.getName(), referencedEntityName, relMapper, insertable, + MappingTools.ignoreNotFound( value ) ); // Adding mapper for the id diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/EntityConfiguration.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/EntityConfiguration.java index 8ee7d237e3..d734eb95f4 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/EntityConfiguration.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/EntityConfiguration.java @@ -46,9 +46,8 @@ public class EntityConfiguration { private Map relations; private String parentEntityName; - public EntityConfiguration( - String versionsEntityName, String entityClassName, IdMappingData idMappingData, - ExtendedPropertyMapper propertyMapper, String parentEntityName) { + public EntityConfiguration(String versionsEntityName, String entityClassName, IdMappingData idMappingData, + ExtendedPropertyMapper propertyMapper, String parentEntityName) { this.versionsEntityName = versionsEntityName; this.entityClassName = entityClassName; this.idMappingData = idMappingData; @@ -58,60 +57,36 @@ public class EntityConfiguration { this.relations = new HashMap(); } - public void addToOneRelation(String fromPropertyName, String toEntityName, IdMapper idMapper, boolean insertable) { + public void addToOneRelation(String fromPropertyName, String toEntityName, IdMapper idMapper, boolean insertable, + boolean ignoreNotFound) { relations.put( fromPropertyName, - new RelationDescription( - fromPropertyName, - RelationType.TO_ONE, - toEntityName, - null, - idMapper, - null, - null, - insertable + RelationDescription.toOne( + fromPropertyName, RelationType.TO_ONE, toEntityName, null, idMapper, null, + null, insertable, ignoreNotFound ) ); } - public void addToOneNotOwningRelation( - String fromPropertyName, - String mappedByPropertyName, - String toEntityName, - IdMapper idMapper) { + public void addToOneNotOwningRelation(String fromPropertyName, String mappedByPropertyName, + String toEntityName, IdMapper idMapper, boolean ignoreNotFound) { relations.put( fromPropertyName, - new RelationDescription( - fromPropertyName, - RelationType.TO_ONE_NOT_OWNING, - toEntityName, - mappedByPropertyName, - idMapper, - null, - null, - true + RelationDescription.toOne( + fromPropertyName, RelationType.TO_ONE_NOT_OWNING, toEntityName, mappedByPropertyName, + idMapper, null, null, true, ignoreNotFound ) ); } - public void addToManyNotOwningRelation( - String fromPropertyName, - String mappedByPropertyName, - String toEntityName, - IdMapper idMapper, - PropertyMapper fakeBidirectionalRelationMapper, - PropertyMapper fakeBidirectionalRelationIndexMapper) { + public void addToManyNotOwningRelation(String fromPropertyName, String mappedByPropertyName, String toEntityName, + IdMapper idMapper, PropertyMapper fakeBidirectionalRelationMapper, + PropertyMapper fakeBidirectionalRelationIndexMapper) { relations.put( fromPropertyName, - new RelationDescription( - fromPropertyName, - RelationType.TO_MANY_NOT_OWNING, - toEntityName, - mappedByPropertyName, - idMapper, - fakeBidirectionalRelationMapper, - fakeBidirectionalRelationIndexMapper, - true + RelationDescription.toMany( + fromPropertyName, RelationType.TO_MANY_NOT_OWNING, toEntityName, mappedByPropertyName, + idMapper, fakeBidirectionalRelationMapper, fakeBidirectionalRelationIndexMapper, true ) ); } @@ -119,34 +94,18 @@ public class EntityConfiguration { public void addToManyMiddleRelation(String fromPropertyName, String toEntityName) { relations.put( fromPropertyName, - new RelationDescription( - fromPropertyName, - RelationType.TO_MANY_MIDDLE, - toEntityName, - null, - null, - null, - null, - true + RelationDescription.toMany( + fromPropertyName, RelationType.TO_MANY_MIDDLE, toEntityName, null, null, null, null, true ) ); } - public void addToManyMiddleNotOwningRelation( - String fromPropertyName, - String mappedByPropertyName, - String toEntityName) { + public void addToManyMiddleNotOwningRelation(String fromPropertyName, String mappedByPropertyName, String toEntityName) { relations.put( fromPropertyName, - new RelationDescription( - fromPropertyName, - RelationType.TO_MANY_MIDDLE_NOT_OWNING, - toEntityName, - mappedByPropertyName, - null, - null, - null, - true + RelationDescription.toMany( + fromPropertyName, RelationType.TO_MANY_MIDDLE_NOT_OWNING, toEntityName, mappedByPropertyName, + null, null, null, true ) ); } @@ -175,6 +134,13 @@ public class EntityConfiguration { return parentEntityName; } + /** + * @return the className for the configured entity + */ + public String getEntityClassName() { + return entityClassName; + } + // For use by EntitiesConfigurations String getVersionsEntityName() { @@ -184,11 +150,4 @@ public class EntityConfiguration { Iterable getRelationsIterator() { return relations.values(); } - - /** - * @return the className for the configured entity - */ - public String getEntityClassName() { - return entityClassName; - } } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/RelationDescription.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/RelationDescription.java index a4cc200a68..691dbad671 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/RelationDescription.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/RelationDescription.java @@ -34,21 +34,44 @@ public class RelationDescription { private final RelationType relationType; private final String toEntityName; private final String mappedByPropertyName; + private final boolean ignoreNotFound; private final IdMapper idMapper; private final PropertyMapper fakeBidirectionalRelationMapper; private final PropertyMapper fakeBidirectionalRelationIndexMapper; private final boolean insertable; private boolean bidirectional; - public RelationDescription( - String fromPropertyName, RelationType relationType, String toEntityName, - String mappedByPropertyName, IdMapper idMapper, - PropertyMapper fakeBidirectionalRelationMapper, - PropertyMapper fakeBidirectionalRelationIndexMapper, boolean insertable) { + public static RelationDescription toOne(String fromPropertyName, RelationType relationType, String toEntityName, + String mappedByPropertyName, IdMapper idMapper, PropertyMapper fakeBidirectionalRelationMapper, + PropertyMapper fakeBidirectionalRelationIndexMapper, boolean insertable, + boolean ignoreNotFound) { + return new RelationDescription( + fromPropertyName, relationType, toEntityName, mappedByPropertyName, idMapper, fakeBidirectionalRelationMapper, + fakeBidirectionalRelationIndexMapper, insertable, ignoreNotFound + ); + } + + public static RelationDescription toMany(String fromPropertyName, RelationType relationType, String toEntityName, + String mappedByPropertyName, IdMapper idMapper, PropertyMapper fakeBidirectionalRelationMapper, + PropertyMapper fakeBidirectionalRelationIndexMapper, boolean insertable) { + // Envers populates collections by executing dedicated queries. Special handling of + // @NotFound(action = NotFoundAction.IGNORE) can be omitted in such case as exceptions + // (e.g. EntityNotFoundException, ObjectNotFoundException) are never thrown. + // Therefore assigning false to ignoreNotFound. + return new RelationDescription( + fromPropertyName, relationType, toEntityName, mappedByPropertyName, idMapper, fakeBidirectionalRelationMapper, + fakeBidirectionalRelationIndexMapper, insertable, false + ); + } + + private RelationDescription(String fromPropertyName, RelationType relationType, String toEntityName, + String mappedByPropertyName, IdMapper idMapper, PropertyMapper fakeBidirectionalRelationMapper, + PropertyMapper fakeBidirectionalRelationIndexMapper, boolean insertable, boolean ignoreNotFound) { this.fromPropertyName = fromPropertyName; this.relationType = relationType; this.toEntityName = toEntityName; this.mappedByPropertyName = mappedByPropertyName; + this.ignoreNotFound = ignoreNotFound; this.idMapper = idMapper; this.fakeBidirectionalRelationMapper = fakeBidirectionalRelationMapper; this.fakeBidirectionalRelationIndexMapper = fakeBidirectionalRelationIndexMapper; @@ -73,6 +96,10 @@ public class RelationDescription { return mappedByPropertyName; } + public boolean isIgnoreNotFound() { + return ignoreNotFound; + } + public IdMapper getIdMapper() { return idMapper; } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/ToOneEntityLoader.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/ToOneEntityLoader.java index af8897846e..78eedada9a 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/ToOneEntityLoader.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/ToOneEntityLoader.java @@ -35,7 +35,8 @@ import org.hibernate.persister.entity.EntityPersister; */ public class ToOneEntityLoader { /** - * Immediately loads historical entity or its current state when excluded from audit process. + * Immediately loads historical entity or its current state when excluded from audit process. Returns {@code null} + * reference if entity has not been found in the database. */ public static Object loadImmediate( AuditReaderImplementor versionsReader, diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/ToOneIdMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/ToOneIdMapper.java index f41bfd7a07..d99b31f26e 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/ToOneIdMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/ToOneIdMapper.java @@ -45,11 +45,8 @@ public class ToOneIdMapper extends AbstractToOneMapper { private final String referencedEntityName; private final boolean nonInsertableFake; - public ToOneIdMapper( - IdMapper delegate, - PropertyData propertyData, - String referencedEntityName, - boolean nonInsertableFake) { + public ToOneIdMapper(IdMapper delegate, PropertyData propertyData, String referencedEntityName, + boolean nonInsertableFake) { super( propertyData ); this.delegate = delegate; this.referencedEntityName = referencedEntityName; @@ -57,11 +54,8 @@ public class ToOneIdMapper extends AbstractToOneMapper { } @Override - public boolean mapToMapFromEntity( - SessionImplementor session, - Map data, - Object newObj, - Object oldObj) { + public boolean mapToMapFromEntity(SessionImplementor session, Map data, Object newObj, + Object oldObj) { final HashMap newData = new HashMap(); // If this property is originally non-insertable, but made insertable because it is in a many-to-one "fake" @@ -77,11 +71,8 @@ public class ToOneIdMapper extends AbstractToOneMapper { } @Override - public void mapModifiedFlagsToMapFromEntity( - SessionImplementor session, - Map data, - Object newObj, - Object oldObj) { + public void mapModifiedFlagsToMapFromEntity(SessionImplementor session, Map data, Object newObj, + Object oldObj) { if ( getPropertyData().isUsingModifiedFlag() ) { data.put( getPropertyData().getModifiedFlagPropertyName(), checkModified( session, newObj, oldObj ) ); } @@ -103,9 +94,8 @@ public class ToOneIdMapper extends AbstractToOneMapper { } @Override - public void nullSafeMapToEntityFromMap( - AuditConfiguration verCfg, Object obj, Map data, Object primaryKey, - AuditReaderImplementor versionsReader, Number revision) { + public void nullSafeMapToEntityFromMap(AuditConfiguration verCfg, Object obj, Map data, Object primaryKey, + AuditReaderImplementor versionsReader, Number revision) { final Object entityId = delegate.mapToIdFromMap( data ); Object value = null; if ( entityId != null ) { @@ -114,27 +104,35 @@ public class ToOneIdMapper extends AbstractToOneMapper { } else { final EntityInfo referencedEntity = getEntityInfo( verCfg, referencedEntityName ); - value = ToOneEntityLoader.createProxyOrLoadImmediate( - versionsReader, referencedEntity.getEntityClass(), referencedEntityName, - entityId, revision, RevisionType.DEL.equals( - data.get( - verCfg.getAuditEntCfg() - .getRevisionTypePropName() - ) - ), verCfg - ); + boolean ignoreNotFound = false; + if ( !referencedEntity.isAudited() ) { + final String referencingEntityName = verCfg.getEntCfg().getEntityNameForVersionsEntityName( (String) data.get( "$type$" ) ); + ignoreNotFound = verCfg.getEntCfg().get( referencingEntityName ).getRelationDescription( getPropertyData().getName() ).isIgnoreNotFound(); + } + if ( ignoreNotFound ) { + // Eagerly loading referenced entity to silence potential (in case of proxy) + // EntityNotFoundException or ObjectNotFoundException. Assigning null reference. + value = ToOneEntityLoader.loadImmediate( + versionsReader, referencedEntity.getEntityClass(), referencedEntityName, + entityId, revision, RevisionType.DEL.equals( data.get( verCfg.getAuditEntCfg().getRevisionTypePropName() ) ), + verCfg + ); + } + else { + value = ToOneEntityLoader.createProxyOrLoadImmediate( + versionsReader, referencedEntity.getEntityClass(), referencedEntityName, + entityId, revision, RevisionType.DEL.equals( data.get( verCfg.getAuditEntCfg().getRevisionTypePropName() ) ), + verCfg + ); + } } } setPropertyValue( obj, value ); } - public void addMiddleEqualToQuery( - Parameters parameters, - String idPrefix1, - String prefix1, - String idPrefix2, - String prefix2) { + public void addMiddleEqualToQuery(Parameters parameters, String idPrefix1, String prefix1, + String idPrefix2, String prefix2) { delegate.addIdsEqualToQuery( parameters, prefix1, delegate, prefix2 ); } } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/tools/MappingTools.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/tools/MappingTools.java index d91cbe3364..52446e8da6 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/tools/MappingTools.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/tools/MappingTools.java @@ -24,6 +24,7 @@ package org.hibernate.envers.internal.tools; import org.hibernate.mapping.Collection; +import org.hibernate.mapping.ManyToOne; import org.hibernate.mapping.OneToMany; import org.hibernate.mapping.ToOne; import org.hibernate.mapping.Value; @@ -63,4 +64,18 @@ public abstract class MappingTools { return null; } + + /** + * @param value Persistent property. + * @return {@code false} if lack of associated entity shall raise an exception, {@code true} otherwise. + */ + public static boolean ignoreNotFound(Value value) { + if ( value instanceof ManyToOne ) { + return ( (ManyToOne) value ).isIgnoreNotFound(); + } + else if ( value instanceof OneToMany ) { + return ( (OneToMany) value ).isIgnoreNotFound(); + } + return false; + } } diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/manytomany/unidirectional/ManyToManyNotAuditedNullEntity.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/manytomany/unidirectional/ManyToManyNotAuditedNullEntity.java new file mode 100644 index 0000000000..e7b6bc1e49 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/manytomany/unidirectional/ManyToManyNotAuditedNullEntity.java @@ -0,0 +1,111 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2008, 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.envers.test.entities.manytomany.unidirectional; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.ManyToMany; +import javax.persistence.Table; + +import org.hibernate.annotations.NotFound; +import org.hibernate.annotations.NotFoundAction; +import org.hibernate.envers.Audited; +import org.hibernate.envers.RelationTargetAuditMode; +import org.hibernate.envers.test.entities.UnversionedStrTestEntity; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +@Audited +@Entity +@Table(name = "M2M_N_AUD_NULL") +public class ManyToManyNotAuditedNullEntity implements Serializable { + @Id + private Integer id; + + private String data; + + @Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED) + @ManyToMany(fetch = FetchType.LAZY) + @NotFound(action = NotFoundAction.IGNORE) + private List references = new ArrayList(); + + protected ManyToManyNotAuditedNullEntity() { + } + + public ManyToManyNotAuditedNullEntity(Integer id, String data) { + this.id = id; + this.data = data; + } + + public boolean equals(Object o) { + if ( this == o ) return true; + if ( !( o instanceof ManyToManyNotAuditedNullEntity ) ) return false; + + ManyToManyNotAuditedNullEntity that = (ManyToManyNotAuditedNullEntity) o; + + if ( data != null ? !data.equals( that.getData() ) : that.getData() != null ) return false; + if ( id != null ? !id.equals( that.getId() ) : that.getId() != null ) return false; + + return true; + } + + public int hashCode() { + int result = ( id != null ? id.hashCode() : 0 ); + result = 31 * result + ( data != null ? data.hashCode() : 0 ); + return result; + } + + public String toString() { + return "ManyToManyNotAuditedNullEntity(id = " + id + ", data = " + data + ")"; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public List getReferences() { + return references; + } + + public void setReferences(List references) { + this.references = references; + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/manytoone/unidirectional/ManyToOneNotAuditedNullEntity.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/manytoone/unidirectional/ManyToOneNotAuditedNullEntity.java new file mode 100644 index 0000000000..ffe1b564a9 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/manytoone/unidirectional/ManyToOneNotAuditedNullEntity.java @@ -0,0 +1,110 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2008, 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.envers.test.entities.manytoone.unidirectional; + +import java.io.Serializable; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +import org.hibernate.annotations.NotFound; +import org.hibernate.annotations.NotFoundAction; +import org.hibernate.envers.Audited; +import org.hibernate.envers.RelationTargetAuditMode; +import org.hibernate.envers.test.entities.UnversionedStrTestEntity; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +@Audited +@Entity +@Table(name = "M2O_N_AUD_NULL") +public class ManyToOneNotAuditedNullEntity implements Serializable { + @Id + private Integer id; + + private String data; + + @Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED) + @ManyToOne(fetch = FetchType.LAZY, optional = true) + @NotFound(action = NotFoundAction.IGNORE) + private UnversionedStrTestEntity reference; + + protected ManyToOneNotAuditedNullEntity() { + } + + public ManyToOneNotAuditedNullEntity(Integer id, String data, UnversionedStrTestEntity reference) { + this.id = id; + this.data = data; + this.reference = reference; + } + + public boolean equals(Object o) { + if ( this == o ) return true; + if ( !( o instanceof ManyToOneNotAuditedNullEntity ) ) return false; + + ManyToOneNotAuditedNullEntity that = (ManyToOneNotAuditedNullEntity) o; + + if ( data != null ? !data.equals( that.getData() ) : that.getData() != null ) return false; + if ( id != null ? !id.equals( that.getId() ) : that.getId() != null ) return false; + + return true; + } + + public int hashCode() { + int result = ( id != null ? id.hashCode() : 0 ); + result = 31 * result + ( data != null ? data.hashCode() : 0 ); + return result; + } + + public String toString() { + return "ManyToOneNotAuditedNullEntity(id = " + id + ", data = " + data + ")"; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public UnversionedStrTestEntity getReference() { + return reference; + } + + public void setReference(UnversionedStrTestEntity reference) { + this.reference = reference; + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/onetomany/OneToManyNotAuditedNullEntity.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/onetomany/OneToManyNotAuditedNullEntity.java new file mode 100644 index 0000000000..e0d80e3735 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/onetomany/OneToManyNotAuditedNullEntity.java @@ -0,0 +1,111 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2008, 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.envers.test.entities.onetomany; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import javax.persistence.Table; + +import org.hibernate.annotations.NotFound; +import org.hibernate.annotations.NotFoundAction; +import org.hibernate.envers.Audited; +import org.hibernate.envers.RelationTargetAuditMode; +import org.hibernate.envers.test.entities.UnversionedStrTestEntity; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +@Audited +@Entity +@Table(name = "O2M_N_AUD_NULL") +public class OneToManyNotAuditedNullEntity implements Serializable { + @Id + private Integer id; + + private String data; + + @Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED) + @OneToMany(fetch = FetchType.LAZY) + @NotFound(action = NotFoundAction.IGNORE) + private List references = new ArrayList(); + + protected OneToManyNotAuditedNullEntity() { + } + + public OneToManyNotAuditedNullEntity(Integer id, String data) { + this.id = id; + this.data = data; + } + + public boolean equals(Object o) { + if ( this == o ) return true; + if ( !( o instanceof OneToManyNotAuditedNullEntity ) ) return false; + + OneToManyNotAuditedNullEntity that = (OneToManyNotAuditedNullEntity) o; + + if ( data != null ? !data.equals( that.getData() ) : that.getData() != null ) return false; + if ( id != null ? !id.equals( that.getId() ) : that.getId() != null ) return false; + + return true; + } + + public int hashCode() { + int result = ( id != null ? id.hashCode() : 0 ); + result = 31 * result + ( data != null ? data.hashCode() : 0 ); + return result; + } + + public String toString() { + return "OneToManyNotAuditedNullEntity(id = " + id + ", data = " + data + ")"; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public List getReferences() { + return references; + } + + public void setReferences(List references) { + this.references = references; + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/proxy/ProxyIdentifier.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/proxy/ProxyIdentifier.java index 95b010430d..c3b73ca6f5 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/proxy/ProxyIdentifier.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/proxy/ProxyIdentifier.java @@ -25,26 +25,37 @@ package org.hibernate.envers.test.integration.proxy; import javax.persistence.EntityManager; +import org.junit.Assert; +import org.junit.Test; + import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase; import org.hibernate.envers.test.Priority; import org.hibernate.envers.test.entities.UnversionedStrTestEntity; +import org.hibernate.envers.test.entities.manytomany.unidirectional.ManyToManyNotAuditedNullEntity; +import org.hibernate.envers.test.entities.manytoone.unidirectional.ManyToOneNotAuditedNullEntity; import org.hibernate.envers.test.entities.manytoone.unidirectional.TargetNotAuditedEntity; +import org.hibernate.envers.test.entities.onetomany.OneToManyNotAuditedNullEntity; import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.LazyInitializer; - -import org.junit.Test; - +import org.hibernate.testing.TestForIssue; /** * @author Eugene Goroschenya */ public class ProxyIdentifier extends BaseEnversJPAFunctionalTestCase { - private TargetNotAuditedEntity tnae1; - private UnversionedStrTestEntity uste1; + private TargetNotAuditedEntity tnae1 = null; + private ManyToOneNotAuditedNullEntity mtonane1 = null; + private ManyToManyNotAuditedNullEntity mtmnane1 = null; + private OneToManyNotAuditedNullEntity otmnane1 = null; + private UnversionedStrTestEntity uste1 = null; + private UnversionedStrTestEntity uste2 = null; @Override protected Class[] getAnnotatedClasses() { - return new Class[] {TargetNotAuditedEntity.class, UnversionedStrTestEntity.class}; + return new Class[] { + TargetNotAuditedEntity.class, ManyToOneNotAuditedNullEntity.class, UnversionedStrTestEntity.class, + ManyToManyNotAuditedNullEntity.class, OneToManyNotAuditedNullEntity.class + }; } @Test @@ -53,10 +64,12 @@ public class ProxyIdentifier extends BaseEnversJPAFunctionalTestCase { EntityManager em = getEntityManager(); uste1 = new UnversionedStrTestEntity( "str1" ); + uste2 = new UnversionedStrTestEntity( "str2" ); // No revision em.getTransaction().begin(); em.persist( uste1 ); + em.persist( uste2 ); em.getTransaction().commit(); // Revision 1 @@ -65,24 +78,73 @@ public class ProxyIdentifier extends BaseEnversJPAFunctionalTestCase { tnae1 = new TargetNotAuditedEntity( 1, "tnae1", uste1 ); em.persist( tnae1 ); em.getTransaction().commit(); + + // Revision 2 + em.getTransaction().begin(); + uste2 = em.find( UnversionedStrTestEntity.class, uste2.getId() ); + mtonane1 = new ManyToOneNotAuditedNullEntity( 2, "mtonane1", uste2 ); + mtmnane1 = new ManyToManyNotAuditedNullEntity( 3, "mtmnane1" ); + mtmnane1.getReferences().add( uste2 ); + otmnane1 = new OneToManyNotAuditedNullEntity( 4, "otmnane1" ); + otmnane1.getReferences().add( uste2 ); + em.persist( mtonane1 ); + em.persist( mtmnane1 ); + em.persist( otmnane1 ); + em.getTransaction().commit(); + + em.clear(); + + // Revision 3 + // Remove not audited target entity, so we can verify null reference + // when @NotFound(action = NotFoundAction.IGNORE) applied. + em.getTransaction().begin(); + ManyToOneNotAuditedNullEntity tmp1 = em.find( ManyToOneNotAuditedNullEntity.class, mtonane1.getId() ); + tmp1.setReference( null ); + tmp1 = em.merge( tmp1 ); + ManyToManyNotAuditedNullEntity tmp2 = em.find( ManyToManyNotAuditedNullEntity.class, mtmnane1.getId() ); + tmp2.setReferences( null ); + tmp2 = em.merge( tmp2 ); + OneToManyNotAuditedNullEntity tmp3 = em.find( OneToManyNotAuditedNullEntity.class, otmnane1.getId() ); + tmp3.setReferences( null ); + tmp3 = em.merge( tmp3 ); + em.remove( em.getReference( UnversionedStrTestEntity.class, uste2.getId() ) ); + em.getTransaction().commit(); + + em.close(); } @Test public void testProxyIdentifier() { TargetNotAuditedEntity rev1 = getAuditReader().find( TargetNotAuditedEntity.class, tnae1.getId(), 1 ); - assert rev1.getReference() instanceof HibernateProxy; + Assert.assertTrue( rev1.getReference() instanceof HibernateProxy ); HibernateProxy proxyCreateByEnvers = (HibernateProxy) rev1.getReference(); LazyInitializer lazyInitializer = proxyCreateByEnvers.getHibernateLazyInitializer(); - assert lazyInitializer.isUninitialized(); - assert lazyInitializer.getIdentifier() != null; - assert lazyInitializer.getIdentifier().equals( tnae1.getId() ); - assert lazyInitializer.isUninitialized(); + Assert.assertTrue( lazyInitializer.isUninitialized() ); + Assert.assertNotNull( lazyInitializer.getIdentifier() ); + Assert.assertEquals( tnae1.getId(), lazyInitializer.getIdentifier() ); + Assert.assertTrue( lazyInitializer.isUninitialized() ); - assert rev1.getReference().getId().equals( uste1.getId() ); - assert rev1.getReference().getStr().equals( uste1.getStr() ); - assert !lazyInitializer.isUninitialized(); + Assert.assertEquals( uste1.getId(), rev1.getReference().getId() ); + Assert.assertEquals( uste1.getStr(), rev1.getReference().getStr() ); + Assert.assertFalse( lazyInitializer.isUninitialized() ); + } + + @Test + @TestForIssue( jiraKey = "HHH-8174" ) + public void testNullReferenceWithNotFoundActionIgnore() { + ManyToOneNotAuditedNullEntity mtoRev2 = getAuditReader().find( ManyToOneNotAuditedNullEntity.class, mtonane1.getId(), 2 ); + Assert.assertEquals( mtonane1, mtoRev2 ); + Assert.assertNull( mtoRev2.getReference() ); + + ManyToManyNotAuditedNullEntity mtmRev2 = getAuditReader().find( ManyToManyNotAuditedNullEntity.class, mtmnane1.getId(), 2 ); + Assert.assertEquals( mtmnane1, mtmRev2 ); + Assert.assertTrue( mtmRev2.getReferences().isEmpty() ); + + OneToManyNotAuditedNullEntity otmRev2 = getAuditReader().find( OneToManyNotAuditedNullEntity.class, otmnane1.getId(), 2 ); + Assert.assertEquals( otmnane1, otmRev2 ); + Assert.assertTrue( otmRev2.getReferences().isEmpty() ); } } From 6a71cbb991104176c32bbd5a697e724ac8429f3a Mon Sep 17 00:00:00 2001 From: Lukasz Antoniak Date: Tue, 11 Jun 2013 10:14:45 -0700 Subject: [PATCH 34/36] HHH-8301 - SQLServer2005LimitHandler skips column alias generation --- .../pagination/SQLServer2005LimitHandler.java | 4 +++- .../dialect/SQLServer2005DialectTestCase.java | 13 +++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/pagination/SQLServer2005LimitHandler.java b/hibernate-core/src/main/java/org/hibernate/dialect/pagination/SQLServer2005LimitHandler.java index 2dd22b2a47..d00a755406 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/pagination/SQLServer2005LimitHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/pagination/SQLServer2005LimitHandler.java @@ -186,8 +186,10 @@ public class SQLServer2005LimitHandler extends AbstractLimitHandler { // Inserting alias. It is unlikely that we would have to add alias, but just in case. alias = StringHelper.generateAlias( "page", unique ); sb.insert( nextComa, " as " + alias ); + int aliasExprLength = ( " as " + alias ).length(); ++unique; - nextComa += ( " as " + alias ).length(); + nextComa += aliasExprLength; + endPos += aliasExprLength; } aliases.add( alias ); } diff --git a/hibernate-core/src/test/java/org/hibernate/dialect/SQLServer2005DialectTestCase.java b/hibernate-core/src/test/java/org/hibernate/dialect/SQLServer2005DialectTestCase.java index da2924d3ea..35aa9cf8c1 100644 --- a/hibernate-core/src/test/java/org/hibernate/dialect/SQLServer2005DialectTestCase.java +++ b/hibernate-core/src/test/java/org/hibernate/dialect/SQLServer2005DialectTestCase.java @@ -81,6 +81,19 @@ public class SQLServer2005DialectTestCase extends BaseUnitTestCase { ); } + @Test + @TestForIssue(jiraKey = "HHH-8301") + public void testGetLimitStringAliasGeneration() { + final String notAliasedSQL = "select column1, column2, column3, column4 from table1"; + + assertEquals( + "WITH query AS (SELECT inner_query.*, ROW_NUMBER() OVER (ORDER BY CURRENT_TIMESTAMP) as __hibernate_row_nr__ FROM ( " + + "select column1 as page0_, column2 as page1_, column3 as page2_, column4 as page3_ from table1 ) inner_query ) " + + "SELECT page0_, page1_, page2_, page3_ FROM query WHERE __hibernate_row_nr__ >= ? AND __hibernate_row_nr__ < ?", + dialect.buildLimitHandler( notAliasedSQL, toRowSelection( 3, 5 ) ).getProcessedSql() + ); + } + @Test @TestForIssue(jiraKey = "HHH-7019") public void testGetLimitStringWithSubselect() { From 6cabc326b8056f92005bd2c8d4fa01c9eb04f555 Mon Sep 17 00:00:00 2001 From: Strong Liu Date: Sat, 15 Jun 2013 15:58:48 +0800 Subject: [PATCH 35/36] HHH-8312 - named parameters binding are not correct when used within subquery --- .../dialect/AbstractTransactSQLDialect.java | 2 +- .../java/org/hibernate/dialect/Dialect.java | 2 +- .../dialect/SybaseASE157Dialect.java | 2 +- .../engine/jdbc/ColumnNameCache.java | 2 +- .../hql/internal/ast/QueryTranslatorImpl.java | 6 +- .../hql/internal/ast/SqlGenerator.java | 4 +- .../hql/internal/ast/tree/FromElement.java | 17 ++-- .../hql/internal/ast/tree/IndexNode.java | 21 +++-- .../java/org/hibernate/loader/Loader.java | 69 +++++++------- .../collection/SubselectCollectionLoader.java | 11 ++- .../collection/SubselectOneToManyLoader.java | 12 ++- .../hibernate/loader/custom/CustomLoader.java | 2 +- .../org/hibernate/loader/hql/QueryLoader.java | 92 +++++++------------ ...bstractExplicitParameterSpecification.java | 16 +--- ...ectionFilterKeyParameterSpecification.java | 16 +--- .../DynamicFilterParameterSpecification.java | 16 +--- .../param/NamedParameterSpecification.java | 2 +- .../PositionalParameterSpecification.java | 2 +- ...VersionTypeSeedParameterSpecification.java | 18 +--- .../org/hibernate/sql/ForUpdateFragment.java | 8 +- .../org/hibernate/type/ComponentType.java | 53 +++++------ .../subselect/CompositeIdTypeBindingTest.java | 89 ++++++++++++++++++ .../hibernate/test/subselect/Employee.java | 53 +++++++++++ .../test/subselect/Employeegroup.java | 70 ++++++++++++++ .../test/subselect/EmployeegroupId.java | 58 ++++++++++++ 25 files changed, 434 insertions(+), 209 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/subselect/CompositeIdTypeBindingTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/subselect/Employee.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/subselect/Employeegroup.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/subselect/EmployeegroupId.java diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/AbstractTransactSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/AbstractTransactSQLDialect.java index 915c492f43..8e3c084207 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/AbstractTransactSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/AbstractTransactSQLDialect.java @@ -178,7 +178,7 @@ abstract class AbstractTransactSQLDialect extends Dialect { } @Override - public String applyLocksToSql(String sql, LockOptions aliasedLockOptions, Map keyColumnNames) { + public String applyLocksToSql(String sql, LockOptions aliasedLockOptions, Map keyColumnNames) { // TODO: merge additional lockoptions support in Dialect.applyLocksToSql final Iterator itr = aliasedLockOptions.getAliasLockIterator(); final StringBuilder buffer = new StringBuilder( sql ); 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 5119f540ef..bd1e08cbbd 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -1452,7 +1452,7 @@ public abstract class Dialect implements ConversionContext { * @param keyColumnNames a map of key columns indexed by aliased table names. * @return the modified SQL string. */ - public String applyLocksToSql(String sql, LockOptions aliasedLockOptions, Map keyColumnNames) { + public String applyLocksToSql(String sql, LockOptions aliasedLockOptions, Map keyColumnNames) { return sql + new ForUpdateFragment( this, aliasedLockOptions, keyColumnNames ).toFragmentString(); } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASE157Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASE157Dialect.java index 9e164b9e05..6cf85a47ed 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASE157Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASE157Dialect.java @@ -96,7 +96,7 @@ public class SybaseASE157Dialect extends SybaseASE15Dialect { } @Override - public String applyLocksToSql(String sql, LockOptions aliasedLockOptions, Map keyColumnNames) { + public String applyLocksToSql(String sql, LockOptions aliasedLockOptions, Map keyColumnNames) { return sql + new ForUpdateFragment( this, aliasedLockOptions, keyColumnNames ).toFragmentString(); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/ColumnNameCache.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/ColumnNameCache.java index c1b006958c..e4f7434ba6 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/ColumnNameCache.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/ColumnNameCache.java @@ -36,7 +36,7 @@ import java.util.concurrent.ConcurrentHashMap; public class ColumnNameCache { private static final float LOAD_FACTOR = .75f; - private final Map columnNameToIndexCache; + private final ConcurrentHashMap columnNameToIndexCache; /** * Constructs a ColumnNameCache diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/QueryTranslatorImpl.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/QueryTranslatorImpl.java index d7f7756641..da87cb4f12 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/QueryTranslatorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/QueryTranslatorImpl.java @@ -69,6 +69,7 @@ import org.hibernate.internal.util.ReflectHelper; import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.collections.IdentitySet; import org.hibernate.loader.hql.QueryLoader; +import org.hibernate.param.ParameterSpecification; import org.hibernate.persister.entity.Queryable; import org.hibernate.type.Type; @@ -101,7 +102,7 @@ public class QueryTranslatorImpl implements FilterTranslator { private String sql; private ParameterTranslations paramTranslations; - private List collectedParameterSpecifications; + private List collectedParameterSpecifications; /** @@ -569,12 +570,11 @@ public class QueryTranslatorImpl implements FilterTranslator { public ParameterTranslations getParameterTranslations() { if ( paramTranslations == null ) { paramTranslations = new ParameterTranslationsImpl( getWalker().getParameters() ); -// paramTranslations = new ParameterTranslationsImpl( collectedParameterSpecifications ); } return paramTranslations; } - public List getCollectedParameterSpecifications() { + public List getCollectedParameterSpecifications() { return collectedParameterSpecifications; } diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/SqlGenerator.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/SqlGenerator.java index 0c9b8846b5..e8339b99c2 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/SqlGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/SqlGenerator.java @@ -76,7 +76,7 @@ public class SqlGenerator extends SqlGeneratorBase implements ErrorReporter { private SessionFactoryImplementor sessionFactory; private LinkedList outputStack = new LinkedList(); private final ASTPrinter printer = new ASTPrinter( SqlTokenTypes.class ); - private List collectedParameters = new ArrayList(); + private List collectedParameters = new ArrayList(); // handle trace logging ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -106,7 +106,7 @@ public class SqlGenerator extends SqlGeneratorBase implements ErrorReporter { LOG.trace( prefix + ruleName ); } - public List getCollectedParameters() { + public List getCollectedParameters() { return collectedParameters; } diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElement.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElement.java index ceb1b87ab8..3138a76940 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElement.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElement.java @@ -452,16 +452,12 @@ public class FromElement extends HqlSqlWalkerNode implements DisplayableNode, Pa this.alias = alias; } - /** - * {@inheritDoc} - */ + @Override public String getSqlFragment() { return persisterDiscriminatorMetadata.getSqlFragment( alias ); } - /** - * {@inheritDoc} - */ + @Override public Type getResolutionType() { return persisterDiscriminatorMetadata.getResolutionType(); } @@ -660,21 +656,24 @@ public class FromElement extends HqlSqlWalkerNode implements DisplayableNode, Pa // ParameterContainer impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - private List embeddedParameters; + private List embeddedParameters; + @Override public void addEmbeddedParameter(ParameterSpecification specification) { if ( embeddedParameters == null ) { - embeddedParameters = new ArrayList(); + embeddedParameters = new ArrayList(); } embeddedParameters.add( specification ); } + @Override public boolean hasEmbeddedParameters() { return embeddedParameters != null && ! embeddedParameters.isEmpty(); } + @Override public ParameterSpecification[] getEmbeddedParameters() { - return ( ParameterSpecification[] ) embeddedParameters.toArray( new ParameterSpecification[ embeddedParameters.size() ] ); + return embeddedParameters.toArray( new ParameterSpecification[ embeddedParameters.size() ] ); } public ParameterSpecification getIndexCollectionSelectorParamSpec() { diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/IndexNode.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/IndexNode.java index c579b9b6d5..0c12b17c5e 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/IndexNode.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/IndexNode.java @@ -147,14 +147,14 @@ public class IndexNode extends FromReferenceNode { } String selectorExpression = gen.getSQL(); joinSequence.addCondition( collectionTableAlias + '.' + indexCols[0] + " = " + selectorExpression ); - List paramSpecs = gen.getCollectedParameters(); + List paramSpecs = gen.getCollectedParameters(); if ( paramSpecs != null ) { switch ( paramSpecs.size() ) { case 0 : // nothing to do break; case 1 : - ParameterSpecification paramSpec = ( ParameterSpecification ) paramSpecs.get( 0 ); + ParameterSpecification paramSpec = paramSpecs.get( 0 ); paramSpec.setExpectedType( queryableCollection.getIndexType() ); fromElement.setIndexCollectionSelectorParamSpec( paramSpec ); break; @@ -176,39 +176,40 @@ public class IndexNode extends FromReferenceNode { * In the (rare?) case where the index selector contains multiple parameters... */ private static class AggregatedIndexCollectionSelectorParameterSpecifications implements ParameterSpecification { - private final List paramSpecs; + private final List paramSpecs; - public AggregatedIndexCollectionSelectorParameterSpecifications(List paramSpecs) { + public AggregatedIndexCollectionSelectorParameterSpecifications(List paramSpecs) { this.paramSpecs = paramSpecs; } + @Override public int bind(PreparedStatement statement, QueryParameters qp, SessionImplementor session, int position) throws SQLException { int bindCount = 0; - Iterator itr = paramSpecs.iterator(); - while ( itr.hasNext() ) { - final ParameterSpecification paramSpec = ( ParameterSpecification ) itr.next(); + for ( ParameterSpecification paramSpec : paramSpecs ) { bindCount += paramSpec.bind( statement, qp, session, position + bindCount ); } return bindCount; } + @Override public Type getExpectedType() { return null; } + @Override public void setExpectedType(Type expectedType) { } + @Override public String renderDisplayInfo() { return "index-selector [" + collectDisplayInfo() + "]" ; } private String collectDisplayInfo() { StringBuilder buffer = new StringBuilder(); - Iterator itr = paramSpecs.iterator(); - while ( itr.hasNext() ) { - buffer.append( ( ( ParameterSpecification ) itr.next() ).renderDisplayInfo() ); + for ( ParameterSpecification paramSpec : paramSpecs ) { + buffer.append( ( paramSpec ).renderDisplayInfo() ); } return buffer.toString(); } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/Loader.java b/hibernate-core/src/main/java/org/hibernate/loader/Loader.java index 784559c18d..685c7a778d 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/Loader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/Loader.java @@ -79,6 +79,7 @@ import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.FetchingScrollableResultsImpl; import org.hibernate.internal.ScrollableResultsImpl; import org.hibernate.internal.util.StringHelper; +import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.loader.spi.AfterLoadAction; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; @@ -110,7 +111,7 @@ import org.hibernate.type.VersionType; public abstract class Loader { protected static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, Loader.class.getName()); - + protected static final boolean DEBUG_ENABLED = LOG.isDebugEnabled(); private final SessionFactoryImplementor factory; private ColumnNameCache columnNameCache; @@ -931,9 +932,9 @@ public abstract class Loader { EntityKey[] keys = new EntityKey[entitySpan]; //we can reuse it for each row LOG.trace( "Processing result set" ); int count; - boolean isDebugEnabled = LOG.isDebugEnabled(); + for ( count = 0; count < maxRows && rs.next(); count++ ) { - if ( isDebugEnabled ) + if ( DEBUG_ENABLED ) LOG.debugf( "Result set row: %s", count ); Object result = getRowFromResultSet( rs, @@ -975,8 +976,10 @@ public abstract class Loader { protected boolean hasSubselectLoadableCollections() { final Loadable[] loadables = getEntityPersisters(); - for (int i=0; i namedParams, final int startIndex, final SessionImplementor session) throws SQLException, HibernateException { - if ( namedParams != null ) { - // assumes that types are all of span 1 - Iterator iter = namedParams.entrySet().iterator(); - final boolean debugEnabled = LOG.isDebugEnabled(); - int result = 0; - while ( iter.hasNext() ) { - Map.Entry e = ( Map.Entry ) iter.next(); - String name = ( String ) e.getKey(); - TypedValue typedval = ( TypedValue ) e.getValue(); - int[] locs = getNamedParameterLocs( name ); - for ( int i = 0; i < locs.length; i++ ) { - if ( debugEnabled ) LOG.debugf( "bindNamedParameters() %s -> %s [%s]", typedval.getValue(), name, locs[i] + startIndex ); - typedval.getType().nullSafeSet( statement, typedval.getValue(), locs[i] + startIndex, session ); - } - result += locs.length; - } + int result = 0; + if ( CollectionHelper.isEmpty( namedParams ) ) { return result; } - else { - return 0; + + for ( String name : namedParams.keySet() ) { + TypedValue typedValue = namedParams.get( name ); + int columnSpan = typedValue.getType().getColumnSpan( getFactory() ); + int[] locs = getNamedParameterLocs( name ); + for ( int loc : locs ) { + if ( DEBUG_ENABLED ) { + LOG.debugf( + "bindNamedParameters() %s -> %s [%s]", + typedValue.getValue(), + name, + loc + startIndex + ); + } + int start = loc * columnSpan + startIndex; + typedValue.getType().nullSafeSet( statement, typedValue.getValue(), start, session ); + } + result += locs.length; } + return result; } public int[] getNamedParameterLocs(String name) { @@ -2080,7 +2084,7 @@ public abstract class Loader { private ColumnNameCache retreiveColumnNameToIndexCache(ResultSet rs) throws SQLException { if ( columnNameCache == null ) { - LOG.trace( "Building columnName->columnIndex cache" ); + LOG.trace( "Building columnName -> columnIndex cache" ); columnNameCache = new ColumnNameCache( rs.getMetaData().getColumnCount() ); } @@ -2293,7 +2297,7 @@ public abstract class Loader { final Serializable[] ids, final Object[] parameterValues, final Type[] parameterTypes, - final Map namedParameters, + final Map namedParameters, final Type type) throws HibernateException { Type[] idTypes = new Type[ids.length]; @@ -2551,7 +2555,6 @@ public abstract class Loader { // whether scrolling of their result set should be allowed. // // By default it is allowed. - return; } /** diff --git a/hibernate-core/src/main/java/org/hibernate/loader/collection/SubselectCollectionLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/collection/SubselectCollectionLoader.java index 81246e8aeb..009cd54731 100755 --- a/hibernate-core/src/main/java/org/hibernate/loader/collection/SubselectCollectionLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/collection/SubselectCollectionLoader.java @@ -35,6 +35,7 @@ import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.QueryParameters; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.TypedValue; import org.hibernate.persister.collection.QueryableCollection; import org.hibernate.type.Type; @@ -47,15 +48,15 @@ public class SubselectCollectionLoader extends BasicCollectionLoader { private final Serializable[] keys; private final Type[] types; private final Object[] values; - private final Map namedParameters; - private final Map namedParameterLocMap; + private final Map namedParameters; + private final Map namedParameterLocMap; public SubselectCollectionLoader( QueryableCollection persister, String subquery, Collection entityKeys, QueryParameters queryParameters, - Map namedParameterLocMap, + Map namedParameterLocMap, SessionFactoryImplementor factory, LoadQueryInfluencers loadQueryInfluencers) throws MappingException { super( persister, 1, subquery, factory, loadQueryInfluencers ); @@ -74,6 +75,7 @@ public class SubselectCollectionLoader extends BasicCollectionLoader { } + @Override public void initialize(Serializable id, SessionImplementor session) throws HibernateException { loadCollectionSubselect( @@ -86,8 +88,9 @@ public class SubselectCollectionLoader extends BasicCollectionLoader { ); } + @Override public int[] getNamedParameterLocs(String name) { - return (int[]) namedParameterLocMap.get( name ); + return namedParameterLocMap.get( name ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/collection/SubselectOneToManyLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/collection/SubselectOneToManyLoader.java index 4ef55cee5d..f830301645 100755 --- a/hibernate-core/src/main/java/org/hibernate/loader/collection/SubselectOneToManyLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/collection/SubselectOneToManyLoader.java @@ -35,6 +35,7 @@ import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.QueryParameters; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.TypedValue; import org.hibernate.persister.collection.QueryableCollection; import org.hibernate.type.Type; @@ -47,15 +48,15 @@ public class SubselectOneToManyLoader extends OneToManyLoader { private final Serializable[] keys; private final Type[] types; private final Object[] values; - private final Map namedParameters; - private final Map namedParameterLocMap; + private final Map namedParameters; + private final Map namedParameterLocMap; public SubselectOneToManyLoader( QueryableCollection persister, String subquery, Collection entityKeys, QueryParameters queryParameters, - Map namedParameterLocMap, + Map namedParameterLocMap, SessionFactoryImplementor factory, LoadQueryInfluencers loadQueryInfluencers) throws MappingException { super( persister, 1, subquery, factory, loadQueryInfluencers ); @@ -73,6 +74,7 @@ public class SubselectOneToManyLoader extends OneToManyLoader { this.namedParameterLocMap = namedParameterLocMap; } + @Override public void initialize(Serializable id, SessionImplementor session) throws HibernateException { loadCollectionSubselect( session, @@ -83,9 +85,9 @@ public class SubselectOneToManyLoader extends OneToManyLoader { getKeyType() ); } - + @Override public int[] getNamedParameterLocs(String name) { - return (int[]) namedParameterLocMap.get( name ); + return namedParameterLocMap.get( name ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/custom/CustomLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/custom/CustomLoader.java index b626e375ec..e8fd1013c6 100755 --- a/hibernate-core/src/main/java/org/hibernate/loader/custom/CustomLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/custom/CustomLoader.java @@ -459,7 +459,7 @@ public class CustomLoader extends Loader { ); } if ( loc instanceof Integer ) { - return new int[] { ( ( Integer ) loc ).intValue() }; + return new int[] { (Integer) loc }; } else { return ArrayHelper.toIntArray( ( List ) loc ); diff --git a/hibernate-core/src/main/java/org/hibernate/loader/hql/QueryLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/hql/QueryLoader.java index 3a45e39d53..e3a4c73136 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/hql/QueryLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/hql/QueryLoader.java @@ -88,7 +88,7 @@ public class QueryLoader extends BasicLoader { //private Type[] sqlResultTypes; private Type[] queryReturnTypes; - private final Map sqlAliasByEntityAlias = new HashMap(8); + private final Map sqlAliasByEntityAlias = new HashMap(8); private EntityType[] ownerAssociationTypes; private int[] owners; @@ -209,15 +209,15 @@ public class QueryLoader extends BasicLoader { public final void validateScrollability() throws HibernateException { queryTranslator.validateScrollability(); } - + @Override protected boolean needsFetchingScroll() { return queryTranslator.containsCollectionFetches(); } - + @Override public Loadable[] getEntityPersisters() { return entityPersisters; } - + @Override public String[] getAliases() { return sqlAliases; } @@ -225,15 +225,15 @@ public class QueryLoader extends BasicLoader { public String[] getSqlAliasSuffixes() { return sqlAliasSuffixes; } - + @Override public String[] getSuffixes() { return getSqlAliasSuffixes(); } - + @Override public String[] getCollectionSuffixes() { return collectionSuffixes; } - + @Override protected String getQueryIdentifier() { return queryTranslator.getQueryIdentifier(); } @@ -241,6 +241,7 @@ public class QueryLoader extends BasicLoader { /** * The SQL query string to be called. */ + @Override public String getSQLString() { return queryTranslator.getSQLString(); } @@ -249,14 +250,15 @@ public class QueryLoader extends BasicLoader { * An (optional) persister for a collection to be initialized; only collection loaders * return a non-null value */ + @Override protected CollectionPersister[] getCollectionPersisters() { return collectionPersisters; } - + @Override protected int[] getCollectionOwners() { return collectionOwners; } - + @Override protected boolean[] getEntityEagerPropertyFetches() { return entityEagerPropertyFetches; } @@ -265,16 +267,17 @@ public class QueryLoader extends BasicLoader { * An array of indexes of the entity that owns a one-to-one association * to the entity at the given index (-1 if there is no "owner") */ + @Override protected int[] getOwners() { return owners; } - + @Override protected EntityType[] getOwnerAssociationTypes() { return ownerAssociationTypes; } // -- Loader overrides -- - + @Override protected boolean isSubselectLoadingEnabled() { return hasSubselectLoadableCollections(); } @@ -282,6 +285,7 @@ public class QueryLoader extends BasicLoader { /** * @param lockOptions a collection of lock modes specified dynamically via the Query interface */ + @Override protected LockMode[] getLockModes(LockOptions lockOptions) { if ( lockOptions == null ) { return defaultLockModes; @@ -341,16 +345,14 @@ public class QueryLoader extends BasicLoader { // we need both the set of locks and the columns to reference in locks // as the ultimate output of this section... final LockOptions locks = new LockOptions( lockOptions.getLockMode() ); - final Map keyColumnNames = dialect.forUpdateOfColumns() ? new HashMap() : null; + final Map keyColumnNames = dialect.forUpdateOfColumns() ? new HashMap() : null; locks.setScope( lockOptions.getScope() ); locks.setTimeOut( lockOptions.getTimeOut() ); - final Iterator itr = sqlAliasByEntityAlias.entrySet().iterator(); - while ( itr.hasNext() ) { - final Map.Entry entry = (Map.Entry) itr.next(); - final String userAlias = (String) entry.getKey(); - final String drivingSqlAlias = (String) entry.getValue(); + for ( Map.Entry entry : sqlAliasByEntityAlias.entrySet() ) { + final String userAlias = entry.getKey(); + final String drivingSqlAlias = entry.getValue(); if ( drivingSqlAlias == null ) { throw new IllegalArgumentException( "could not locate alias to apply lock mode : " + userAlias ); } @@ -360,8 +362,8 @@ public class QueryLoader extends BasicLoader { // the exception case here is joined-subclass hierarchies where we instead // want to apply the lock against the root table (for all other strategies, // it just happens that driving and root are the same). - final QueryNode select = ( QueryNode ) queryTranslator.getSqlAST(); - final Lockable drivingPersister = ( Lockable ) select.getFromClause() + final QueryNode select = (QueryNode) queryTranslator.getSqlAST(); + final Lockable drivingPersister = (Lockable) select.getFromClause() .findFromElementByUserOrSqlAlias( userAlias, drivingSqlAlias ) .getQueryable(); final String sqlAlias = drivingPersister.getRootTableAlias( drivingSqlAlias ); @@ -377,7 +379,7 @@ public class QueryLoader extends BasicLoader { // apply the collected locks and columns return dialect.applyLocksToSql( sql, locks, keyColumnNames ); } - + @Override protected void applyPostLoadLocks(Object[] row, LockMode[] lockModesArray, SessionImplementor session) { // todo : scalars??? // if ( row.length != lockModesArray.length ) { @@ -393,7 +395,7 @@ public class QueryLoader extends BasicLoader { // } // } } - + @Override protected boolean upgradeLocks() { return true; } @@ -401,18 +403,18 @@ public class QueryLoader extends BasicLoader { private boolean hasSelectNew() { return aggregatedSelectExpression != null && aggregatedSelectExpression.getResultTransformer() != null; } - + @Override protected String[] getResultRowAliases() { return queryReturnAliases; } - + @Override protected ResultTransformer resolveResultTransformer(ResultTransformer resultTransformer) { final ResultTransformer implicitResultTransformer = aggregatedSelectExpression == null ? null : aggregatedSelectExpression.getResultTransformer(); return HolderInstantiator.resolveResultTransformer( implicitResultTransformer, resultTransformer ); } - + @Override protected boolean[] includeInResultRow() { boolean[] includeInResultTuple = includeInSelect; if ( hasScalars ) { @@ -421,7 +423,7 @@ public class QueryLoader extends BasicLoader { } return includeInResultTuple; } - + @Override protected Object getResultColumnOrRow(Object[] row, ResultTransformer transformer, ResultSet rs, SessionImplementor session) throws SQLException, HibernateException { @@ -433,6 +435,7 @@ public class QueryLoader extends BasicLoader { ); } + @Override protected Object[] getResultRow(Object[] row, ResultSet rs, SessionImplementor session) throws SQLException, HibernateException { Object[] resultRow; @@ -450,6 +453,8 @@ public class QueryLoader extends BasicLoader { return resultRow; } + @SuppressWarnings("unchecked") + @Override protected List getResultList(List results, ResultTransformer resultTransformer) throws QueryException { // meant to handle dynamic instantiation queries... HolderInstantiator holderInstantiator = buildHolderInstantiator( resultTransformer ); @@ -579,6 +584,7 @@ public class QueryLoader extends BasicLoader { /** * Returns the locations of all occurrences of the named parameter. */ + @Override public int[] getNamedParameterLocs(String name) throws QueryException { return queryTranslator.getParameterTranslations().getNamedParameterSqlLocations( name ); } @@ -586,7 +592,7 @@ public class QueryLoader extends BasicLoader { /** * We specifically override this method here, because in general we know much more * about the parameters and their appropriate bind positions here then we do in - * our super because we track them explciitly here through the ParameterSpecification + * our super because we track them explicitly here through the ParameterSpecification * interface. * * @param queryParameters The encapsulation of the parameter values to be bound. @@ -595,45 +601,17 @@ public class QueryLoader extends BasicLoader { * @return The number of JDBC bind positions actually bound during this method execution. * @throws SQLException Indicates problems performing the binding. */ + @Override protected int bindParameterValues( final PreparedStatement statement, final QueryParameters queryParameters, final int startIndex, final SessionImplementor session) throws SQLException { -// int position = bindFilterParameterValues( statement, queryParameters, startIndex, session ); int position = startIndex; -// List parameterSpecs = queryTranslator.getSqlAST().getWalker().getParameters(); - List parameterSpecs = queryTranslator.getCollectedParameterSpecifications(); - Iterator itr = parameterSpecs.iterator(); - while ( itr.hasNext() ) { - ParameterSpecification spec = ( ParameterSpecification ) itr.next(); + List parameterSpecs = queryTranslator.getCollectedParameterSpecifications(); + for ( ParameterSpecification spec : parameterSpecs ) { position += spec.bind( statement, queryParameters, session, position ); } return position - startIndex; } - - private int bindFilterParameterValues( - PreparedStatement st, - QueryParameters queryParameters, - int position, - SessionImplementor session) throws SQLException { - // todo : better to handle dynamic filters through implicit DynamicFilterParameterSpecification - // see the discussion there in DynamicFilterParameterSpecification's javadocs as to why - // it is currently not done that way. - int filteredParamCount = queryParameters.getFilteredPositionalParameterTypes() == null - ? 0 - : queryParameters.getFilteredPositionalParameterTypes().length; - int nonfilteredParamCount = queryParameters.getPositionalParameterTypes() == null - ? 0 - : queryParameters.getPositionalParameterTypes().length; - int filterParamCount = filteredParamCount - nonfilteredParamCount; - for ( int i = 0; i < filterParamCount; i++ ) { - Type type = queryParameters.getFilteredPositionalParameterTypes()[i]; - Object value = queryParameters.getFilteredPositionalParameterValues()[i]; - type.nullSafeSet( st, value, position, session ); - position += type.getColumnSpan( getFactory() ); - } - - return position; - } } diff --git a/hibernate-core/src/main/java/org/hibernate/param/AbstractExplicitParameterSpecification.java b/hibernate-core/src/main/java/org/hibernate/param/AbstractExplicitParameterSpecification.java index de27388ea4..a5fffb6804 100644 --- a/hibernate-core/src/main/java/org/hibernate/param/AbstractExplicitParameterSpecification.java +++ b/hibernate-core/src/main/java/org/hibernate/param/AbstractExplicitParameterSpecification.java @@ -46,30 +46,22 @@ public abstract class AbstractExplicitParameterSpecification implements Explicit this.sourceColumn = sourceColumn; } - /** - * {@inheritDoc} - */ + @Override public int getSourceLine() { return sourceLine; } - /** - * {@inheritDoc} - */ + @Override public int getSourceColumn() { return sourceColumn; } - /** - * {@inheritDoc} - */ + @Override public Type getExpectedType() { return expectedType; } - /** - * {@inheritDoc} - */ + @Override public void setExpectedType(Type expectedType) { this.expectedType = expectedType; } diff --git a/hibernate-core/src/main/java/org/hibernate/param/CollectionFilterKeyParameterSpecification.java b/hibernate-core/src/main/java/org/hibernate/param/CollectionFilterKeyParameterSpecification.java index 67c774f2d1..eb8f6be70a 100644 --- a/hibernate-core/src/main/java/org/hibernate/param/CollectionFilterKeyParameterSpecification.java +++ b/hibernate-core/src/main/java/org/hibernate/param/CollectionFilterKeyParameterSpecification.java @@ -55,9 +55,7 @@ public class CollectionFilterKeyParameterSpecification implements ParameterSpeci this.queryParameterPosition = queryParameterPosition; } - /** - * {@inheritDoc} - */ + @Override public int bind( PreparedStatement statement, QueryParameters qp, @@ -68,23 +66,17 @@ public class CollectionFilterKeyParameterSpecification implements ParameterSpeci return keyType.getColumnSpan( session.getFactory() ); } - /** - * {@inheritDoc} - */ + @Override public Type getExpectedType() { return keyType; } - /** - * {@inheritDoc} - */ + @Override public void setExpectedType(Type expectedType) { // todo : throw exception? } - /** - * {@inheritDoc} - */ + @Override public String renderDisplayInfo() { return "collection-filter-key=" + collectionRole; } diff --git a/hibernate-core/src/main/java/org/hibernate/param/DynamicFilterParameterSpecification.java b/hibernate-core/src/main/java/org/hibernate/param/DynamicFilterParameterSpecification.java index cc1fca6054..3258d76639 100644 --- a/hibernate-core/src/main/java/org/hibernate/param/DynamicFilterParameterSpecification.java +++ b/hibernate-core/src/main/java/org/hibernate/param/DynamicFilterParameterSpecification.java @@ -60,9 +60,7 @@ public class DynamicFilterParameterSpecification implements ParameterSpecificati this.definedParameterType = definedParameterType; } - /** - * {@inheritDoc} - */ + @Override public int bind( PreparedStatement statement, QueryParameters qp, @@ -85,23 +83,17 @@ public class DynamicFilterParameterSpecification implements ParameterSpecificati } } - /** - * {@inheritDoc} - */ + @Override public Type getExpectedType() { return definedParameterType; } - /** - * {@inheritDoc} - */ + @Override public void setExpectedType(Type expectedType) { // todo : throw exception? maybe warn if not the same? } - /** - * {@inheritDoc} - */ + @Override public String renderDisplayInfo() { return "dynamic-filter={filterName=" + filterName + ",paramName=" + parameterName + "}"; } diff --git a/hibernate-core/src/main/java/org/hibernate/param/NamedParameterSpecification.java b/hibernate-core/src/main/java/org/hibernate/param/NamedParameterSpecification.java index 90d08fb14f..02efca32b2 100644 --- a/hibernate-core/src/main/java/org/hibernate/param/NamedParameterSpecification.java +++ b/hibernate-core/src/main/java/org/hibernate/param/NamedParameterSpecification.java @@ -35,7 +35,7 @@ import org.hibernate.engine.spi.TypedValue; * * @author Steve Ebersole */ -public class NamedParameterSpecification extends AbstractExplicitParameterSpecification implements ParameterSpecification { +public class NamedParameterSpecification extends AbstractExplicitParameterSpecification { private final String name; /** diff --git a/hibernate-core/src/main/java/org/hibernate/param/PositionalParameterSpecification.java b/hibernate-core/src/main/java/org/hibernate/param/PositionalParameterSpecification.java index f9f314a0e0..24c047b96d 100644 --- a/hibernate-core/src/main/java/org/hibernate/param/PositionalParameterSpecification.java +++ b/hibernate-core/src/main/java/org/hibernate/param/PositionalParameterSpecification.java @@ -35,7 +35,7 @@ import org.hibernate.type.Type; * * @author Steve Ebersole */ -public class PositionalParameterSpecification extends AbstractExplicitParameterSpecification implements ParameterSpecification { +public class PositionalParameterSpecification extends AbstractExplicitParameterSpecification { private final int hqlPosition; /** diff --git a/hibernate-core/src/main/java/org/hibernate/param/VersionTypeSeedParameterSpecification.java b/hibernate-core/src/main/java/org/hibernate/param/VersionTypeSeedParameterSpecification.java index 84e2580d24..2407e23bb0 100644 --- a/hibernate-core/src/main/java/org/hibernate/param/VersionTypeSeedParameterSpecification.java +++ b/hibernate-core/src/main/java/org/hibernate/param/VersionTypeSeedParameterSpecification.java @@ -37,7 +37,7 @@ import org.hibernate.type.VersionType; * @author Steve Ebersole */ public class VersionTypeSeedParameterSpecification implements ParameterSpecification { - private VersionType type; + private final VersionType type; /** * Constructs a version seed parameter bind specification. @@ -48,32 +48,24 @@ public class VersionTypeSeedParameterSpecification implements ParameterSpecifica this.type = type; } - /** - * {@inheritDoc} - */ + @Override public int bind(PreparedStatement statement, QueryParameters qp, SessionImplementor session, int position) throws SQLException { type.nullSafeSet( statement, type.seed( session ), position, session ); return 1; } - /** - * {@inheritDoc} - */ + @Override public Type getExpectedType() { return type; } - /** - * {@inheritDoc} - */ + @Override public void setExpectedType(Type expectedType) { // expected type is intrinsic here... } - /** - * {@inheritDoc} - */ + @Override public String renderDisplayInfo() { return "version-seed, type=" + type; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ForUpdateFragment.java b/hibernate-core/src/main/java/org/hibernate/sql/ForUpdateFragment.java index 71895a88dd..4897bff2ea 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ForUpdateFragment.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ForUpdateFragment.java @@ -48,7 +48,7 @@ public class ForUpdateFragment { this.dialect = dialect; } - public ForUpdateFragment(Dialect dialect, LockOptions lockOptions, Map keyColumnNames) throws QueryException { + public ForUpdateFragment(Dialect dialect, LockOptions lockOptions, Map keyColumnNames) throws QueryException { this( dialect ); LockMode upgradeType = null; Iterator iter = lockOptions.getAliasLockIterator(); @@ -68,13 +68,13 @@ public class ForUpdateFragment { if ( LockMode.READ.lessThan( lockMode ) ) { final String tableAlias = ( String ) me.getKey(); if ( dialect.forUpdateOfColumns() ) { - String[] keyColumns = ( String[] ) keyColumnNames.get( tableAlias ); //use the id column alias + String[] keyColumns = keyColumnNames.get( tableAlias ); //use the id column alias if ( keyColumns == null ) { throw new IllegalArgumentException( "alias not found: " + tableAlias ); } keyColumns = StringHelper.qualify( tableAlias, keyColumns ); - for ( int i = 0; i < keyColumns.length; i++ ) { - addTableAlias( keyColumns[i] ); + for ( String keyColumn : keyColumns ) { + addTableAlias( keyColumn ); } } else { diff --git a/hibernate-core/src/main/java/org/hibernate/type/ComponentType.java b/hibernate-core/src/main/java/org/hibernate/type/ComponentType.java index 3febc377dd..7ae2972361 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/ComponentType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/ComponentType.java @@ -120,8 +120,8 @@ public class ComponentType extends AbstractType implements CompositeType, Proced int n = 0; for ( int i = 0; i < propertySpan; i++ ) { int[] subtypes = propertyTypes[i].sqlTypes( mapping ); - for ( int j = 0; j < subtypes.length; j++ ) { - sqlTypes[n++] = subtypes[j]; + for ( int subtype : subtypes ) { + sqlTypes[n++] = subtype; } } return sqlTypes; @@ -327,7 +327,7 @@ public class ComponentType extends AbstractType implements CompositeType, Proced return old != null; } if ( old == null ) { - return current != null; + return true; } Object[] currentValues = getPropertyValues( current, session ); Object[] oldValues = ( Object[] ) old; @@ -344,12 +344,12 @@ public class ComponentType extends AbstractType implements CompositeType, Proced return false; } - + @Override public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throws HibernateException, SQLException { return resolve( hydrate( rs, names, session, owner ), session, owner ); } - + @Override public void nullSafeSet(PreparedStatement st, Object value, int begin, SessionImplementor session) throws HibernateException, SQLException { @@ -360,7 +360,7 @@ public class ComponentType extends AbstractType implements CompositeType, Proced begin += propertyTypes[i].getColumnSpan( session.getFactory() ); } } - + @Override public void nullSafeSet( PreparedStatement st, Object value, @@ -401,13 +401,13 @@ public class ComponentType extends AbstractType implements CompositeType, Proced return getPropertyValues( value, entityMode ); } } - + @Override public Object nullSafeGet(ResultSet rs, String name, SessionImplementor session, Object owner) throws HibernateException, SQLException { return nullSafeGet( rs, new String[] {name}, session, owner ); } - + @Override public Object getPropertyValue(Object component, int i, SessionImplementor session) throws HibernateException { return getPropertyValue( component, i, entityMode ); @@ -417,12 +417,12 @@ public class ComponentType extends AbstractType implements CompositeType, Proced throws HibernateException { return componentTuplizer.getPropertyValue( component, i ); } - + @Override public Object[] getPropertyValues(Object component, SessionImplementor session) throws HibernateException { return getPropertyValues( component, entityMode ); } - + @Override public Object[] getPropertyValues(Object component, EntityMode entityMode) throws HibernateException { if ( component instanceof Object[] ) { @@ -435,40 +435,41 @@ public class ComponentType extends AbstractType implements CompositeType, Proced return componentTuplizer.getPropertyValues( component ); } } - + @Override public void setPropertyValues(Object component, Object[] values, EntityMode entityMode) throws HibernateException { componentTuplizer.setPropertyValues( component, values ); } - + @Override public Type[] getSubtypes() { return propertyTypes; } - + @Override public String getName() { return "component" + ArrayHelper.toString( propertyNames ); } - + @Override public String toLoggableString(Object value, SessionFactoryImplementor factory) throws HibernateException { if ( value == null ) { return "null"; } - Map result = new HashMap(); + if ( entityMode == null ) { throw new ClassCastException( value.getClass().getName() ); } + Map result = new HashMap(); Object[] values = getPropertyValues( value, entityMode ); for ( int i = 0; i < propertyTypes.length; i++ ) { result.put( propertyNames[i], propertyTypes[i].toLoggableString( values[i], factory ) ); } return StringHelper.unqualify( getName() ) + result.toString(); } - + @Override public String[] getPropertyNames() { return propertyNames; } - + @Override public Object deepCopy(Object component, SessionFactoryImplementor factory) throws HibernateException { if ( component == null ) { @@ -491,7 +492,7 @@ public class ComponentType extends AbstractType implements CompositeType, Proced return result; } - + @Override public Object replace( Object original, Object target, @@ -577,11 +578,11 @@ public class ComponentType extends AbstractType implements CompositeType, Proced return result; } - + @Override public CascadeStyle getCascadeStyle(int i) { return cascade[i]; } - + @Override public boolean isMutable() { return true; } @@ -620,7 +621,7 @@ public class ComponentType extends AbstractType implements CompositeType, Proced return result; } } - + @Override public FetchMode getFetchMode(int i) { return joinedFetch[i]; } @@ -681,7 +682,7 @@ public class ComponentType extends AbstractType implements CompositeType, Proced //for components with many-to-one associations return resolve( value, session, owner ); } - + @Override public boolean[] getPropertyNullability() { return propertyNullability; } @@ -690,15 +691,15 @@ public class ComponentType extends AbstractType implements CompositeType, Proced public boolean isXMLElement() { return true; } - + @Override public Object fromXMLNode(Node xml, Mapping factory) throws HibernateException { return xml; } - + @Override public void setToXMLNode(Node node, Object value, SessionFactoryImplementor factory) throws HibernateException { replaceNode( node, ( Element ) value ); } - + @Override public boolean[] toColumnNullness(Object value, Mapping mapping) { boolean[] result = new boolean[ getColumnSpan( mapping ) ]; if ( value == null ) { @@ -713,7 +714,7 @@ public class ComponentType extends AbstractType implements CompositeType, Proced } return result; } - + @Override public boolean isEmbedded() { return false; } diff --git a/hibernate-core/src/test/java/org/hibernate/test/subselect/CompositeIdTypeBindingTest.java b/hibernate-core/src/test/java/org/hibernate/test/subselect/CompositeIdTypeBindingTest.java new file mode 100644 index 0000000000..302c12cfcd --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/subselect/CompositeIdTypeBindingTest.java @@ -0,0 +1,89 @@ +/* + * 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.test.subselect; + +import java.util.ArrayList; +import java.util.List; + +import junit.framework.Assert; +import org.junit.Test; + +import org.hibernate.Session; +import org.hibernate.dialect.H2Dialect; +import org.hibernate.testing.SkipForDialect; +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; + +/** + * @author Strong Liu + */ +@SkipForDialect(value = H2Dialect.class, comment = "H2 doesn't support this sql syntax") +@TestForIssue( jiraKey = "HHH-8312") +public class CompositeIdTypeBindingTest extends BaseCoreFunctionalTestCase { + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Employee.class, EmployeeGroup.class }; + } + + @Test + public void testCompositeTypeBinding() { + Session session = openSession(); + session.beginTransaction(); + + EmployeeGroup employeegroup = new EmployeeGroup( new EmployeeGroupId( "a", "b" ) ); + employeegroup.addEmployee( new Employee( "stliu" ) ); + employeegroup.addEmployee( new Employee( "david" ) ); + session.save( employeegroup ); + + + employeegroup = new EmployeeGroup( new EmployeeGroupId( "c", "d" ) ); + employeegroup.addEmployee( new Employee( "gail" ) ); + employeegroup.addEmployee( new Employee( "steve" ) ); + session.save( employeegroup ); + + + session.getTransaction().commit(); + + session.close(); + + session = openSession(); + + List parameters = new ArrayList(); + parameters.add( new EmployeeGroupId( "a", "b" ) ); + parameters.add( new EmployeeGroupId( "c", "d" ) ); + parameters.add( new EmployeeGroupId( "e", "f" ) ); + + List result = session.createQuery( "select eg from EmployeeGroup eg where eg.id in (:employeegroupIds)" ) + .setParameterList( "employeegroupIds", parameters ).list(); + + Assert.assertEquals( 2, result.size() ); + + employeegroup = (EmployeeGroup) result.get( 0 ); + + Assert.assertEquals( "a", employeegroup.getId().getGroupName() ); + Assert.assertNotNull( employeegroup.getEmployees() ); + Assert.assertEquals( 2, employeegroup.getEmployees().size() ); + session.close(); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/subselect/Employee.java b/hibernate-core/src/test/java/org/hibernate/test/subselect/Employee.java new file mode 100644 index 0000000000..17852a2f0c --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/subselect/Employee.java @@ -0,0 +1,53 @@ +/* + * 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.test.subselect; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +@Entity +public class Employee { + @Id + @GeneratedValue + private Long id; + private String name; + + public String getName() { + return name; + } + + @SuppressWarnings("unused") + private Employee() { + } + + public Employee(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } +} \ No newline at end of file diff --git a/hibernate-core/src/test/java/org/hibernate/test/subselect/Employeegroup.java b/hibernate-core/src/test/java/org/hibernate/test/subselect/Employeegroup.java new file mode 100644 index 0000000000..f859eb4ab9 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/subselect/Employeegroup.java @@ -0,0 +1,70 @@ +/* + * 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.test.subselect; + + +import java.util.ArrayList; +import java.util.List; +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.OneToMany; + +import org.hibernate.annotations.Fetch; +import org.hibernate.annotations.FetchMode; + +@Entity +public class EmployeeGroup { + @Id + private EmployeeGroupId id; + + @OneToMany(cascade = CascadeType.ALL) + @Fetch(FetchMode.SUBSELECT) + private List employees = new ArrayList(); + + public EmployeeGroup(EmployeeGroupId id) { + this.id = id; + } + + @SuppressWarnings("unused") + private EmployeeGroup() { + } + + public boolean addEmployee(Employee employee) { + return employees.add(employee); + } + + public List getEmployees() { + return employees; + } + + public EmployeeGroupId getId() { + return id; + } + + @Override + public String toString() { + return id.toString(); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/subselect/EmployeegroupId.java b/hibernate-core/src/test/java/org/hibernate/test/subselect/EmployeegroupId.java new file mode 100644 index 0000000000..2a97e6033f --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/subselect/EmployeegroupId.java @@ -0,0 +1,58 @@ +/* + * 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.test.subselect; + + +import java.io.Serializable; +import javax.persistence.Embeddable; + +@Embeddable +public class EmployeeGroupId + implements Serializable { + private static final long serialVersionUID = 1L; + private String groupName; + private String departmentName; + + @SuppressWarnings("unused") + private EmployeeGroupId() { + } + + public EmployeeGroupId(String groupName, String departmentName) { + this.groupName = groupName; + this.departmentName = departmentName; + } + + public String getDepartmentName() { + return departmentName; + } + + public String getGroupName() { + return groupName; + } + + @Override + public String toString() { + return "groupName: " + groupName + ", departmentName: " + departmentName; + } +} From 899b306f2612311a88a7f1bf4175ea58d82e42e0 Mon Sep 17 00:00:00 2001 From: Strong Liu Date: Sat, 15 Jun 2013 08:15:19 +0800 Subject: [PATCH 36/36] HHH-8312 - named parameters binding are not correct when used within subquery --- .../test/subselect/{Employeegroup.java => EmployeeGroup.java} | 0 .../test/subselect/{EmployeegroupId.java => EmployeeGroupId.java} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename hibernate-core/src/test/java/org/hibernate/test/subselect/{Employeegroup.java => EmployeeGroup.java} (100%) rename hibernate-core/src/test/java/org/hibernate/test/subselect/{EmployeegroupId.java => EmployeeGroupId.java} (100%) diff --git a/hibernate-core/src/test/java/org/hibernate/test/subselect/Employeegroup.java b/hibernate-core/src/test/java/org/hibernate/test/subselect/EmployeeGroup.java similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/test/subselect/Employeegroup.java rename to hibernate-core/src/test/java/org/hibernate/test/subselect/EmployeeGroup.java diff --git a/hibernate-core/src/test/java/org/hibernate/test/subselect/EmployeegroupId.java b/hibernate-core/src/test/java/org/hibernate/test/subselect/EmployeeGroupId.java similarity index 100% rename from hibernate-core/src/test/java/org/hibernate/test/subselect/EmployeegroupId.java rename to hibernate-core/src/test/java/org/hibernate/test/subselect/EmployeeGroupId.java