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.EntityEntry;
|
||||||
import org.hibernate.engine.EntityKey;
|
import org.hibernate.engine.EntityKey;
|
||||||
import org.hibernate.engine.Nullability;
|
import org.hibernate.engine.Nullability;
|
||||||
|
import org.hibernate.engine.SessionFactoryImplementor;
|
||||||
import org.hibernate.engine.SessionImplementor;
|
import org.hibernate.engine.SessionImplementor;
|
||||||
import org.hibernate.engine.Status;
|
import org.hibernate.engine.Status;
|
||||||
import org.hibernate.engine.Versioning;
|
import org.hibernate.engine.Versioning;
|
||||||
|
@ -62,8 +63,12 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener
|
||||||
/**
|
/**
|
||||||
* make sure user didn't mangle the id
|
* make sure user didn't mangle the id
|
||||||
*/
|
*/
|
||||||
public void checkId(Object object, EntityPersister persister, Serializable id, EntityMode entityMode)
|
public void checkId(
|
||||||
throws HibernateException {
|
Object object,
|
||||||
|
EntityPersister persister,
|
||||||
|
Serializable id,
|
||||||
|
EntityMode entityMode,
|
||||||
|
SessionFactoryImplementor factory) throws HibernateException {
|
||||||
|
|
||||||
if ( id != null && id instanceof DelayedPostInsertIdentifier ) {
|
if ( id != null && id instanceof DelayedPostInsertIdentifier ) {
|
||||||
// this is a situation where the entity id is assigned by a post-insert generator
|
// 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) {
|
if (id==null) {
|
||||||
throw new AssertionFailure("null id in " + persister.getEntityName() + " entry (don't flush the Session after an exception occurs)");
|
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(
|
throw new HibernateException(
|
||||||
"identifier of an instance of " +
|
"identifier of an instance of " +
|
||||||
persister.getEntityName() +
|
persister.getEntityName() +
|
||||||
|
@ -184,7 +189,7 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener
|
||||||
values = loadedState;
|
values = loadedState;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
checkId( entity, persister, entry.getId(), entityMode );
|
checkId( entity, persister, entry.getId(), entityMode, session.getFactory() );
|
||||||
|
|
||||||
// grab its current state
|
// grab its current state
|
||||||
values = persister.getPropertyValues( entity, entityMode );
|
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
|
* 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
|
* indicated by the @author tags or express copyright attribution
|
||||||
* statements applied by the authors. All third-party contributions are
|
* 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,
|
* 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
|
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||||
|
@ -20,17 +20,30 @@
|
||||||
* Free Software Foundation, Inc.
|
* Free Software Foundation, Inc.
|
||||||
* 51 Franklin Street, Fifth Floor
|
* 51 Franklin Street, Fifth Floor
|
||||||
* Boston, MA 02110-1301 USA
|
* Boston, MA 02110-1301 USA
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
package org.hibernate.mapping;
|
package org.hibernate.mapping;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import org.hibernate.EntityMode;
|
import org.hibernate.EntityMode;
|
||||||
|
import org.hibernate.HibernateException;
|
||||||
import org.hibernate.MappingException;
|
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.tuple.component.ComponentMetamodel;
|
||||||
import org.hibernate.type.ComponentType;
|
import org.hibernate.type.ComponentType;
|
||||||
import org.hibernate.type.EmbeddedComponentType;
|
import org.hibernate.type.EmbeddedComponentType;
|
||||||
|
@ -41,10 +54,11 @@ import org.hibernate.util.ReflectHelper;
|
||||||
/**
|
/**
|
||||||
* The mapping for a component, composite element,
|
* The mapping for a component, composite element,
|
||||||
* composite identifier, etc.
|
* composite identifier, etc.
|
||||||
|
*
|
||||||
* @author Gavin King
|
* @author Gavin King
|
||||||
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class Component extends SimpleValue implements MetaAttributable {
|
public class Component extends SimpleValue implements MetaAttributable {
|
||||||
|
|
||||||
private ArrayList properties = new ArrayList();
|
private ArrayList properties = new ArrayList();
|
||||||
private String componentClassName;
|
private String componentClassName;
|
||||||
private boolean embedded;
|
private boolean embedded;
|
||||||
|
@ -295,4 +309,135 @@ public class Component extends SimpleValue implements MetaAttributable {
|
||||||
return getClass().getName() + '(' + properties.toString() + ')';
|
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
|
* @author Gavin King
|
||||||
*/
|
*/
|
||||||
public class SimpleValue implements KeyValue {
|
public class SimpleValue implements KeyValue {
|
||||||
|
public static final String DEFAULT_ID_GEN_STRATEGY = "assigned";
|
||||||
|
|
||||||
private final List columns = new ArrayList();
|
private final List columns = new ArrayList();
|
||||||
private String typeName;
|
private String typeName;
|
||||||
private Properties identifierGeneratorProperties;
|
private Properties identifierGeneratorProperties;
|
||||||
private String identifierGeneratorStrategy = "assigned";
|
private String identifierGeneratorStrategy = DEFAULT_ID_GEN_STRATEGY;
|
||||||
private String nullValue;
|
private String nullValue;
|
||||||
private Table table;
|
private Table table;
|
||||||
private String foreignKeyName;
|
private String foreignKeyName;
|
||||||
|
@ -124,8 +125,7 @@ public class SimpleValue implements KeyValue {
|
||||||
Dialect dialect,
|
Dialect dialect,
|
||||||
String defaultCatalog,
|
String defaultCatalog,
|
||||||
String defaultSchema,
|
String defaultSchema,
|
||||||
RootClass rootClass)
|
RootClass rootClass) throws MappingException {
|
||||||
throws MappingException {
|
|
||||||
|
|
||||||
Properties params = new Properties();
|
Properties params = new Properties();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue