org.hibernate.type.AnyType.java Source code

Java tutorial

Introduction

Here is the source code for org.hibernate.type.AnyType.java

Source

/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
 * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
 */
package org.hibernate.type;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;

import org.hibernate.EntityMode;
import org.hibernate.EntityNameResolver;
import org.hibernate.FetchMode;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.PropertyNotFoundException;
import org.hibernate.TransientObjectException;
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
import org.hibernate.engine.internal.ForeignKeys;
import org.hibernate.engine.jdbc.Size;
import org.hibernate.engine.spi.CascadeStyle;
import org.hibernate.engine.spi.CascadeStyles;
import org.hibernate.engine.spi.Mapping;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Joinable;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.HibernateProxyHelper;
import org.hibernate.proxy.LazyInitializer;

/**
 * Handles "any" mappings
 * 
 * @author Gavin King
 */
public class AnyType extends AbstractType implements CompositeType, AssociationType {
    private final TypeFactory.TypeScope scope;
    private final Type identifierType;
    private final Type discriminatorType;
    private final boolean eager;

    /**
     * Intended for use only from legacy {@link ObjectType} type definition
     */
    protected AnyType(Type discriminatorType, Type identifierType) {
        this(null, discriminatorType, identifierType, true);
    }

    public AnyType(TypeFactory.TypeScope scope, Type discriminatorType, Type identifierType, boolean lazy) {
        this.scope = scope;
        this.discriminatorType = discriminatorType;
        this.identifierType = identifierType;
        this.eager = !lazy;
    }

    public Type getIdentifierType() {
        return identifierType;
    }

    public Type getDiscriminatorType() {
        return discriminatorType;
    }

    // general Type metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    @Override
    public String getName() {
        return "object";
    }

    @Override
    public Class getReturnedClass() {
        return Object.class;
    }

    @Override
    public int[] sqlTypes(Mapping mapping) throws MappingException {
        return ArrayHelper.join(discriminatorType.sqlTypes(mapping), identifierType.sqlTypes(mapping));
    }

    @Override
    public Size[] dictatedSizes(Mapping mapping) throws MappingException {
        return ArrayHelper.join(discriminatorType.dictatedSizes(mapping), identifierType.dictatedSizes(mapping));
    }

    @Override
    public Size[] defaultSizes(Mapping mapping) throws MappingException {
        return ArrayHelper.join(discriminatorType.defaultSizes(mapping), identifierType.defaultSizes(mapping));
    }

    @Override
    public Object[] getPropertyValues(Object component, EntityMode entityMode) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean isAnyType() {
        return true;
    }

    @Override
    public boolean isAssociationType() {
        return true;
    }

    @Override
    public boolean isComponentType() {
        return true;
    }

    @Override
    public boolean isEmbedded() {
        return false;
    }

    @Override
    public boolean isMutable() {
        return false;
    }

    @Override
    public Object deepCopy(Object value, SessionFactoryImplementor factory) {
        return value;
    }

    // general Type functionality ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    @Override
    public int compare(Object x, Object y) {
        if (x == null) {
            // if y is also null, return that they are the same (no option for "UNKNOWN")
            // if y is not null, return that y is "greater" (-1 because the result is from the perspective of
            //       the first arg: x)
            return y == null ? 0 : -1;
        } else if (y == null) {
            // x is not null, but y is.  return that x is "greater"
            return 1;
        }

        // At this point we know both are non-null.
        final Object xId = extractIdentifier(x);
        final Object yId = extractIdentifier(y);

        return getIdentifierType().compare(xId, yId);
    }

    private Object extractIdentifier(Object entity) {
        final EntityPersister concretePersister = guessEntityPersister(entity);
        return concretePersister == null ? null : concretePersister.getEntityTuplizer().getIdentifier(entity, null);
    }

    private EntityPersister guessEntityPersister(Object object) {
        if (scope == null) {
            return null;
        }

        String entityName = null;

        // this code is largely copied from Session's bestGuessEntityName
        Object entity = object;
        if (entity instanceof HibernateProxy) {
            final LazyInitializer initializer = ((HibernateProxy) entity).getHibernateLazyInitializer();
            if (initializer.isUninitialized()) {
                entityName = initializer.getEntityName();
            }
            entity = initializer.getImplementation();
        }

        if (entityName == null) {
            for (EntityNameResolver resolver : scope.getTypeConfiguration().getSessionFactory().getMetamodel()
                    .getEntityNameResolvers()) {
                entityName = resolver.resolveEntityName(entity);
                if (entityName != null) {
                    break;
                }
            }
        }

        if (entityName == null) {
            // the old-time stand-by...
            entityName = object.getClass().getName();
        }

        return scope.getTypeConfiguration().getSessionFactory().getMetamodel().entityPersister(entityName);
    }

    @Override
    public boolean isSame(Object x, Object y) throws HibernateException {
        return x == y;
    }

    @Override
    public boolean isModified(Object old, Object current, boolean[] checkable,
            SharedSessionContractImplementor session) throws HibernateException {
        if (current == null) {
            return old != null;
        } else if (old == null) {
            return true;
        }

        final ObjectTypeCacheEntry holder = (ObjectTypeCacheEntry) old;
        final boolean[] idCheckable = new boolean[checkable.length - 1];
        System.arraycopy(checkable, 1, idCheckable, 0, idCheckable.length);
        return (checkable[0] && !holder.entityName.equals(session.bestGuessEntityName(current)))
                || identifierType.isModified(holder.id, getIdentifier(current, session), idCheckable, session);
    }

    @Override
    public boolean[] toColumnNullness(Object value, Mapping mapping) {
        final boolean[] result = new boolean[getColumnSpan(mapping)];
        if (value != null) {
            Arrays.fill(result, true);
        }
        return result;
    }

    @Override
    public boolean isDirty(Object old, Object current, boolean[] checkable,
            SharedSessionContractImplementor session) throws HibernateException {
        return isDirty(old, current, session);
    }

    @Override
    public int getColumnSpan(Mapping session) {
        return 2;
    }

    @Override
    public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner)
            throws HibernateException, SQLException {
        return resolveAny((String) discriminatorType.nullSafeGet(rs, names[0], session, owner),
                (Serializable) identifierType.nullSafeGet(rs, names[1], session, owner), session);
    }

    @Override
    public Object hydrate(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner)
            throws HibernateException, SQLException {
        final String entityName = (String) discriminatorType.nullSafeGet(rs, names[0], session, owner);
        final Serializable id = (Serializable) identifierType.nullSafeGet(rs, names[1], session, owner);
        return new ObjectTypeCacheEntry(entityName, id);
    }

    @Override
    public Object resolve(Object value, SharedSessionContractImplementor session, Object owner)
            throws HibernateException {
        final ObjectTypeCacheEntry holder = (ObjectTypeCacheEntry) value;
        return resolveAny(holder.entityName, holder.id, session);
    }

    private Object resolveAny(String entityName, Serializable id, SharedSessionContractImplementor session)
            throws HibernateException {
        return entityName == null || id == null ? null : session.internalLoad(entityName, id, eager, false);
    }

    @Override
    public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session)
            throws HibernateException, SQLException {
        nullSafeSet(st, value, index, null, session);
    }

    @Override
    public void nullSafeSet(PreparedStatement st, Object value, int index, boolean[] settable,
            SharedSessionContractImplementor session) throws HibernateException, SQLException {
        Serializable id;
        String entityName;
        if (value == null) {
            id = null;
            entityName = null;
        } else {
            entityName = session.bestGuessEntityName(value);
            id = ForeignKeys.getEntityIdentifierIfNotUnsaved(entityName, value, session);
        }

        // discriminatorType is assumed to be single-column type
        if (settable == null || settable[0]) {
            discriminatorType.nullSafeSet(st, entityName, index, session);
        }
        if (settable == null) {
            identifierType.nullSafeSet(st, id, index + 1, session);
        } else {
            final boolean[] idSettable = new boolean[settable.length - 1];
            System.arraycopy(settable, 1, idSettable, 0, idSettable.length);
            identifierType.nullSafeSet(st, id, index + 1, idSettable, session);
        }
    }

    @Override
    public String toLoggableString(Object value, SessionFactoryImplementor factory) throws HibernateException {
        //TODO: terrible implementation!
        if (value == null) {
            return "null";
        }
        if (value == LazyPropertyInitializer.UNFETCHED_PROPERTY || !Hibernate.isInitialized(value)) {
            return "<uninitialized>";
        }
        Class valueClass = HibernateProxyHelper.getClassWithoutInitializingProxy(value);
        return factory.getTypeHelper().entity(valueClass).toLoggableString(value, factory);
    }

    @Override
    public Object assemble(Serializable cached, SharedSessionContractImplementor session, Object owner)
            throws HibernateException {
        final ObjectTypeCacheEntry e = (ObjectTypeCacheEntry) cached;
        return e == null ? null : session.internalLoad(e.entityName, e.id, eager, false);
    }

    @Override
    public Serializable disassemble(Object value, SharedSessionContractImplementor session, Object owner)
            throws HibernateException {
        if (value == null) {
            return null;
        } else {
            return new ObjectTypeCacheEntry(session.bestGuessEntityName(value), ForeignKeys
                    .getEntityIdentifierIfNotUnsaved(session.bestGuessEntityName(value), value, session));
        }
    }

    @Override
    public Object replace(Object original, Object target, SharedSessionContractImplementor session, Object owner,
            Map copyCache) throws HibernateException {
        if (original == null) {
            return null;
        } else {
            final String entityName = session.bestGuessEntityName(original);
            final Serializable id = ForeignKeys.getEntityIdentifierIfNotUnsaved(entityName, original, session);
            return session.internalLoad(entityName, id, eager, false);
        }
    }

    @Override
    public Object nullSafeGet(ResultSet rs, String name, SharedSessionContractImplementor session, Object owner) {
        throw new UnsupportedOperationException("object is a multicolumn type");
    }

    @Override
    public Object semiResolve(Object value, SharedSessionContractImplementor session, Object owner) {
        throw new UnsupportedOperationException("any mappings may not form part of a property-ref");
    }

    // CompositeType implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    @Override
    public boolean isMethodOf(Method method) {
        return false;
    }

    private static final String[] PROPERTY_NAMES = new String[] { "class", "id" };

    @Override
    public String[] getPropertyNames() {
        return PROPERTY_NAMES;
    }

    @Override
    public int getPropertyIndex(String name) {
        if (PROPERTY_NAMES[0].equals(name)) {
            return 0;
        } else if (PROPERTY_NAMES[1].equals(name)) {
            return 1;
        }

        throw new PropertyNotFoundException("Unable to locate property named " + name + " on AnyType");
    }

    @Override
    public Object getPropertyValue(Object component, int i, SharedSessionContractImplementor session)
            throws HibernateException {
        return i == 0 ? session.bestGuessEntityName(component) : getIdentifier(component, session);
    }

    @Override
    public Object[] getPropertyValues(Object component, SharedSessionContractImplementor session)
            throws HibernateException {
        return new Object[] { session.bestGuessEntityName(component), getIdentifier(component, session) };
    }

    private Serializable getIdentifier(Object value, SharedSessionContractImplementor session)
            throws HibernateException {
        try {
            return ForeignKeys.getEntityIdentifierIfNotUnsaved(session.bestGuessEntityName(value), value, session);
        } catch (TransientObjectException toe) {
            return null;
        }
    }

    @Override
    public void setPropertyValues(Object component, Object[] values, EntityMode entityMode) {
        throw new UnsupportedOperationException();
    }

    private static final boolean[] NULLABILITY = new boolean[] { false, false };

    @Override
    public boolean[] getPropertyNullability() {
        return NULLABILITY;
    }

    @Override
    public boolean hasNotNullProperty() {
        // both are non-nullable
        return true;
    }

    @Override
    public Type[] getSubtypes() {
        return new Type[] { discriminatorType, identifierType };
    }

    @Override
    public CascadeStyle getCascadeStyle(int i) {
        return CascadeStyles.NONE;
    }

    @Override
    public FetchMode getFetchMode(int i) {
        return FetchMode.SELECT;
    }

    // AssociationType implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    @Override
    public ForeignKeyDirection getForeignKeyDirection() {
        return ForeignKeyDirection.FROM_PARENT;
    }

    @Override
    public boolean useLHSPrimaryKey() {
        return false;
    }

    @Override
    public String getLHSPropertyName() {
        return null;
    }

    public boolean isReferenceToPrimaryKey() {
        return true;
    }

    @Override
    public String getRHSUniqueKeyPropertyName() {
        return null;
    }

    @Override
    public boolean isAlwaysDirtyChecked() {
        return false;
    }

    @Override
    public Joinable getAssociatedJoinable(SessionFactoryImplementor factory) {
        throw new UnsupportedOperationException("any types do not have a unique referenced persister");
    }

    @Override
    public String getAssociatedEntityName(SessionFactoryImplementor factory) {
        throw new UnsupportedOperationException("any types do not have a unique referenced persister");
    }

    @Override
    public String getOnCondition(String alias, SessionFactoryImplementor factory, Map enabledFilters) {
        throw new UnsupportedOperationException();
    }

    @Override
    public String getOnCondition(String alias, SessionFactoryImplementor factory, Map enabledFilters,
            Set<String> treatAsDeclarations) {
        throw new UnsupportedOperationException();
    }

    /**
     * Used to externalize discrimination per a given identifier.  For example, when writing to
     * second level cache we write the discrimination resolved concrete type for each entity written.
     */
    public static final class ObjectTypeCacheEntry implements Serializable {
        final String entityName;
        final Serializable id;

        ObjectTypeCacheEntry(String entityName, Serializable id) {
            this.entityName = entityName;
            this.id = id;
        }
    }
}