Skip to content

Commit

Permalink
HHH-11274 - EntityManagerFactoryImpl.getIdentifier uses deprecated ve…
Browse files Browse the repository at this point in the history
…rsion of getIdentifier
  • Loading branch information
gbadner committed May 10, 2017
1 parent 3da42d0 commit 7c30aec
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 53 deletions.
Expand Up @@ -7,11 +7,13 @@
package org.hibernate.tuple.entity;

import java.io.Serializable;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.hibernate.EntityMode;
import org.hibernate.EntityNameResolver;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
Expand Down Expand Up @@ -176,6 +178,7 @@ public AbstractEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass m
else {
identifierMapperType = (CompositeType) mapper.getType();
mappedIdentifierValueMarshaller = buildMappedIdentifierValueMarshaller(
getFactory(),
(ComponentType) entityMetamodel.getIdentifierProperty().getType(),
(ComponentType) identifierMapperType
);
Expand Down Expand Up @@ -276,6 +279,7 @@ private static interface MappedIdentifierValueMarshaller {
private final MappedIdentifierValueMarshaller mappedIdentifierValueMarshaller;

private static MappedIdentifierValueMarshaller buildMappedIdentifierValueMarshaller(
SessionFactoryImplementor sessionFactory,
ComponentType mappedIdClassComponentType,
ComponentType virtualIdComponent) {
// so basically at this point we know we have a "mapped" composite identifier
Expand All @@ -302,8 +306,9 @@ private static MappedIdentifierValueMarshaller buildMappedIdentifierValueMarshal
return wereAllEquivalent
? new NormalMappedIdentifierValueMarshaller( virtualIdComponent, mappedIdClassComponentType )
: new IncrediblySillyJpaMapsIdMappedIdentifierValueMarshaller(
virtualIdComponent,
mappedIdClassComponentType
sessionFactory,
virtualIdComponent,
mappedIdClassComponentType
);
}

Expand Down Expand Up @@ -338,12 +343,15 @@ public void setIdentifier(Object entity, Serializable id, EntityMode entityMode,

private static class IncrediblySillyJpaMapsIdMappedIdentifierValueMarshaller
implements MappedIdentifierValueMarshaller {
private final SessionFactoryImplementor sessionFactory;
private final ComponentType virtualIdComponent;
private final ComponentType mappedIdentifierType;

private IncrediblySillyJpaMapsIdMappedIdentifierValueMarshaller(
SessionFactoryImplementor sessionFactory,
ComponentType virtualIdComponent,
ComponentType mappedIdentifierType) {
this.sessionFactory = sessionFactory;
this.virtualIdComponent = virtualIdComponent;
this.mappedIdentifierType = mappedIdentifierType;
}
Expand All @@ -354,25 +362,18 @@ public Object getIdentifier(Object entity, EntityMode entityMode, SessionImpleme
final Object[] propertyValues = virtualIdComponent.getPropertyValues( entity, entityMode );
final Type[] subTypes = virtualIdComponent.getSubtypes();
final Type[] copierSubTypes = mappedIdentifierType.getSubtypes();
final Iterable<PersistEventListener> persistEventListeners = persistEventListeners( session );
final int length = subTypes.length;
for ( int i = 0; i < length; i++ ) {
if ( propertyValues[i] == null ) {
throw new HibernateException( "No part of a composite identifier may be null" );
}
//JPA 2 @MapsId + @IdClass points to the pk of the entity
if ( subTypes[i].isAssociationType() && !copierSubTypes[i].isAssociationType() ) {
// we need a session to handle this use case
if ( session == null ) {
throw new AssertionError(
"Deprecated version of getIdentifier (no session) was used but session was required"
);
}
if ( subTypes[i].isAssociationType() && !copierSubTypes[i].isAssociationType() ) {
propertyValues[i] = determineEntityIdPersistIfNecessary(
propertyValues[i],
(AssociationType) subTypes[i],
session,
persistEventListeners
sessionFactory
);
}
}
Expand All @@ -397,7 +398,7 @@ public void setIdentifier(Object entity, Serializable id, EntityMode entityMode,
final String associatedEntityName = ( (EntityType) virtualPropertyType ).getAssociatedEntityName();
final EntityKey entityKey = session.generateEntityKey(
(Serializable) extractedValues[i],
session.getFactory().getEntityPersister( associatedEntityName )
sessionFactory.getEntityPersister( associatedEntityName )
);
// it is conceivable there is a proxy, so check that first
Object association = persistenceContext.getProxy( entityKey );
Expand All @@ -416,6 +417,9 @@ public void setIdentifier(Object entity, Serializable id, EntityMode entityMode,
}

private static Iterable<PersistEventListener> persistEventListeners(SessionImplementor session) {
if ( session == null ) {
return Collections.emptyList();
}
return session
.getFactory()
.getServiceRegistry()
Expand All @@ -428,62 +432,107 @@ private static Serializable determineEntityIdPersistIfNecessary(
Object entity,
AssociationType associationType,
SessionImplementor session,
Iterable<PersistEventListener> persistEventListeners) {
SessionFactoryImplementor sessionFactory) {
if ( entity == null ) {
return null;
}

// NOTE : persist if necessary for proper merge support (HHH-11328)
// but only allow persist if a Session is passed (HHH-11274)

if ( HibernateProxy.class.isInstance( entity ) ) {
// entity is a proxy, so we know it is not transient; just return ID from proxy
return ( (HibernateProxy) entity ).getHibernateLazyInitializer().getIdentifier();
}
else {
EntityEntry pcEntry = session.getPersistenceContext().getEntry( entity );
if ( session != null ) {
final EntityEntry pcEntry = session.getPersistenceContext().getEntry( entity );
if ( pcEntry != null ) {
// entity managed; return ID.
return pcEntry.getId();
}
else {
final EntityPersister persister = session.getEntityPersister(
associationType.getAssociatedEntityName( session.getFactory() ),
entity
);
Serializable entityId = persister.getIdentifier( entity, session );
if ( entityId == null ) {
// entity is transient with no ID; we need to persist the entity to get the ID.
entityId = persistTransientEntity( entity, session, persistEventListeners );
}
else {
// entity has an ID.
final EntityKey entityKey = session.generateEntityKey( entityId, persister );
// if the entity is in the process of being merged, it may be stored in the
// PC already, but doesn't have an EntityEntry yet. If this is the case,
// then don't persist even if it is transient because doing so can lead
// to having 2 entities in the PC with the same ID (HHH-11328).
if ( session.getPersistenceContext().getEntity( entityKey ) == null &&
ForeignKeys.isTransient(
persister.getEntityName(),
entity,
null,
session
) ) {
// entity is transient and it is not in the PersistenceContext.
// entity needs to be persisted.
persistTransientEntity( entity, session, persistEventListeners );
}
}

final EntityPersister persister = resolveEntityPersister(
entity,
associationType,
session,
sessionFactory
);

Serializable entityId = persister.getIdentifier( entity, session );

if ( entityId == null ) {
if ( session != null ) {
// if we have a session, then follow the HHH-11328 requirements
entityId = persistTransientEntity( entity, session );
}
// otherwise just let it be null HHH-11274
}
else {
if ( session != null ) {
// if the entity is in the process of being merged, it may be stored in the
// PC already, but doesn't have an EntityEntry yet. If this is the case,
// then don't persist even if it is transient because doing so can lead
// to having 2 entities in the PC with the same ID (HHH-11328).
final EntityKey entityKey = session.generateEntityKey( entityId, persister );
if ( session.getPersistenceContext().getEntity( entityKey ) == null &&
ForeignKeys.isTransient(
persister.getEntityName(),
entity,
null,
session
) ) {
// entity is transient and it is not in the PersistenceContext.
// entity needs to be persisted.
persistTransientEntity( entity, session );
}
return entityId;
}
}
return entityId;
}

private static Serializable persistTransientEntity(
private static EntityPersister resolveEntityPersister(
Object entity,
AssociationType associationType,
SessionImplementor session,
Iterable<PersistEventListener> persistEventListeners) {
SessionFactoryImplementor sessionFactory) {
assert sessionFactory != null;

if ( session != null ) {
return session.getEntityPersister(
associationType.getAssociatedEntityName( session.getFactory() ),
entity
);
}

String entityName = null;
for ( EntityNameResolver entityNameResolver : sessionFactory.iterateEntityNameResolvers() ) {
entityName = entityNameResolver.resolveEntityName( entity );
if ( entityName != null ) {
break;
}
}
if ( entityName == null ) {
// old fall-back
entityName = entity.getClass().getName();
}

return sessionFactory.getEntityPersister( entityName );
}

private static Serializable persistTransientEntity(
Object entity,
SessionImplementor session) {
assert session != null;

LOG.debug( "Performing implicit derived identity cascade" );
final PersistEvent event = new PersistEvent(
null,
entity,
(EventSource) session
);
for ( PersistEventListener listener : persistEventListeners ) {

for ( PersistEventListener listener : persistEventListeners( session ) ) {
listener.onPersist( event );
}
final EntityEntry pcEntry = session.getPersistenceContext().getEntry( entity );
Expand Down
Expand Up @@ -42,6 +42,7 @@
import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cfg.annotations.NamedEntityGraphDefinition;
import org.hibernate.ejb.HibernateEntityManagerFactory;
import org.hibernate.engine.spi.ManagedEntity;
import org.hibernate.engine.spi.NamedQueryDefinition;
import org.hibernate.engine.spi.NamedQueryDefinitionBuilder;
import org.hibernate.engine.spi.NamedSQLQueryDefinition;
Expand All @@ -64,7 +65,9 @@
import org.hibernate.jpa.internal.metamodel.MetamodelImpl;
import org.hibernate.jpa.internal.util.PersistenceUtilHelper;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.procedure.ProcedureCall;
import org.hibernate.proxy.HibernateProxy;

import org.jboss.logging.Logger;

Expand Down Expand Up @@ -664,13 +667,32 @@ public boolean isLoaded(Object entity) {

@Override
public Object getIdentifier(Object entity) {
final Class entityClass = Hibernate.getClass( entity );
final ClassMetadata classMetadata = emf.getSessionFactory().getClassMetadata( entityClass );
if ( classMetadata == null ) {
throw new IllegalArgumentException( entityClass + " is not an entity" );
if ( entity == null ) {
throw new IllegalArgumentException( "Passed entity cannot be null" );
}

if ( entity instanceof HibernateProxy ) {
final HibernateProxy proxy = (HibernateProxy) entity;
return proxy.getHibernateLazyInitializer().getIdentifier();
}
else if ( entity instanceof ManagedEntity ) {
final ManagedEntity enhancedEntity = (ManagedEntity) entity;
return enhancedEntity.$$_hibernate_getEntityEntry().getId();
}
else {
log.debugf(
"javax.persistence.PersistenceUnitUtil.getIdentifier is only intended to work with enhanced entities " +
"(although Hibernate also adapts this support to its proxies); " +
"however the passed entity was not enhanced (nor a proxy).. may not be able to read identifier"
);
final Class entityClass = Hibernate.getClass( entity );
final EntityPersister persister = emf.getSessionFactory().getEntityPersister( entityClass.getName() );
if ( persister == null ) {
throw new IllegalArgumentException( entityClass + " is not an entity" );
}
//TODO does that work for @IdClass?
return persister.getIdentifier( entity );
}
//TODO does that work for @IdClass?
return classMetadata.getIdentifier( entity );
}
}
}

0 comments on commit 7c30aec

Please sign in to comment.