Author: steve.ebersole(a)jboss.com
Date: 2010-01-21 14:37:01 -0500 (Thu, 21 Jan 2010)
New Revision: 18601
Added:
core/trunk/core/src/main/java/org/hibernate/id/CompositeNestedGeneratedValueGenerator.java
Modified:
core/trunk/core/src/main/java/org/hibernate/event/def/DefaultFlushEntityEventListener.java
core/trunk/core/src/main/java/org/hibernate/mapping/Component.java
core/trunk/core/src/main/java/org/hibernate/mapping/SimpleValue.java
Log:
HHH-4552 - Support generated value within composite keys
Modified:
core/trunk/core/src/main/java/org/hibernate/event/def/DefaultFlushEntityEventListener.java
===================================================================
---
core/trunk/core/src/main/java/org/hibernate/event/def/DefaultFlushEntityEventListener.java 2010-01-21
17:51:09 UTC (rev 18600)
+++
core/trunk/core/src/main/java/org/hibernate/event/def/DefaultFlushEntityEventListener.java 2010-01-21
19:37:01 UTC (rev 18601)
@@ -38,6 +38,7 @@
import org.hibernate.engine.EntityEntry;
import org.hibernate.engine.EntityKey;
import org.hibernate.engine.Nullability;
+import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.engine.Status;
import org.hibernate.engine.Versioning;
@@ -62,8 +63,12 @@
/**
* make sure user didn't mangle the id
*/
- public void checkId(Object object, EntityPersister persister, Serializable id,
EntityMode entityMode)
- throws HibernateException {
+ public void checkId(
+ Object object,
+ EntityPersister persister,
+ Serializable id,
+ EntityMode entityMode,
+ SessionFactoryImplementor factory) throws HibernateException {
if ( id != null && id instanceof DelayedPostInsertIdentifier ) {
// this is a situation where the entity id is assigned by a post-insert generator
@@ -77,7 +82,7 @@
if (id==null) {
throw new AssertionFailure("null id in " + persister.getEntityName() +
" entry (don't flush the Session after an exception occurs)");
}
- if ( !persister.getIdentifierType().isEqual(id, oid, entityMode) ) {
+ if ( !persister.getIdentifierType().isEqual( id, oid, entityMode, factory ) ) {
throw new HibernateException(
"identifier of an instance of " +
persister.getEntityName() +
@@ -184,7 +189,7 @@
values = loadedState;
}
else {
- checkId( entity, persister, entry.getId(), entityMode );
+ checkId( entity, persister, entry.getId(), entityMode, session.getFactory() );
// grab its current state
values = persister.getPropertyValues( entity, entityMode );
Added:
core/trunk/core/src/main/java/org/hibernate/id/CompositeNestedGeneratedValueGenerator.java
===================================================================
---
core/trunk/core/src/main/java/org/hibernate/id/CompositeNestedGeneratedValueGenerator.java
(rev 0)
+++
core/trunk/core/src/main/java/org/hibernate/id/CompositeNestedGeneratedValueGenerator.java 2010-01-21
19:37:01 UTC (rev 18601)
@@ -0,0 +1,96 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2010, 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;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionImplementor;
+
+/**
+ * For composite identifiers, defines a number of "nested" generations that
+ * need to happen to "fill" the identifier property(s).
+ * <p/>
+ * This generator is used implicitly for all composite identifier scenarios if an
+ * explicit generator is not in place. So it make sense to discuss the various
+ * potential scenarios:<ul>
+ * <li>
+ * <i>"embedded" composite identifier</i> - this is possible only
in HBM mappings
+ * as {@code <composite-id/>} (notice the lack of both a name and class attribute
+ * declarations). The term {@link org.hibernate.mapping.Component#isEmbedded()
"embedded"}
+ * here refers to the Hibernate usage which is actually the exact opposite of the JPA
+ * meaning of "embedded". Essentially this means that the entity class itself
holds
+ * the named composite pk properties. This is very similar to the JPA {@code @IdClass}
+ * usage, though without a separate pk-class for loading.
+ * </li>
+ * <li>
+ * <i>pk-class as entity attribute</i> - this is possible in both annotations
({@code @EmbeddedId})
+ * and HBM mappings ({@code <composite-id name="idAttributeName"
class="PkClassName"/>})
+ * </li>
+ * <li>
+ * <i>"embedded" composite identifier with a pk-class</i> - this is
the JPA {@code @IdClass} use case
+ * and is only possible in annotations
+ * </li>
+ * </ul>
+ * <p/>
+ * Most of the grunt work is done in {@link org.hibernate.mapping.Component}.
+ *
+ * @author Steve Ebersole
+ */
+public class CompositeNestedGeneratedValueGenerator implements IdentifierGenerator,
Serializable {
+ public static interface GenerationContextLocator {
+ public Serializable locateGenerationContext(SessionImplementor session, Object
incomingObject);
+ }
+
+ public static interface GenerationPlan {
+ public void execute(SessionImplementor session, Object incomingObject);
+ }
+
+ private final GenerationContextLocator generationContextLocator;
+ private List generationPlans = new ArrayList();
+
+ public CompositeNestedGeneratedValueGenerator(GenerationContextLocator
generationContextLocator) {
+ this.generationContextLocator = generationContextLocator;
+ }
+
+ public void addGeneratedValuePlan(GenerationPlan plan) {
+ generationPlans.add( plan );
+ }
+
+ public Serializable generate(SessionImplementor session, Object object) throws
HibernateException {
+ final Serializable context = generationContextLocator.locateGenerationContext( session,
object );
+
+ Iterator itr = generationPlans.iterator();
+ while ( itr.hasNext() ) {
+ final GenerationPlan plan = (GenerationPlan) itr.next();
+ plan.execute( session, context );
+ }
+
+ return context;
+ }
+
+}
Modified: core/trunk/core/src/main/java/org/hibernate/mapping/Component.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/mapping/Component.java 2010-01-21 17:51:09
UTC (rev 18600)
+++ core/trunk/core/src/main/java/org/hibernate/mapping/Component.java 2010-01-21 19:37:01
UTC (rev 18601)
@@ -1,10 +1,10 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
- * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * Copyright (c) 2010, 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,17 +20,30 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
- *
*/
package org.hibernate.mapping;
+import java.io.Serializable;
+import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
import org.hibernate.MappingException;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.id.CompositeNestedGeneratedValueGenerator;
+import org.hibernate.id.IdentifierGenerator;
+import org.hibernate.id.factory.IdentifierGeneratorFactory;
+import org.hibernate.property.Getter;
+import org.hibernate.property.PropertyAccessor;
+import org.hibernate.property.Setter;
import org.hibernate.tuple.component.ComponentMetamodel;
import org.hibernate.type.ComponentType;
import org.hibernate.type.EmbeddedComponentType;
@@ -41,10 +54,11 @@
/**
* The mapping for a component, composite element,
* composite identifier, etc.
+ *
* @author Gavin King
+ * @author Steve Ebersole
*/
public class Component extends SimpleValue implements MetaAttributable {
-
private ArrayList properties = new ArrayList();
private String componentClassName;
private boolean embedded;
@@ -295,4 +309,135 @@
return getClass().getName() + '(' + properties.toString() + ')';
}
+ private IdentifierGenerator builtIdentifierGenerator;
+
+ public IdentifierGenerator createIdentifierGenerator(
+ IdentifierGeneratorFactory identifierGeneratorFactory,
+ Dialect dialect,
+ String defaultCatalog,
+ String defaultSchema,
+ RootClass rootClass) throws MappingException {
+ if ( builtIdentifierGenerator == null ) {
+ builtIdentifierGenerator = buildIdentifierGenerator(
+ identifierGeneratorFactory,
+ dialect,
+ defaultCatalog,
+ defaultSchema,
+ rootClass
+ );
+ }
+ return builtIdentifierGenerator;
+ }
+
+ private IdentifierGenerator buildIdentifierGenerator(
+ IdentifierGeneratorFactory identifierGeneratorFactory,
+ Dialect dialect,
+ String defaultCatalog,
+ String defaultSchema,
+ RootClass rootClass) throws MappingException {
+ final boolean hasCustomGenerator = ! DEFAULT_ID_GEN_STRATEGY.equals(
getIdentifierGeneratorStrategy() );
+ if ( hasCustomGenerator ) {
+ return super.createIdentifierGenerator(
+ identifierGeneratorFactory, dialect, defaultCatalog, defaultSchema, rootClass
+ );
+ }
+
+ final Class entityClass = rootClass.getMappedClass();
+ final Class attributeDeclarer; // what class is the declarer of the composite pk
attributes
+ CompositeNestedGeneratedValueGenerator.GenerationContextLocator locator;
+
+ // IMPL NOTE : See the javadoc discussion on CompositeNestedGeneratedValueGenerator wrt
the
+ // various scenarios for which we need to account here
+ if ( isEmbedded() ) {
+ // we have the "straight up" embedded (again the hibernate term) component
identifier
+ attributeDeclarer = entityClass;
+ }
+ else if ( rootClass.getIdentifierProperty() != null ) {
+ // we have the "@EmbeddedId" / <composite-id name="idName"/>
case
+ attributeDeclarer = resolveComponentClass();
+ }
+ else {
+ // we have the @IdClass / <composite-id mapped="true"/> case
+ attributeDeclarer = resolveComponentClass();
+ }
+
+ locator = new StandardGenerationContextLocator( rootClass.getEntityName() );
+ final CompositeNestedGeneratedValueGenerator generator = new
CompositeNestedGeneratedValueGenerator( locator );
+
+ Iterator itr = getPropertyIterator();
+ while ( itr.hasNext() ) {
+ final Property property = (Property) itr.next();
+ if ( property.getValue().isSimpleValue() ) {
+ final SimpleValue value = (SimpleValue) property.getValue();
+
+ if ( DEFAULT_ID_GEN_STRATEGY.equals( value.getIdentifierGeneratorStrategy() ) ) {
+ // skip any 'assigned' generators, they would have been handled by
+ // the StandardGenerationContextLocator
+ continue;
+ }
+
+ final IdentifierGenerator valueGenerator = value.createIdentifierGenerator(
+ identifierGeneratorFactory,
+ dialect,
+ defaultCatalog,
+ defaultSchema,
+ rootClass
+ );
+ final Setter injector = property.getPropertyAccessor( attributeDeclarer )
+ .getSetter( attributeDeclarer, property.getName() );
+ generator.addGeneratedValuePlan(
+ new ValueGenerationPlan(
+ property.getName(),
+ valueGenerator,
+ injector
+ )
+ );
+ }
+ }
+ return generator;
+ }
+
+ private Class resolveComponentClass() {
+ try {
+ return getComponentClass();
+ }
+ catch ( Exception e ) {
+ return null;
+ }
+ }
+
+ public static class StandardGenerationContextLocator
+ implements CompositeNestedGeneratedValueGenerator.GenerationContextLocator {
+ private final String entityName;
+
+ public StandardGenerationContextLocator(String entityName) {
+ this.entityName = entityName;
+ }
+
+ public Serializable locateGenerationContext(SessionImplementor session, Object
incomingObject) {
+ return session.getEntityPersister( entityName, incomingObject )
+ .getIdentifier( incomingObject, session.getEntityMode() );
+ }
+ }
+
+ public static class ValueGenerationPlan implements
CompositeNestedGeneratedValueGenerator.GenerationPlan {
+ private final String propertyName;
+ private final IdentifierGenerator subGenerator;
+ private final Setter injector;
+
+ public ValueGenerationPlan(
+ String propertyName,
+ IdentifierGenerator subGenerator,
+ Setter injector) {
+ this.propertyName = propertyName;
+ this.subGenerator = subGenerator;
+ this.injector = injector;
+ }
+
+ public void execute(SessionImplementor session, Object incomingObject) {
+ final Object generatedValue = subGenerator.generate( session, incomingObject );
+ injector.set( incomingObject, generatedValue, session.getFactory() );
+ }
+ }
+
}
Modified: core/trunk/core/src/main/java/org/hibernate/mapping/SimpleValue.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/mapping/SimpleValue.java 2010-01-21
17:51:09 UTC (rev 18600)
+++ core/trunk/core/src/main/java/org/hibernate/mapping/SimpleValue.java 2010-01-21
19:37:01 UTC (rev 18601)
@@ -46,11 +46,12 @@
* @author Gavin King
*/
public class SimpleValue implements KeyValue {
+ public static final String DEFAULT_ID_GEN_STRATEGY = "assigned";
private final List columns = new ArrayList();
private String typeName;
private Properties identifierGeneratorProperties;
- private String identifierGeneratorStrategy = "assigned";
+ private String identifierGeneratorStrategy = DEFAULT_ID_GEN_STRATEGY;
private String nullValue;
private Table table;
private String foreignKeyName;
@@ -124,8 +125,7 @@
Dialect dialect,
String defaultCatalog,
String defaultSchema,
- RootClass rootClass)
- throws MappingException {
+ RootClass rootClass) throws MappingException {
Properties params = new Properties();