package org.hibernate.tuple.entity;
import java.io.Serializable;
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;
import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.Assigned;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.loader.PropertyPath;
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.access.spi.Getter;
import org.hibernate.property.access.spi.Setter;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.ProxyFactory;
import org.hibernate.tuple.Instantiator;
import org.hibernate.tuple.NonIdentifierAttribute;
import org.hibernate.type.AssociationType;
import org.hibernate.type.ComponentType;
import org.hibernate.type.CompositeType;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;
import static org.hibernate.internal.CoreLogging.messageLogger;
public abstract class AbstractEntityTuplizer implements EntityTuplizer {
private static final CoreMessageLogger LOG = messageLogger( AbstractEntityTuplizer.class );
private final EntityMetamodel entityMetamodel;
private final Getter idGetter;
private final Setter idSetter;
protected final Getter[] getters;
protected final Setter[] setters;
protected final int propertySpan;
protected final boolean hasCustomAccessors;
private final Instantiator instantiator;
private final ProxyFactory proxyFactory;
private final CompositeType identifierMapperType;
public Type getIdentifierMapperType() {
return identifierMapperType;
}
protected abstract Getter buildPropertyGetter(Property mappedProperty, PersistentClass mappedEntity);
protected abstract Setter buildPropertySetter(Property mappedProperty, PersistentClass mappedEntity);
protected abstract Instantiator buildInstantiator(EntityMetamodel entityMetamodel, PersistentClass mappingInfo);
protected abstract ProxyFactory buildProxyFactory(PersistentClass mappingInfo, Getter idGetter, Setter idSetter);
public AbstractEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappingInfo) {
this.entityMetamodel = entityMetamodel;
if ( !entityMetamodel.getIdentifierProperty().isVirtual() ) {
idGetter = buildPropertyGetter( mappingInfo.getIdentifierProperty(), mappingInfo );
idSetter = buildPropertySetter( mappingInfo.getIdentifierProperty(), mappingInfo );
}
else {
idGetter = null;
idSetter = null;
}
propertySpan = entityMetamodel.getPropertySpan();
getters = new Getter[propertySpan];
setters = new Setter[propertySpan];
Iterator itr = mappingInfo.getPropertyClosureIterator();
boolean foundCustomAccessor = false;
int i = 0;
while ( itr.hasNext() ) {
Property property = (Property) itr.next();
getters[i] = buildPropertyGetter( property, mappingInfo );
setters[i] = buildPropertySetter( property, mappingInfo );
if ( !property.isBasicPropertyAccessor() ) {
foundCustomAccessor = true;
}
i++;
}
hasCustomAccessors = foundCustomAccessor;
instantiator = buildInstantiator( entityMetamodel, mappingInfo );
if ( entityMetamodel.isLazy() && !entityMetamodel.getBytecodeEnhancementMetadata().isEnhancedForLazyLoading() ) {
proxyFactory = buildProxyFactory( mappingInfo, idGetter, idSetter );
if ( proxyFactory == null ) {
entityMetamodel.setLazy( false );
}
}
else {
proxyFactory = null;
}
Component mapper = mappingInfo.getIdentifierMapper();
if ( mapper == null ) {
identifierMapperType = null;
mappedIdentifierValueMarshaller = null;
}
else {
identifierMapperType = (CompositeType) mapper.getType();
mappedIdentifierValueMarshaller = buildMappedIdentifierValueMarshaller(
getEntityName(),
getFactory(),
(ComponentType) entityMetamodel.getIdentifierProperty().getType(),
(ComponentType) identifierMapperType
);
}
}
protected String getEntityName() {
return entityMetamodel.getName();
}
protected Set getSubclassEntityNames() {
return entityMetamodel.getSubclassEntityNames();
}
@Override
public Serializable getIdentifier(Object entity) throws HibernateException {
return getIdentifier( entity, null );
}
@Override
public Serializable getIdentifier(Object entity, SharedSessionContractImplementor session) {
final Object id;
if ( entityMetamodel.getIdentifierProperty().isEmbedded() ) {
id = entity;
}
else if ( HibernateProxy.class.isInstance( entity ) ) {
id = ( (HibernateProxy) entity ).getHibernateLazyInitializer().getIdentifier();
}
else {
if ( idGetter == null ) {
if ( identifierMapperType == null ) {
throw new HibernateException( "The class has no identifier property: " + getEntityName() );
}
else {
id = mappedIdentifierValueMarshaller.getIdentifier( entity, getEntityMode(), session );
}
}
else {
id = idGetter.get( entity );
}
}
try {
return (Serializable) id;
}
catch (ClassCastException cce) {
StringBuilder msg = new StringBuilder( "Identifier classes must be serializable. " );
if ( id != null ) {
msg.append( id.getClass().getName() ).append( " is not serializable. " );
}
if ( cce.getMessage() != null ) {
msg.append( cce.getMessage() );
}
throw new ClassCastException( msg.toString() );
}
}
@Override
public void setIdentifier(Object entity, Serializable id) throws HibernateException {
setIdentifier( entity, id, null );
}
@Override
public void setIdentifier(Object entity, Serializable id, SharedSessionContractImplementor session) {
if ( entityMetamodel.getIdentifierProperty().isEmbedded() ) {
if ( entity != id ) {
CompositeType copier = (CompositeType) entityMetamodel.getIdentifierProperty().getType();
copier.setPropertyValues( entity, copier.getPropertyValues( id, getEntityMode() ), getEntityMode() );
}
}
else if ( idSetter != null ) {
idSetter.set( entity, id, getFactory() );
}
else if ( identifierMapperType != null ) {
mappedIdentifierValueMarshaller.setIdentifier( entity, id, getEntityMode(), session );
}
}
private static interface MappedIdentifierValueMarshaller {
public Object getIdentifier(Object entity, EntityMode entityMode, SharedSessionContractImplementor session);
public void setIdentifier(Object entity, Serializable id, EntityMode entityMode, SharedSessionContractImplementor session);
}
private final MappedIdentifierValueMarshaller mappedIdentifierValueMarshaller;
private static MappedIdentifierValueMarshaller buildMappedIdentifierValueMarshaller(
String entityName,
SessionFactoryImplementor sessionFactory,
ComponentType mappedIdClassComponentType,
ComponentType virtualIdComponent) {
boolean wereAllEquivalent = true;
for ( int i = 0; i < virtualIdComponent.getSubtypes().length; i++ ) {
if ( virtualIdComponent.getSubtypes()[i].isEntityType()
&& !mappedIdClassComponentType.getSubtypes()[i].isEntityType() ) {
wereAllEquivalent = false;
break;
}
}
return wereAllEquivalent ?
new NormalMappedIdentifierValueMarshaller( virtualIdComponent, mappedIdClassComponentType ) :
new IncrediblySillyJpaMapsIdMappedIdentifierValueMarshaller(
entityName,
sessionFactory,
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;
}
@Override
public Object getIdentifier(Object entity, EntityMode entityMode, SharedSessionContractImplementor session) {
Object id = mappedIdentifierType.instantiate( entityMode );
final Object[] propertyValues = virtualIdComponent.getPropertyValues( entity, entityMode );
mappedIdentifierType.setPropertyValues( id, propertyValues, entityMode );
return id;
}
@Override
public void setIdentifier(Object entity, Serializable id, EntityMode entityMode, SharedSessionContractImplementor session) {
virtualIdComponent.setPropertyValues(
entity,
mappedIdentifierType.getPropertyValues( id, session ),
entityMode
);
}
}
private static class IncrediblySillyJpaMapsIdMappedIdentifierValueMarshaller
implements MappedIdentifierValueMarshaller {
private final String entityName;
private final SessionFactoryImplementor sessionFactory;
private final ComponentType virtualIdComponent;
private final ComponentType mappedIdentifierType;
private IncrediblySillyJpaMapsIdMappedIdentifierValueMarshaller(
String entityName,
SessionFactoryImplementor sessionFactory,
ComponentType virtualIdComponent,
ComponentType mappedIdentifierType) {
this.sessionFactory = sessionFactory;
this.entityName = entityName;
this.virtualIdComponent = virtualIdComponent;
this.mappedIdentifierType = mappedIdentifierType;
}
@Override
public Object getIdentifier(Object entity, EntityMode entityMode, SharedSessionContractImplementor session) {
final Object id = mappedIdentifierType.instantiate( entityMode );
final Object[] propertyValues = virtualIdComponent.getPropertyValues( entity, entityMode );
final Type[] subTypes = virtualIdComponent.getSubtypes();
final Type[] copierSubTypes = mappedIdentifierType.getSubtypes();
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" );
}
if ( subTypes[i].isAssociationType() && !copierSubTypes[i].isAssociationType() ) {
propertyValues[i] = determineEntityId(
propertyValues[i],
(AssociationType) subTypes[i],
session,
sessionFactory
);
}
}
mappedIdentifierType.setPropertyValues( id, propertyValues, entityMode );
return id;
}
@Override
public void setIdentifier(Object entity, Serializable id, EntityMode entityMode, SharedSessionContractImplementor session) {
final Object[] extractedValues = mappedIdentifierType.getPropertyValues( id, entityMode );
final Object[] injectionValues = new Object[extractedValues.length];
final PersistenceContext persistenceContext = session.getPersistenceContext();
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() ) {
if ( session == null ) {
throw new AssertionError(
"Deprecated version of getIdentifier (no session) was used but session was required"
);
}
final String associatedEntityName = ( (EntityType) virtualPropertyType ).getAssociatedEntityName();
final EntityKey entityKey = session.generateEntityKey(
(Serializable) extractedValues[i],
sessionFactory.getMetamodel().entityPersister( associatedEntityName )
);
Object association = persistenceContext.getProxy( entityKey );
if ( association == null ) {
association = persistenceContext.getEntity( entityKey );
if ( association == null ) {
association = sessionFactory.getMetamodel().entityPersister( entityName ).getPropertyValue(
entity,
virtualIdComponent.getPropertyNames()[i]
);
}
}
injectionValues[i] = association;
}
else {
injectionValues[i] = extractedValues[i];
}
}
virtualIdComponent.setPropertyValues( entity, injectionValues, entityMode );
}
}
private static Serializable determineEntityId(
Object entity,
AssociationType associationType,
SharedSessionContractImplementor session,
SessionFactoryImplementor sessionFactory) {
if ( entity == null ) {
return null;
}
if ( HibernateProxy.class.isInstance( entity ) ) {
return ( (HibernateProxy) entity ).getHibernateLazyInitializer().getIdentifier();
}
if ( session != null ) {
final EntityEntry pcEntry = session.getPersistenceContext().getEntry( entity );
if ( pcEntry != null ) {
return pcEntry.getId();
}
}
final EntityPersister persister = resolveEntityPersister(
entity,
associationType,
session,
sessionFactory
);
return persister.getIdentifier( entity, session );
}
private static EntityPersister resolveEntityPersister(
Object entity,
AssociationType associationType,
SharedSessionContractImplementor session,
SessionFactoryImplementor sessionFactory) {
assert sessionFactory != null;
if ( session != null ) {
return session.getEntityPersister(
associationType.getAssociatedEntityName( session.getFactory() ),
entity
);
}
String entityName = null;
for ( EntityNameResolver entityNameResolver : sessionFactory.getMetamodel().getEntityNameResolvers() ) {
entityName = entityNameResolver.resolveEntityName( entity );
if ( entityName != null ) {
break;
}
}
if ( entityName == null ) {
entityName = entity.getClass().getName();
}
return sessionFactory.getMetamodel().entityPersister( entityName );
}
@Override
public void resetIdentifier(Object entity, Serializable currentId, Object currentVersion) {
resetIdentifier( entity, currentId, currentVersion, null );
}
@Override
public void resetIdentifier(
Object entity,
Serializable currentId,
Object currentVersion,
SharedSessionContractImplementor session) {
if ( entityMetamodel.getIdentifierProperty().getIdentifierGenerator() instanceof Assigned ) {
}
else {
Serializable result = entityMetamodel.getIdentifierProperty()
.getUnsavedValue()
.getDefaultValue( currentId );
setIdentifier( entity, result, session );
VersionProperty versionProperty = entityMetamodel.getVersionProperty();
if ( entityMetamodel.isVersioned() ) {
setPropertyValue(
entity,
entityMetamodel.getVersionPropertyIndex(),
versionProperty.getUnsavedValue().getDefaultValue( currentVersion )
);
}
}
}
@Override
public Object getVersion(Object entity) throws HibernateException {
if ( !entityMetamodel.isVersioned() ) {
return null;
}
return getters[entityMetamodel.getVersionPropertyIndex()].get( entity );
}
protected boolean shouldGetAllProperties(Object entity) {
if ( !getEntityMetamodel().getBytecodeEnhancementMetadata().isEnhancedForLazyLoading() ) {
return true;
}
return !getEntityMetamodel().getBytecodeEnhancementMetadata().hasUnFetchedAttributes( entity );
}
@Override
public Object[] getPropertyValues(Object entity) {
final BytecodeEnhancementMetadata enhancementMetadata = entityMetamodel.getBytecodeEnhancementMetadata();
final int span = entityMetamodel.getPropertySpan();
final Object[] result = new Object[span];
for ( int j = 0; j < span; j++ ) {
NonIdentifierAttribute property = entityMetamodel.getProperties()[j];
if ( !property.isLazy() || enhancementMetadata.isAttributeLoaded( entity, property.getName() ) ) {
result[j] = getters[j].get( entity );
}
else {
result[j] = LazyPropertyInitializer.UNFETCHED_PROPERTY;
}
}
return result;
}
@Override
public Object[] getPropertyValuesToInsert(Object entity, Map mergeMap, SharedSessionContractImplementor session) {
final int span = entityMetamodel.getPropertySpan();
final Object[] result = new Object[span];
for ( int j = 0; j < span; j++ ) {
result[j] = getters[j].getForInsert( entity, mergeMap, session );
}
return result;
}
@Override
public Object getPropertyValue(Object entity, int i) throws HibernateException {
return getters[i].get( entity );
}
@Override
public Object getPropertyValue(Object entity, String propertyPath) throws HibernateException {
int loc = propertyPath.indexOf( '.' );
String basePropertyName = loc > 0
? propertyPath.substring( 0, loc )
: propertyPath;
Integer index = entityMetamodel.getPropertyIndexOrNull( basePropertyName );
if ( index == null ) {
propertyPath = PropertyPath.IDENTIFIER_MAPPER_PROPERTY + "." + propertyPath;
loc = propertyPath.indexOf( '.' );
basePropertyName = loc > 0
? propertyPath.substring( 0, loc )
: propertyPath;
}
index = entityMetamodel.getPropertyIndexOrNull( basePropertyName );
final Object baseValue = getPropertyValue( entity, index );
if ( loc > 0 ) {
if ( baseValue == null ) {
return null;
}
return getComponentValue(
(ComponentType) entityMetamodel.getPropertyTypes()[index],
baseValue,
propertyPath.substring( loc + 1 )
);
}
else {
return baseValue;
}
}
protected Object getComponentValue(ComponentType type, Object component, String propertyPath) {
final int loc = propertyPath.indexOf( '.' );
final String basePropertyName = loc > 0
? propertyPath.substring( 0, loc )
: propertyPath;
final int index = findSubPropertyIndex( type, basePropertyName );
final Object baseValue = type.getPropertyValue( component, index );
if ( loc > 0 ) {
if ( baseValue == null ) {
return null;
}
return getComponentValue(
(ComponentType) type.getSubtypes()[index],
baseValue,
propertyPath.substring( loc + 1 )
);
}
else {
return baseValue;
}
}
private int findSubPropertyIndex(ComponentType type, String subPropertyName) {
final String[] propertyNames = type.getPropertyNames();
for ( int index = 0; index < propertyNames.length; index++ ) {
if ( subPropertyName.equals( propertyNames[index] ) ) {
return index;
}
}
throw new MappingException( "component property not found: " + subPropertyName );
}
@Override
public void setPropertyValues(Object entity, Object[] values) throws HibernateException {
boolean setAll = !entityMetamodel.hasLazyProperties();
for ( int j = 0; j < entityMetamodel.getPropertySpan(); j++ ) {
if ( setAll || values[j] != LazyPropertyInitializer.UNFETCHED_PROPERTY ) {
setters[j].set( entity, values[j], getFactory() );
}
}
}
@Override
public void setPropertyValue(Object entity, int i, Object value) throws HibernateException {
setters[i].set( entity, value, getFactory() );
}
@Override
public void setPropertyValue(Object entity, String propertyName, Object value) throws HibernateException {
setters[entityMetamodel.getPropertyIndex( propertyName )].set( entity, value, getFactory() );
}
@Override
public final Object instantiate(Serializable id) throws HibernateException {
return instantiate( id, null );
}
@Override
public final Object instantiate(Serializable id, SharedSessionContractImplementor session) {
Object result = getInstantiator().instantiate( id );
if ( id != null ) {
setIdentifier( result, id, session );
}
return result;
}
@Override
public final Object instantiate() throws HibernateException {
return instantiate( null, null );
}
@Override
public void afterInitialize(Object entity, SharedSessionContractImplementor session) {
}
@Override
public final boolean isInstance(Object object) {
return getInstantiator().isInstance( object );
}
@Override
public boolean hasProxy() {
return entityMetamodel.isLazy() && !entityMetamodel.getBytecodeEnhancementMetadata().isEnhancedForLazyLoading();
}
@Override
public final Object createProxy(Serializable id, SharedSessionContractImplementor session) {
return getProxyFactory().getProxy( id, session );
}
@Override
public boolean isLifecycleImplementor() {
return false;
}
protected final EntityMetamodel getEntityMetamodel() {
return entityMetamodel;
}
protected final SessionFactoryImplementor getFactory() {
return entityMetamodel.getSessionFactory();
}
protected final Instantiator getInstantiator() {
return instantiator;
}
protected final ProxyFactory getProxyFactory() {
return proxyFactory;
}
@Override
public String toString() {
return getClass().getName() + '(' + getEntityMetamodel().getName() + ')';
}
@Override
public Getter getIdentifierGetter() {
return idGetter;
}
@Override
public Getter getVersionGetter() {
if ( getEntityMetamodel().isVersioned() ) {
return getGetter( getEntityMetamodel().getVersionPropertyIndex() );
}
return null;
}
@Override
public Getter getGetter(int i) {
return getters[i];
}
}