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:
parent
d3886e2012
commit
341ea5b8f5
|
@ -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<?>[] {
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -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();
|
||||||
|
@ -90,31 +89,57 @@ public class DefaultPersistEventListener extends AbstractSaveEventListener imple
|
||||||
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) {
|
final EntityEntry entityEntry = source.getPersistenceContext().getEntry( entity );
|
||||||
|
int entityState = getEntityState( entity, entityName, entityEntry, source );
|
||||||
|
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:
|
case DETACHED:
|
||||||
throw new PersistentObjectException(
|
throw new PersistentObjectException(
|
||||||
"detached entity passed to persist: " +
|
"detached entity passed to persist: " +
|
||||||
getLoggableName( event.getEntityName(), entity )
|
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 )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue