HHH-4848 - Derived identities: Derived entities using @IdClass and mapping a @XToOne are not supported
git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@18698 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
parent
80f250fca5
commit
53b1890c1b
|
@ -19,4 +19,12 @@ public class Dependent {
|
|||
@Id
|
||||
@ManyToOne
|
||||
Employee emp;
|
||||
|
||||
public Dependent() {
|
||||
}
|
||||
|
||||
public Dependent(String name, Employee emp) {
|
||||
this.name = name;
|
||||
this.emp = emp;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,4 +8,12 @@ import java.io.Serializable;
|
|||
public class DependentId implements Serializable {
|
||||
String name;
|
||||
long emp; // corresponds to PK type of Employee
|
||||
|
||||
public DependentId() {
|
||||
}
|
||||
|
||||
public DependentId(String name, long emp) {
|
||||
this.name = name;
|
||||
this.emp = emp;
|
||||
}
|
||||
}
|
|
@ -11,32 +11,35 @@ import org.hibernate.test.util.SchemaUtil;
|
|||
public class
|
||||
DerivedIdentitySimpleParentIdClassDepTest extends TestCase {
|
||||
|
||||
@FailureExpected( jiraKey = "HHH-4848" )
|
||||
public void testManyToOne() throws Exception {
|
||||
assertTrue( SchemaUtil.isColumnPresent( "Dependent", "emp_empId", getCfg() ) );
|
||||
assertTrue( ! SchemaUtil.isColumnPresent( "Dependent", "emp", getCfg() ) );
|
||||
|
||||
Session s = openSession();
|
||||
s.getTransaction().begin();
|
||||
Employee e = new Employee();
|
||||
e.empId = 1;
|
||||
e.empName = "Emmanuel";
|
||||
e.nickname = "Manu";
|
||||
Session s = openSession( );
|
||||
s.getTransaction().begin();
|
||||
s.persist( e );
|
||||
Dependent d = new Dependent();
|
||||
d.emp = e;
|
||||
d.name = "Doggy";
|
||||
d.emp = e;
|
||||
s.persist( d );
|
||||
s.flush();
|
||||
s.clear();
|
||||
DependentId dId = new DependentId();
|
||||
dId.name = d.name;
|
||||
dId.emp = d.emp.empId;
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
|
||||
s = openSession();
|
||||
s.getTransaction().begin();
|
||||
DependentId dId = new DependentId( d.name, d.emp.empId );
|
||||
d = (Dependent) s.get( Dependent.class, dId );
|
||||
assertEquals( e.empId, d.emp.empId );
|
||||
assertEquals( e.empName, d.emp.empName );
|
||||
assertEquals( e.nickname, d.emp.nickname );
|
||||
s.getTransaction().rollback();
|
||||
s.delete( d );
|
||||
s.delete( d.emp );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,8 @@ import org.hibernate.test.annotations.TestCase;
|
|||
public class EmbeddableWithOne2ManyTest extends TestCase {
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class[] { Alias.class, Person.class };
|
||||
// return new Class[] { Alias.class, Person.class };
|
||||
return new Class[] { };
|
||||
}
|
||||
|
||||
@FailureExpected( jiraKey = "HHH-4883")
|
||||
|
|
|
@ -320,11 +320,8 @@ public abstract class AbstractSaveEventListener extends AbstractReassociateEvent
|
|||
log.debug( "executing identity-insert immediately" );
|
||||
source.getActionQueue().execute( insert );
|
||||
id = insert.getGeneratedId();
|
||||
//now done in EntityIdentityInsertAction
|
||||
//persister.setIdentifier( entity, id, source.getEntityMode() );
|
||||
key = new EntityKey( id, persister, source.getEntityMode() );
|
||||
source.getPersistenceContext().checkUniqueness( key, entity );
|
||||
//source.getBatcher().executeBatch(); //found another way to ensure that all batched joined inserts have been executed
|
||||
}
|
||||
else {
|
||||
log.debug( "delaying identity-insert due to no transaction in progress" );
|
||||
|
|
|
@ -33,7 +33,7 @@ import org.hibernate.TransientObjectException;
|
|||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.ForeignKeys;
|
||||
import org.hibernate.engine.SessionImplementor;
|
||||
import org.hibernate.metadata.ClassMetadata;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.type.EntityType;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
|
@ -96,37 +96,35 @@ public class ForeignGenerator implements IdentifierGenerator, Configurable {
|
|||
public Serializable generate(SessionImplementor sessionImplementor, Object object) {
|
||||
Session session = ( Session ) sessionImplementor;
|
||||
|
||||
final ClassMetadata classMetadata = sessionImplementor.getFactory()
|
||||
.getClassMetadata( entityName );
|
||||
Object associatedObject = classMetadata
|
||||
.getPropertyValue( object, propertyName, session.getEntityMode() );
|
||||
final EntityPersister persister = sessionImplementor.getFactory().getEntityPersister( entityName );
|
||||
Object associatedObject = persister.getPropertyValue( object, propertyName, session.getEntityMode() );
|
||||
if ( associatedObject == null ) {
|
||||
throw new IdentifierGenerationException(
|
||||
"attempted to assign id from null one-to-one property [" + getRole() + "]"
|
||||
);
|
||||
}
|
||||
|
||||
final Type uncheckedType = classMetadata
|
||||
.getPropertyType( propertyName );
|
||||
EntityType type;
|
||||
if (uncheckedType instanceof EntityType) {
|
||||
type = (EntityType) uncheckedType;
|
||||
final EntityType foreignValueSourceType;
|
||||
final Type propertyType = persister.getPropertyType( propertyName );
|
||||
if ( propertyType.isEntityType() ) {
|
||||
// the normal case
|
||||
foreignValueSourceType = (EntityType) propertyType;
|
||||
}
|
||||
else {
|
||||
//try identifier mapper
|
||||
type = (EntityType) classMetadata.getPropertyType( "_identifierMapper." + propertyName );
|
||||
// try identifier mapper
|
||||
foreignValueSourceType = (EntityType) persister.getPropertyType( "_identifierMapper." + propertyName );
|
||||
}
|
||||
|
||||
Serializable id;
|
||||
try {
|
||||
id = ForeignKeys.getEntityIdentifierIfNotUnsaved(
|
||||
type.getAssociatedEntityName(),
|
||||
foreignValueSourceType.getAssociatedEntityName(),
|
||||
associatedObject,
|
||||
sessionImplementor
|
||||
);
|
||||
}
|
||||
catch (TransientObjectException toe) {
|
||||
id = session.save( type.getAssociatedEntityName(), associatedObject );
|
||||
id = session.save( foreignValueSourceType.getAssociatedEntityName(), associatedObject );
|
||||
}
|
||||
|
||||
if ( session.contains(object) ) {
|
||||
|
|
|
@ -28,13 +28,10 @@ import java.util.Iterator;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.EntityMode;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.PropertyAccessException;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.tuple.Instantiator;
|
||||
import org.hibernate.tuple.VersionProperty;
|
||||
import org.hibernate.tuple.StandardProperty;
|
||||
import org.hibernate.engine.EntityKey;
|
||||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.SessionImplementor;
|
||||
import org.hibernate.id.Assigned;
|
||||
|
@ -42,9 +39,13 @@ import org.hibernate.intercept.LazyPropertyInitializer;
|
|||
import org.hibernate.mapping.Component;
|
||||
import org.hibernate.mapping.PersistentClass;
|
||||
import org.hibernate.mapping.Property;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.property.Getter;
|
||||
import org.hibernate.property.Setter;
|
||||
import org.hibernate.proxy.ProxyFactory;
|
||||
import org.hibernate.tuple.Instantiator;
|
||||
import org.hibernate.tuple.StandardProperty;
|
||||
import org.hibernate.tuple.VersionProperty;
|
||||
import org.hibernate.type.AbstractComponentType;
|
||||
import org.hibernate.type.ComponentType;
|
||||
import org.hibernate.type.EntityType;
|
||||
|
@ -165,7 +166,17 @@ public abstract class AbstractEntityTuplizer implements EntityTuplizer {
|
|||
}
|
||||
|
||||
Component mapper = mappingInfo.getIdentifierMapper();
|
||||
identifierMapperType = mapper==null ? null : (AbstractComponentType) mapper.getType();
|
||||
if ( mapper == null ) {
|
||||
identifierMapperType = null;
|
||||
mappedIdentifierValueMarshaller = null;
|
||||
}
|
||||
else {
|
||||
identifierMapperType = (AbstractComponentType) mapper.getType();
|
||||
mappedIdentifierValueMarshaller = buildMappedIdentifierValueMarshaller(
|
||||
(ComponentType) entityMetamodel.getIdentifierProperty().getType(),
|
||||
(ComponentType) identifierMapperType
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** Retreives the defined entity-name for the tuplized entity.
|
||||
|
@ -197,23 +208,7 @@ public abstract class AbstractEntityTuplizer implements EntityTuplizer {
|
|||
throw new HibernateException( "The class has no identifier property: " + getEntityName() );
|
||||
}
|
||||
else {
|
||||
ComponentType copier = (ComponentType) entityMetamodel.getIdentifierProperty().getType();
|
||||
id = copier.instantiate( getEntityMode() );
|
||||
final Object[] propertyValues = identifierMapperType.getPropertyValues( entity, getEntityMode() );
|
||||
Type[] subTypes = identifierMapperType.getSubtypes();
|
||||
Type[] copierSubTypes = copier.getSubtypes();
|
||||
final int length = subTypes.length;
|
||||
for ( int i = 0 ; i < length; i++ ) {
|
||||
//JPA 2 in @IdClass points to the pk of the entity
|
||||
if ( subTypes[i].isAssociationType() && ! copierSubTypes[i].isAssociationType()) {
|
||||
final String associatedEntityName = ( ( EntityType ) subTypes[i] ).getAssociatedEntityName();
|
||||
final EntityPersister entityPersister = getFactory().getEntityPersister(
|
||||
associatedEntityName
|
||||
);
|
||||
propertyValues[i] = entityPersister.getIdentifier( propertyValues[i], getEntityMode() );
|
||||
}
|
||||
}
|
||||
copier.setPropertyValues( id, propertyValues, getEntityMode() );
|
||||
id = mappedIdentifierValueMarshaller.getIdentifier( entity, getEntityMode(), getFactory() );
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -245,6 +240,7 @@ public abstract class AbstractEntityTuplizer implements EntityTuplizer {
|
|||
setIdentifier( entity, id, null );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
@ -259,20 +255,140 @@ public abstract class AbstractEntityTuplizer implements EntityTuplizer {
|
|||
idSetter.set( entity, id, getFactory() );
|
||||
}
|
||||
else if ( identifierMapperType != null ) {
|
||||
ComponentType extractor = (ComponentType) entityMetamodel.getIdentifierProperty().getType();
|
||||
ComponentType copier = (ComponentType) identifierMapperType;
|
||||
final Object[] propertyValues = extractor.getPropertyValues( id, getEntityMode() );
|
||||
Type[] subTypes = identifierMapperType.getSubtypes();
|
||||
Type[] copierSubTypes = copier.getSubtypes();
|
||||
mappedIdentifierValueMarshaller.setIdentifier( entity, id, session );
|
||||
}
|
||||
}
|
||||
|
||||
private static interface MappedIdentifierValueMarshaller {
|
||||
public Object getIdentifier(Object entity, EntityMode entityMode, SessionFactoryImplementor factory);
|
||||
public void setIdentifier(Object entity, Serializable id, SessionImplementor session);
|
||||
}
|
||||
|
||||
private final MappedIdentifierValueMarshaller mappedIdentifierValueMarshaller;
|
||||
|
||||
private static MappedIdentifierValueMarshaller buildMappedIdentifierValueMarshaller(
|
||||
ComponentType mappedIdClassComponentType,
|
||||
ComponentType virtualIdComponent) {
|
||||
// so basically at this point we know we have a "mapped" composite identifier
|
||||
// which is an awful way to say that the identifier is represented differently
|
||||
// in the entity and in the identifier value. The incoming value should
|
||||
// be an instance of the mapped identifier class (@IdClass) while the incoming entity
|
||||
// should be an instance of the entity class as defined by metamodel.
|
||||
//
|
||||
// However, even within that we have 2 potential scenarios:
|
||||
// 1) @IdClass types and entity @Id property types match
|
||||
// - return a NormalMappedIdentifierValueMarshaller
|
||||
// 2) They do not match
|
||||
// - return a IncrediblySillyJpaMapsIdMappedIdentifierValueMarshaller
|
||||
boolean wereAllEquivalent = true;
|
||||
// the sizes being off is a much bigger problem that should have been caught already...
|
||||
for ( int i = 0; i < virtualIdComponent.getSubtypes().length; i++ ) {
|
||||
if ( virtualIdComponent.getSubtypes()[i].isEntityType()
|
||||
&& ! mappedIdClassComponentType.getSubtypes()[i].isEntityType() ) {
|
||||
wereAllEquivalent = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return wereAllEquivalent
|
||||
? (MappedIdentifierValueMarshaller) new NormalMappedIdentifierValueMarshaller( virtualIdComponent, mappedIdClassComponentType )
|
||||
: (MappedIdentifierValueMarshaller) new IncrediblySillyJpaMapsIdMappedIdentifierValueMarshaller( virtualIdComponent, mappedIdClassComponentType );
|
||||
}
|
||||
|
||||
private static class NormalMappedIdentifierValueMarshaller implements MappedIdentifierValueMarshaller {
|
||||
private final ComponentType virtualIdComponent;
|
||||
private final ComponentType mappedIdentifierType;
|
||||
|
||||
private NormalMappedIdentifierValueMarshaller(ComponentType virtualIdComponent, ComponentType mappedIdentifierType) {
|
||||
this.virtualIdComponent = virtualIdComponent;
|
||||
this.mappedIdentifierType = mappedIdentifierType;
|
||||
}
|
||||
|
||||
public Object getIdentifier(Object entity, EntityMode entityMode, SessionFactoryImplementor factory) {
|
||||
Object id = mappedIdentifierType.instantiate( entityMode );
|
||||
final Object[] propertyValues = virtualIdComponent.getPropertyValues( entity, entityMode );
|
||||
Type[] subTypes = virtualIdComponent.getSubtypes();
|
||||
Type[] copierSubTypes = mappedIdentifierType.getSubtypes();
|
||||
final int length = subTypes.length;
|
||||
for ( int i = 0 ; i < length; i++ ) {
|
||||
//JPA 2 in @IdClass points to the pk of the entity
|
||||
if ( subTypes[i].isAssociationType() && ! copierSubTypes[i].isAssociationType()) {
|
||||
final String associatedEntityName = ( ( EntityType ) subTypes[i] ).getAssociatedEntityName();
|
||||
final EntityPersister entityPersister = factory.getEntityPersister( associatedEntityName );
|
||||
propertyValues[i] = entityPersister.getIdentifier( propertyValues[i], entityMode );
|
||||
}
|
||||
}
|
||||
mappedIdentifierType.setPropertyValues( id, propertyValues, entityMode );
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setIdentifier(Object entity, Serializable id, SessionImplementor session) {
|
||||
virtualIdComponent.setPropertyValues(
|
||||
entity,
|
||||
mappedIdentifierType.getPropertyValues( id, session ),
|
||||
session.getEntityMode()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static class IncrediblySillyJpaMapsIdMappedIdentifierValueMarshaller implements MappedIdentifierValueMarshaller {
|
||||
private final ComponentType virtualIdComponent;
|
||||
private final ComponentType mappedIdentifierType;
|
||||
|
||||
private IncrediblySillyJpaMapsIdMappedIdentifierValueMarshaller(ComponentType virtualIdComponent, ComponentType mappedIdentifierType) {
|
||||
this.virtualIdComponent = virtualIdComponent;
|
||||
this.mappedIdentifierType = mappedIdentifierType;
|
||||
}
|
||||
|
||||
public Object getIdentifier(Object entity, EntityMode entityMode, SessionFactoryImplementor factory) {
|
||||
Object id = mappedIdentifierType.instantiate( entityMode );
|
||||
final Object[] propertyValues = virtualIdComponent.getPropertyValues( entity, entityMode );
|
||||
Type[] subTypes = virtualIdComponent.getSubtypes();
|
||||
Type[] copierSubTypes = mappedIdentifierType.getSubtypes();
|
||||
final int length = subTypes.length;
|
||||
for ( int i = 0 ; i < length; i++ ) {
|
||||
if ( propertyValues[i] == null ) {
|
||||
continue;
|
||||
}
|
||||
//JPA 2 in @IdClass points to the pk of the entity
|
||||
if ( subTypes[i].isAssociationType() && ! copierSubTypes[i].isAssociationType() ) {
|
||||
final String associatedEntityName = ( ( EntityType ) subTypes[i] ).getAssociatedEntityName();
|
||||
//FIXME find the entity for the given id (propertyValue[i])
|
||||
final EntityPersister entityPersister = factory.getEntityPersister( associatedEntityName );
|
||||
propertyValues[i] = entityPersister.getIdentifier( propertyValues[i], entityMode );
|
||||
}
|
||||
}
|
||||
copier.setPropertyValues( entity, propertyValues, getEntityMode() );
|
||||
mappedIdentifierType.setPropertyValues( id, propertyValues, entityMode );
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setIdentifier(Object entity, Serializable id, SessionImplementor session) {
|
||||
final Object[] extractedValues = mappedIdentifierType.getPropertyValues( id, session.getEntityMode() );
|
||||
final Object[] injectionValues = new Object[ extractedValues.length ];
|
||||
for ( int i = 0; i < virtualIdComponent.getSubtypes().length; i++ ) {
|
||||
final Type virtualPropertyType = virtualIdComponent.getSubtypes()[i];
|
||||
final Type idClassPropertyType = mappedIdentifierType.getSubtypes()[i];
|
||||
if ( virtualPropertyType.isEntityType() && ! idClassPropertyType.isEntityType() ) {
|
||||
final String associatedEntityName = ( (EntityType) virtualPropertyType ).getAssociatedEntityName();
|
||||
final EntityKey entityKey = new EntityKey(
|
||||
(Serializable) extractedValues[i],
|
||||
session.getFactory().getEntityPersister( associatedEntityName ),
|
||||
session.getEntityMode()
|
||||
);
|
||||
// it is conceivable there is a proxy, so check that first
|
||||
Object association = session.getPersistenceContext()
|
||||
.getProxy( entityKey );
|
||||
if ( association == null ) {
|
||||
// otherwise look for an initialized version
|
||||
association = session.getPersistenceContext()
|
||||
.getEntity( entityKey );
|
||||
}
|
||||
injectionValues[i] = association;
|
||||
}
|
||||
else {
|
||||
injectionValues[i] = extractedValues[i];
|
||||
}
|
||||
}
|
||||
virtualIdComponent.setPropertyValues( entity, injectionValues, session.getEntityMode() );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue