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:
+ *
+ *
+ * As an example, consider a case with incrementSize of 20. Initially the
+ * database holds 1:
+ *
{@code upperLimit = (1 * 20) + 1 = 21}
+ *
{@code lowerLimit = 21 - 20 = 1}
+ *
+ * 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:
+ *
{@code upperLimit = (2 * 20) + 1 = 41}
+ *
{@code lowerLimit = 41 - 20 = 21}
+ *
+ * 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 extends Optimizer> optimizerClass;
- private final boolean isPooled;
-
- StandardOptimizerDescriptor(String externalName, Class extends Optimizer> optimizerClass) {
- this( externalName, optimizerClass, false );
- }
-
- StandardOptimizerDescriptor(String externalName, Class extends Optimizer> optimizerClass, boolean pooled) {
- this.externalName = externalName;
- this.optimizerClass = optimizerClass;
- this.isPooled = pooled;
- }
-
- public String getExternalName() {
- return externalName;
- }
-
- public Class extends Optimizer> 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:
- *
- *
- * As an example, consider a case with incrementSize of 20. Initially the
- * database holds 1:
- *
{@code upperLimit = (1 * 20) + 1 = 21}
- *
{@code lowerLimit = 21 - 20 = 1}
- *
- * 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:
- *
{@code upperLimit = (2 * 20) + 1 = 41}
- *
{@code lowerLimit = 41 - 20 = 21}
- *
- * 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 extends Optimizer> optimizerClass;
+ private final boolean isPooled;
+
+ private StandardOptimizerDescriptor(String externalName, Class extends Optimizer> optimizerClass) {
+ this( externalName, optimizerClass, false );
+ }
+
+ private StandardOptimizerDescriptor(String externalName, Class extends Optimizer> optimizerClass, boolean pooled) {
+ this.externalName = externalName;
+ this.optimizerClass = optimizerClass;
+ this.isPooled = pooled;
+ }
+
+ public String getExternalName() {
+ return externalName;
+ }
+
+ public Class extends Optimizer> 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
+ *
* 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 super Wall,?> colorAttribute = em.getMetamodel()
- .entity( Wall.class )
- .getDeclaredSingularAttribute( "color" );
+ SingularAttribute super Wall, ?> 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