package org.hibernate.type;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;
import java.util.Set;
import org.hibernate.AssertionFailure;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.engine.internal.ForeignKeys;
import org.hibernate.engine.spi.EntityUniqueKey;
import org.hibernate.engine.spi.Mapping;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Joinable;
import org.hibernate.persister.entity.UniqueKeyLoadable;
import org.hibernate.proxy.HibernateProxy;
public abstract class EntityType extends AbstractType implements AssociationType {
private final TypeFactory.TypeScope scope;
private final String associatedEntityName;
protected final String uniqueKeyPropertyName;
private final boolean eager;
private final boolean unwrapProxy;
private final boolean referenceToPrimaryKey;
private transient volatile Type associatedIdentifierType;
private transient volatile EntityPersister associatedEntityPersister;
private transient Class returnedClass;
@Deprecated
protected EntityType(
TypeFactory.TypeScope scope,
String entityName,
String uniqueKeyPropertyName,
boolean eager,
boolean unwrapProxy) {
this( scope, entityName, uniqueKeyPropertyName == null, uniqueKeyPropertyName, eager, unwrapProxy );
}
protected EntityType(
TypeFactory.TypeScope scope,
String entityName,
boolean referenceToPrimaryKey,
String uniqueKeyPropertyName,
boolean eager,
boolean unwrapProxy) {
this.scope = scope;
this.associatedEntityName = entityName;
this.uniqueKeyPropertyName = uniqueKeyPropertyName;
this.eager = eager;
this.unwrapProxy = unwrapProxy;
this.referenceToPrimaryKey = referenceToPrimaryKey;
}
protected EntityType(EntityType original, String superTypeEntityName) {
this.scope = original.scope;
this.associatedEntityName = superTypeEntityName;
this.uniqueKeyPropertyName = original.uniqueKeyPropertyName;
this.eager = original.eager;
this.unwrapProxy = original.unwrapProxy;
this.referenceToPrimaryKey = original.referenceToPrimaryKey;
}
protected TypeFactory.TypeScope scope() {
return scope;
}
@Override
public boolean isAssociationType() {
return true;
}
@Override
public final boolean isEntityType() {
return true;
}
@Override
public boolean isMutable() {
return false;
}
@Override
public String toString() {
return getClass().getName() + '(' + getAssociatedEntityName() + ')';
}
@Override
public String getName() {
return associatedEntityName;
}
public boolean isReferenceToPrimaryKey() {
return referenceToPrimaryKey;
}
@Override
public String getRHSUniqueKeyPropertyName() {
return referenceToPrimaryKey ? null : uniqueKeyPropertyName;
}
@Override
public String getLHSPropertyName() {
return null;
}
public String getPropertyName() {
return null;
}
public final String getAssociatedEntityName() {
return associatedEntityName;
}
@Override
public String getAssociatedEntityName(SessionFactoryImplementor factory) {
return getAssociatedEntityName();
}
@Override
public Joinable getAssociatedJoinable(SessionFactoryImplementor factory) throws MappingException {
return (Joinable) getAssociatedEntityPersister( factory );
}
@Override
public final Class getReturnedClass() {
if ( returnedClass == null ) {
returnedClass = determineAssociatedEntityClass();
}
return returnedClass;
}
private Class determineAssociatedEntityClass() {
final String entityName = getAssociatedEntityName();
try {
return ReflectHelper.classForName( entityName );
}
catch (ClassNotFoundException cnfe) {
return this.scope.getTypeConfiguration().getSessionFactory().getMetamodel().entityPersister( entityName ).
getEntityTuplizer().getMappedClass();
}
}
@Override
public Object nullSafeGet(ResultSet rs, String name, SharedSessionContractImplementor session, Object owner)
throws HibernateException, SQLException {
return nullSafeGet( rs, new String[] {name}, session, owner );
}
@Override
public final Object nullSafeGet(
ResultSet rs,
String[] names,
SharedSessionContractImplementor session,
Object owner) throws HibernateException, SQLException {
return resolve( hydrate( rs, names, session, owner ), session, owner );
}
@Override
public void nullSafeSet(PreparedStatement st, Object value, int index, boolean[] settable, SharedSessionContractImplementor session)
throws SQLException {
if ( settable.length > 0 ) {
requireIdentifierOrUniqueKeyType( session.getFactory() )
.nullSafeSet( st, getIdentifier( value, session ), index, settable, session );
}
}
@Override
public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session)
throws SQLException {
requireIdentifierOrUniqueKeyType( session.getFactory() )
.nullSafeSet( st, getIdentifier( value, session ), index, session );
}
@Override
public final boolean isSame(Object x, Object y) {
return x == y;
}
@Override
public int compare(Object x, Object y) {
return 0;
}
@Override
public Object deepCopy(Object value, SessionFactoryImplementor factory) {
return value;
}
@Override
public Object replace(
Object original,
Object target,
SharedSessionContractImplementor session,
Object owner,
Map copyCache) throws HibernateException {
if ( original == null ) {
return null;
}
Object cached = copyCache.get( original );
if ( cached != null ) {
return cached;
}
else {
if ( original == target ) {
return target;
}
if ( session.getContextEntityIdentifier( original ) == null &&
ForeignKeys.isTransient( associatedEntityName, original, Boolean.FALSE, session ) ) {
if ( copyCache.containsValue( original ) ) {
return original;
}
else {
final Object copy = session.getEntityPersister( associatedEntityName, original )
.instantiate( null, session );
copyCache.put( original, copy );
return copy;
}
}
else {
Object id = getIdentifier( original, session );
if ( id == null ) {
throw new AssertionFailure(
"non-transient entity has a null id: " + original.getClass()
.getName()
);
}
id = getIdentifierOrUniqueKeyType( session.getFactory() )
.replace( id, null, session, owner, copyCache );
return resolve( id, session, owner );
}
}
}
@Override
public int getHashCode(Object x, SessionFactoryImplementor factory) {
EntityPersister persister = getAssociatedEntityPersister( factory );
if ( !persister.canExtractIdOutOfEntity() ) {
return super.getHashCode( x );
}
final Serializable id;
if ( x instanceof HibernateProxy ) {
id = ( (HibernateProxy) x ).getHibernateLazyInitializer().getIdentifier();
}
else {
final Class mappedClass = persister.getMappedClass();
if ( mappedClass.isAssignableFrom( x.getClass() ) ) {
id = persister.getIdentifier( x );
}
else {
id = (Serializable) x;
}
}
return persister.getIdentifierType().getHashCode( id, factory );
}
@Override
public boolean isEqual(Object x, Object y, SessionFactoryImplementor factory) {
if ( x == null || y == null ) {
return x == y;
}
EntityPersister persister = getAssociatedEntityPersister( factory );
if ( !persister.canExtractIdOutOfEntity() ) {
return super.isEqual( x, y );
}
final Class mappedClass = persister.getMappedClass();
Serializable xid;
if ( x instanceof HibernateProxy ) {
xid = ( (HibernateProxy) x ).getHibernateLazyInitializer()
.getIdentifier();
}
else {
if ( mappedClass.isAssignableFrom( x.getClass() ) ) {
xid = persister.getIdentifier( x );
}
else {
xid = (Serializable) x;
}
}
Serializable yid;
if ( y instanceof HibernateProxy ) {
yid = ( (HibernateProxy) y ).getHibernateLazyInitializer()
.getIdentifier();
}
else {
if ( mappedClass.isAssignableFrom( y.getClass() ) ) {
yid = persister.getIdentifier( y );
}
else {
yid = (Serializable) y;
}
}
return persister.getIdentifierType()
.isEqual( xid, yid, factory );
}
@Override
public String getOnCondition(String alias, SessionFactoryImplementor factory, Map enabledFilters) {
return getOnCondition( alias, factory, enabledFilters, null );
}
@Override
public String getOnCondition(
String alias,
SessionFactoryImplementor factory,
Map enabledFilters,
Set<String> treatAsDeclarations) {
if ( isReferenceToPrimaryKey() && ( treatAsDeclarations == null || treatAsDeclarations.isEmpty() ) ) {
return "";
}
else {
return getAssociatedJoinable( factory ).filterFragment( alias, enabledFilters, treatAsDeclarations );
}
}
@Override
public Object resolve(Object value, SharedSessionContractImplementor session, Object owner) throws HibernateException {
return resolve(value, session, owner, null);
}
@Override
public Object resolve(Object value, SharedSessionContractImplementor session, Object owner, Boolean overridingEager) throws HibernateException {
if ( value != null && !isNull( owner, session ) ) {
if ( isReferenceToPrimaryKey() ) {
return resolveIdentifier( (Serializable) value, session, overridingEager );
}
else if ( uniqueKeyPropertyName != null ) {
return loadByUniqueKey( getAssociatedEntityName(), uniqueKeyPropertyName, value, session );
}
}
return null;
}
@Override
public Type getSemiResolvedType(SessionFactoryImplementor factory) {
return getAssociatedEntityPersister( factory ).getIdentifierType();
}
protected EntityPersister getAssociatedEntityPersister(final SessionFactoryImplementor factory) {
final EntityPersister persister = associatedEntityPersister;
if ( persister == null ) {
associatedEntityPersister = factory.getMetamodel().entityPersister( getAssociatedEntityName() );
return associatedEntityPersister;
}
else {
return persister;
}
}
protected final Object getIdentifier(Object value, SharedSessionContractImplementor session) throws HibernateException {
if ( isReferenceToPrimaryKey() || uniqueKeyPropertyName == null ) {
return ForeignKeys.getEntityIdentifierIfNotUnsaved(
getAssociatedEntityName(),
value,
session
);
}
else if ( value == null ) {
return null;
}
else {
EntityPersister entityPersister = getAssociatedEntityPersister( session.getFactory() );
Object propertyValue = entityPersister.getPropertyValue( value, uniqueKeyPropertyName );
Type type = entityPersister.getPropertyType( uniqueKeyPropertyName );
if ( type.isEntityType() ) {
propertyValue = ( (EntityType) type ).getIdentifier( propertyValue, session );
}
return propertyValue;
}
}
@Override
public String toLoggableString(Object value, SessionFactoryImplementor factory) {
if ( value == null ) {
return "null";
}
final EntityPersister persister = getAssociatedEntityPersister( factory );
if ( !persister.getEntityTuplizer().isInstance( value ) ) {
if ( persister.getIdentifierType().getReturnedClass().isInstance( value ) ) {
return associatedEntityName + "#" + value;
}
}
final StringBuilder result = new StringBuilder().append( associatedEntityName );
if ( persister.hasIdentifierProperty() ) {
final Serializable id;
if ( value instanceof HibernateProxy ) {
HibernateProxy proxy = (HibernateProxy) value;
id = proxy.getHibernateLazyInitializer().getIdentifier();
}
else {
id = persister.getIdentifier( value );
}
result.append( '#' )
.append( persister.getIdentifierType().toLoggableString( id, factory ) );
}
return result.toString();
}
public abstract boolean isOneToOne();
public boolean isLogicalOneToOne() {
return isOneToOne();
}
Type getIdentifierType(final Mapping factory) {
final Type type = associatedIdentifierType;
if ( type == null ) {
associatedIdentifierType = factory.getIdentifierType( getAssociatedEntityName() );
return associatedIdentifierType;
}
else {
return type;
}
}
Type getIdentifierType(final SharedSessionContractImplementor session) {
final Type type = associatedIdentifierType;
if ( type == null ) {
associatedIdentifierType = getIdentifierType( session.getFactory() );
return associatedIdentifierType;
}
else {
return type;
}
}
public final Type getIdentifierOrUniqueKeyType(Mapping factory) throws MappingException {
if ( isReferenceToPrimaryKey() || uniqueKeyPropertyName == null ) {
return getIdentifierType( factory );
}
else {
Type type = factory.getReferencedPropertyType( getAssociatedEntityName(), uniqueKeyPropertyName );
if ( type.isEntityType() ) {
type = ( (EntityType) type ).getIdentifierOrUniqueKeyType( factory );
}
return type;
}
}
public final String getIdentifierOrUniqueKeyPropertyName(Mapping factory)
throws MappingException {
if ( isReferenceToPrimaryKey() || uniqueKeyPropertyName == null ) {
return factory.getIdentifierPropertyName( getAssociatedEntityName() );
}
else {
return uniqueKeyPropertyName;
}
}
protected abstract boolean isNullable();
protected final Object resolveIdentifier(Serializable id, SharedSessionContractImplementor session, Boolean overridingEager) throws HibernateException {
boolean isProxyUnwrapEnabled = unwrapProxy &&
getAssociatedEntityPersister( session.getFactory() )
.isInstrumented();
boolean eager = overridingEager != null ? overridingEager : this.eager;
Object proxyOrEntity = session.internalLoad(
getAssociatedEntityName(),
id,
eager,
isNullable()
);
if ( proxyOrEntity instanceof HibernateProxy ) {
( (HibernateProxy) proxyOrEntity ).getHibernateLazyInitializer()
.setUnwrap( isProxyUnwrapEnabled );
}
return proxyOrEntity;
}
protected final Object resolveIdentifier(Serializable id, SharedSessionContractImplementor session) throws HibernateException {
return resolveIdentifier( id, session, null );
}
protected boolean isNull(Object owner, SharedSessionContractImplementor session) {
return false;
}
public Object loadByUniqueKey(
String entityName,
String uniqueKeyPropertyName,
Object key,
SharedSessionContractImplementor session) throws HibernateException {
final SessionFactoryImplementor factory = session.getFactory();
UniqueKeyLoadable persister = (UniqueKeyLoadable) factory.getMetamodel().entityPersister( entityName );
EntityUniqueKey euk = new EntityUniqueKey(
entityName,
uniqueKeyPropertyName,
key,
getIdentifierOrUniqueKeyType( factory ),
persister.getEntityMode(),
session.getFactory()
);
final PersistenceContext persistenceContext = session.getPersistenceContext();
Object result = persistenceContext.getEntity( euk );
if ( result == null ) {
result = persister.loadByUniqueKey( uniqueKeyPropertyName, key, session );
if (result != null) {
persistenceContext.addEntity(euk, result);
}
}
return result == null ? null : persistenceContext.proxyFor( result );
}
protected Type requireIdentifierOrUniqueKeyType(Mapping mapping) {
final Type fkTargetType = getIdentifierOrUniqueKeyType( mapping );
if ( fkTargetType == null ) {
throw new MappingException(
"Unable to determine FK target Type for many-to-one or one-to-one mapping: " +
"referenced-entity-name=[" + getAssociatedEntityName() +
"], referenced-entity-attribute-name=[" + getLHSPropertyName() + "]"
);
}
return fkTargetType;
}
}