HHH-4552 - Support generated value within composite keys

git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@18640 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Steve Ebersole 2010-01-26 21:49:51 +00:00
parent 091c2d2269
commit 3b536fa6f6
5 changed files with 51 additions and 27 deletions

View File

@ -60,7 +60,6 @@ public class IdClassGeneratedValueTest extends TestCase {
s.close();
}
@FailureExpected(message = "Not yet implemented", jiraKey = "HHH-4552")
@SuppressWarnings({ "unchecked" })
public void testSingleGeneratedValue() {
Session s = openSession();
@ -77,7 +76,7 @@ public class IdClassGeneratedValueTest extends TestCase {
s.beginTransaction();
List<Simple2> simpleList = s.createQuery( "select s from Simple2 s" ).list();
assertEquals( simpleList.size(), 2 );
s1 = ( Simple2 ) s.load( Simple2.class, new SimplePK( s1Id1, 2L ) );
s1 = ( Simple2 ) s.load( Simple2.class, new SimplePK( s1Id1, 200L ) );
assertEquals( s1.getQuantity(), 10 );
s.clear();
s.createQuery( "delete Simple2" ).executeUpdate();
@ -85,7 +84,6 @@ public class IdClassGeneratedValueTest extends TestCase {
s.close();
}
@FailureExpected(message = "Not yet implemented", jiraKey = "HHH-4552")
@SuppressWarnings({ "unchecked" })
public void testMultipleGeneratedValue() {
Session s = openSession();
@ -103,7 +101,7 @@ public class IdClassGeneratedValueTest extends TestCase {
s.beginTransaction();
List<Multiple> simpleList = s.createQuery( "select m from Multiple m" ).list();
assertEquals( simpleList.size(), 2 );
m1 = ( Multiple ) s.load( Multiple.class, new MultiplePK( m1Id1, m1Id2, 2L ) );
m1 = ( Multiple ) s.load( Multiple.class, new MultiplePK( m1Id1, m1Id2, 1000L ) );
assertEquals( m1.getQuantity(), 10 );
s.clear();
s.createQuery( "delete Multiple" ).executeUpdate();

View File

@ -33,26 +33,15 @@ import java.io.Serializable;
public class SimplePK implements Serializable {
private final Long id1;
private final Long id2;
// AnnotationBinder (incorrectly) requires this to be transient; see HHH-4819 and HHH-4820
private final transient int cachedHashCode;
private SimplePK() {
// required by Hibernate, though never used; see HHH-4818
id1 = null;
id2 = null;
cachedHashCode = super.hashCode();
}
public SimplePK(Long id1, Long id2) {
this.id1 = id1;
this.id2 = id2;
this.cachedHashCode = calculateHashCode();
}
private int calculateHashCode() {
int result = id1.hashCode();
result = 31 * result + id2.hashCode();
return result;
}
public Long getId1() {
@ -80,6 +69,8 @@ public class SimplePK implements Serializable {
@Override
public int hashCode() {
return cachedHashCode;
int result = id1.hashCode();
result = 31 * result + id2.hashCode();
return result;
}
}

View File

@ -62,12 +62,35 @@ import org.hibernate.engine.SessionImplementor;
* @author Steve Ebersole
*/
public class CompositeNestedGeneratedValueGenerator implements IdentifierGenerator, Serializable {
/**
* Contract for declaring how to locate the context for sub-value injection.
*/
public static interface GenerationContextLocator {
/**
* Given the incoming object, determine the context for injecting back its generated
* id sub-values.
*
* @param session The current session
* @param incomingObject The entity for which we are generating id
*
* @return The injection context
*/
public Serializable locateGenerationContext(SessionImplementor session, Object incomingObject);
}
/**
* Contract for performing the actual sub-value generation, usually injecting it into the
* determined {@link GenerationContextLocator#locateGenerationContext context}
*/
public static interface GenerationPlan {
public void execute(SessionImplementor session, Object incomingObject, Object objectId);
/**
* Execute the value generation.
*
* @param session The current session
* @param incomingObject The entity for which we are generating id
* @param injectionContext The context into which the generated value can be injected
*/
public void execute(SessionImplementor session, Object incomingObject, Object injectionContext);
}
private final GenerationContextLocator generationContextLocator;

View File

@ -37,6 +37,7 @@ import org.hibernate.EntityMode;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.EntityEntry;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.id.CompositeNestedGeneratedValueGenerator;
import org.hibernate.id.IdentifierGenerator;
@ -348,17 +349,17 @@ public class Component extends SimpleValue implements MetaAttributable {
// 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;
if ( rootClass.getIdentifierMapper() != null ) {
// we have the @IdClass / <composite-id mapped="true"/> case
attributeDeclarer = resolveComponentClass();
}
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();
// we have the "straight up" embedded (again the hibernate term) component identifier
attributeDeclarer = entityClass;
}
locator = new StandardGenerationContextLocator( rootClass.getEntityName() );
@ -383,13 +384,11 @@ public class Component extends SimpleValue implements MetaAttributable {
defaultSchema,
rootClass
);
final Setter injector = property.getPropertyAccessor( attributeDeclarer )
.getSetter( attributeDeclarer, property.getName() );
generator.addGeneratedValuePlan(
new ValueGenerationPlan(
property.getName(),
valueGenerator,
injector
injector( property, attributeDeclarer )
)
);
}
@ -397,6 +396,11 @@ public class Component extends SimpleValue implements MetaAttributable {
return generator;
}
private Setter injector(Property property, Class attributeDeclarer) {
return property.getPropertyAccessor( attributeDeclarer )
.getSetter( attributeDeclarer, property.getName() );
}
private Class resolveComponentClass() {
try {
return getComponentClass();
@ -434,9 +438,12 @@ public class Component extends SimpleValue implements MetaAttributable {
this.injector = injector;
}
public void execute(SessionImplementor session, Object incomingObject, Object idObject) {
/**
* {@inheritDoc}
*/
public void execute(SessionImplementor session, Object incomingObject, Object injectionContext) {
final Object generatedValue = subGenerator.generate( session, incomingObject );
injector.set( idObject, generatedValue, session.getFactory() );
injector.set( injectionContext, generatedValue, session.getFactory() );
}
}

View File

@ -226,6 +226,11 @@ public abstract class AbstractEntityTuplizer implements EntityTuplizer {
else if ( idSetter != null ) {
idSetter.set( entity, id, getFactory() );
}
else if ( identifierMapperType != null ) {
ComponentType extractor = (ComponentType) entityMetamodel.getIdentifierProperty().getType();
ComponentType copier = (ComponentType) identifierMapperType;
copier.setPropertyValues( entity, extractor.getPropertyValues( id, getEntityMode() ), getEntityMode() );
}
}
public void resetIdentifier(Object entity, Serializable currentId, Object currentVersion) {