HHH-4902 - Handle JPA 2 requirement of setting id attribute to non-null with its equivalent of foreign-generator

git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@18767 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Steve Ebersole 2010-02-10 17:38:59 +00:00
parent d3886e2012
commit 341ea5b8f5
4 changed files with 118 additions and 33 deletions

View File

@ -1,6 +1,10 @@
package org.hibernate.test.annotations.derivedidentities.e4.b; package org.hibernate.test.annotations.derivedidentities.e4.b;
import java.util.Date; import java.util.Date;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import org.hibernate.Session; import org.hibernate.Session;
import org.hibernate.test.annotations.TestCase; import org.hibernate.test.annotations.TestCase;
@ -61,6 +65,45 @@ public class DerivedIdentitySimpleParentSimpleDepMapsIdTest extends TestCase {
s.close(); s.close();
} }
public void testExplicitlyAssignedDependentIdAttributeValue() {
// even though the id is by definition generated (using the "foreign" strategy), JPA
// still does allow manually setting the generated id attribute value which providers
// are expected to promptly disregard :?
Session s = openSession();
s.beginTransaction();
Person person = new Person( "123456789" );
MedicalHistory medicalHistory = new MedicalHistory( "987654321", person );
s.persist( person );
s.persist( medicalHistory );
s.getTransaction().commit();
s.close();
// again, even though we specified an id value of "987654321" prior to persist,
// Hibernate should have replaced that with the "123456789" from the associated
// person
assertEquals( person.ssn, medicalHistory.patient.ssn );
assertEquals( person, medicalHistory.patient );
assertEquals( person.ssn, medicalHistory.id );
s = openSession();
s.beginTransaction();
// Should return null...
MedicalHistory separateMedicalHistory = (MedicalHistory) s.get( MedicalHistory.class, "987654321" );
assertNull( separateMedicalHistory );
// Now we should find it...
separateMedicalHistory = (MedicalHistory) s.get( MedicalHistory.class, "123456789" );
assertNotNull( separateMedicalHistory );
s.getTransaction().commit();
s.close();
s = openSession();
s.beginTransaction();
s.delete( medicalHistory );
s.delete( person );
s.getTransaction().commit();
s.close();
}
@Override @Override
protected Class<?>[] getAnnotatedClasses() { protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] { return new Class<?>[] {

View File

@ -1,6 +1,7 @@
package org.hibernate.test.annotations.derivedidentities.e4.b; package org.hibernate.test.annotations.derivedidentities.e4.b;
import java.util.Date; import java.util.Date;
import javax.persistence.CascadeType;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.Id; import javax.persistence.Id;
import javax.persistence.JoinColumn; import javax.persistence.JoinColumn;
@ -16,11 +17,20 @@ import javax.persistence.TemporalType;
public class MedicalHistory { public class MedicalHistory {
@Id String id; // overriding not allowed ... // default join column name is overridden @MapsId @Id String id; // overriding not allowed ... // default join column name is overridden @MapsId
@MapsId
@JoinColumn(name = "FK")
@OneToOne(cascade= CascadeType.ALL)
Person patient;
@Temporal(TemporalType.DATE) @Temporal(TemporalType.DATE)
Date lastupdate; Date lastupdate;
@JoinColumn(name = "FK") public MedicalHistory() {
@MapsId }
@OneToOne
Person patient; public MedicalHistory(String id, Person patient) {
this.id = id;
this.patient = patient;
}
} }

View File

@ -10,4 +10,11 @@ import javax.persistence.Id;
public class Person { public class Person {
@Id @Id
String ssn; String ssn;
public Person() {
}
public Person(String ssn) {
this.ssn = ssn;
}
} }

View File

@ -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,7 +20,6 @@
* 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.event.def; package org.hibernate.event.def;
@ -32,10 +31,12 @@ import org.hibernate.HibernateException;
import org.hibernate.ObjectDeletedException; import org.hibernate.ObjectDeletedException;
import org.hibernate.PersistentObjectException; import org.hibernate.PersistentObjectException;
import org.hibernate.engine.CascadingAction; import org.hibernate.engine.CascadingAction;
import org.hibernate.engine.EntityEntry;
import org.hibernate.event.EventSource; import org.hibernate.event.EventSource;
import org.hibernate.event.PersistEvent; import org.hibernate.event.PersistEvent;
import org.hibernate.event.PersistEventListener; import org.hibernate.event.PersistEventListener;
import org.hibernate.engine.SessionImplementor; import org.hibernate.engine.SessionImplementor;
import org.hibernate.id.ForeignGenerator;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.LazyInitializer; import org.hibernate.proxy.LazyInitializer;
@ -48,7 +49,6 @@ import org.hibernate.util.IdentityMap;
* @author Gavin King * @author Gavin King
*/ */
public class DefaultPersistEventListener extends AbstractSaveEventListener implements PersistEventListener { public class DefaultPersistEventListener extends AbstractSaveEventListener implements PersistEventListener {
private static final Logger log = LoggerFactory.getLogger(DefaultPersistEventListener.class); private static final Logger log = LoggerFactory.getLogger(DefaultPersistEventListener.class);
/** /**
@ -69,19 +69,18 @@ public class DefaultPersistEventListener extends AbstractSaveEventListener imple
* @throws HibernateException * @throws HibernateException
*/ */
public void onPersist(PersistEvent event, Map createCache) throws HibernateException { public void onPersist(PersistEvent event, Map createCache) throws HibernateException {
final SessionImplementor source = event.getSession(); final SessionImplementor source = event.getSession();
final Object object = event.getObject(); final Object object = event.getObject();
final Object entity; final Object entity;
if (object instanceof HibernateProxy) { if ( object instanceof HibernateProxy ) {
LazyInitializer li = ( (HibernateProxy) object ).getHibernateLazyInitializer(); LazyInitializer li = ( (HibernateProxy) object ).getHibernateLazyInitializer();
if ( li.isUninitialized() ) { if ( li.isUninitialized() ) {
if ( li.getSession()==source ) { if ( li.getSession() == source ) {
return; //NOTE EARLY EXIT! return; //NOTE EARLY EXIT!
} }
else { else {
throw new PersistentObjectException("uninitialized proxy passed to persist()"); throw new PersistentObjectException( "uninitialized proxy passed to persist()" );
} }
} }
entity = li.getImplementation(); entity = li.getImplementation();
@ -89,32 +88,58 @@ public class DefaultPersistEventListener extends AbstractSaveEventListener imple
else { else {
entity = object; entity = object;
} }
int entityState = getEntityState( final String entityName;
entity, if ( event.getEntityName() != null ) {
event.getEntityName(), entityName = event.getEntityName();
source.getPersistenceContext().getEntry(entity), }
source else {
); entityName = source.bestGuessEntityName( entity );
event.setEntityName( entityName );
switch (entityState) { }
case DETACHED:
throw new PersistentObjectException( final EntityEntry entityEntry = source.getPersistenceContext().getEntry( entity );
"detached entity passed to persist: " + int entityState = getEntityState( entity, entityName, entityEntry, source );
getLoggableName( event.getEntityName(), entity ) if ( entityState == DETACHED ) {
); // JPA 2, in its version of a "foreign generated", allows the id attribute value
// to be manually set by the user, even though this manual value is irrelevant.
// The issue is that this causes problems with the Hibernate unsaved-value strategy
// which comes into play here in determining detached/transient state.
//
// Detect if we have this situation and if so null out the id value and calculate the
// entity state again.
// NOTE: entityEntry must be null to get here, so we cannot use any of its values
EntityPersister persister = source.getFactory().getEntityPersister( entityName );
if ( ForeignGenerator.class.isInstance( persister.getIdentifierGenerator() ) ) {
if ( log.isDebugEnabled() ) {
if ( persister.getIdentifier( entity, source ) != null ) {
log.debug( "Resetting entity id attribute to null for foreign generator" );
}
}
persister.setIdentifier( entity, null, source );
entityState = getEntityState( entity, entityName, entityEntry, source );
}
}
switch ( entityState ) {
case DETACHED:
throw new PersistentObjectException(
"detached entity passed to persist: " +
getLoggableName( event.getEntityName(), entity )
);
case PERSISTENT: case PERSISTENT:
entityIsPersistent(event, createCache); entityIsPersistent( event, createCache );
break; break;
case TRANSIENT: case TRANSIENT:
entityIsTransient(event, createCache); entityIsTransient( event, createCache );
break; break;
default: default:
throw new ObjectDeletedException( throw new ObjectDeletedException(
"deleted entity passed to persist", "deleted entity passed to persist",
null, null,
getLoggableName( event.getEntityName(), entity ) getLoggableName( event.getEntityName(), entity )
); );
} }
} }