HHH-4552 - Support generated value within composite keys
git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@18601 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
parent
8577a68e69
commit
79b10cc810
|
@ -38,6 +38,7 @@ import org.hibernate.classic.Validatable;
|
|||
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 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener
|
|||
/**
|
||||
* 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 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener
|
|||
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 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener
|
|||
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 );
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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 @@ import org.hibernate.util.ReflectHelper;
|
|||
/**
|
||||
* 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 @@ public class Component extends SimpleValue implements MetaAttributable {
|
|||
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() );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -46,11 +46,12 @@ import org.hibernate.util.ReflectHelper;
|
|||
* @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 @@ public class SimpleValue implements KeyValue {
|
|||
Dialect dialect,
|
||||
String defaultCatalog,
|
||||
String defaultSchema,
|
||||
RootClass rootClass)
|
||||
throws MappingException {
|
||||
RootClass rootClass) throws MappingException {
|
||||
|
||||
Properties params = new Properties();
|
||||
|
||||
|
|
Loading…
Reference in New Issue