Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.hibernate.type.ComponentType Maven / Gradle / Ivy
/*
* 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 .
*/
package org.hibernate.type;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import org.hibernate.EntityMode;
import org.hibernate.FetchMode;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.PropertyNotFoundException;
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
import org.hibernate.engine.jdbc.Size;
import org.hibernate.engine.spi.CascadeStyle;
import org.hibernate.engine.spi.Mapping;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.tuple.StandardProperty;
import org.hibernate.tuple.ValueGeneration;
import org.hibernate.tuple.component.ComponentMetamodel;
import org.hibernate.tuple.component.ComponentTuplizer;
/**
* Handles "component" mappings
*
* @author Gavin King
*/
public class ComponentType extends AbstractType implements CompositeType, ProcedureParameterExtractionAware {
private final TypeFactory.TypeScope typeScope;
private final String[] propertyNames;
private final Type[] propertyTypes;
private final ValueGeneration[] propertyValueGenerationStrategies;
private final boolean[] propertyNullability;
protected final int propertySpan;
private final CascadeStyle[] cascade;
private final FetchMode[] joinedFetch;
private final boolean isKey;
private boolean hasNotNullProperty;
private final boolean createEmptyCompositesEnabled;
protected final EntityMode entityMode;
protected final ComponentTuplizer componentTuplizer;
public ComponentType(TypeFactory.TypeScope typeScope, ComponentMetamodel metamodel) {
this.typeScope = typeScope;
// for now, just "re-flatten" the metamodel since this is temporary stuff anyway (HHH-1907)
this.isKey = metamodel.isKey();
this.propertySpan = metamodel.getPropertySpan();
this.propertyNames = new String[propertySpan];
this.propertyTypes = new Type[propertySpan];
this.propertyValueGenerationStrategies = new ValueGeneration[propertySpan];
this.propertyNullability = new boolean[propertySpan];
this.cascade = new CascadeStyle[propertySpan];
this.joinedFetch = new FetchMode[propertySpan];
for ( int i = 0; i < propertySpan; i++ ) {
StandardProperty prop = metamodel.getProperty( i );
this.propertyNames[i] = prop.getName();
this.propertyTypes[i] = prop.getType();
this.propertyNullability[i] = prop.isNullable();
this.cascade[i] = prop.getCascadeStyle();
this.joinedFetch[i] = prop.getFetchMode();
if ( !prop.isNullable() ) {
hasNotNullProperty = true;
}
this.propertyValueGenerationStrategies[i] = prop.getValueGenerationStrategy();
}
this.entityMode = metamodel.getEntityMode();
this.componentTuplizer = metamodel.getComponentTuplizer();
this.createEmptyCompositesEnabled = metamodel.isCreateEmptyCompositesEnabled();
}
public boolean isKey() {
return isKey;
}
public EntityMode getEntityMode() {
return entityMode;
}
public ComponentTuplizer getComponentTuplizer() {
return componentTuplizer;
}
@Override
public int getColumnSpan(Mapping mapping) throws MappingException {
int span = 0;
for ( int i = 0; i < propertySpan; i++ ) {
span += propertyTypes[i].getColumnSpan( mapping );
}
return span;
}
@Override
public int[] sqlTypes(Mapping mapping) throws MappingException {
//Not called at runtime so doesn't matter if its slow :)
int[] sqlTypes = new int[getColumnSpan( mapping )];
int n = 0;
for ( int i = 0; i < propertySpan; i++ ) {
int[] subtypes = propertyTypes[i].sqlTypes( mapping );
for ( int subtype : subtypes ) {
sqlTypes[n++] = subtype;
}
}
return sqlTypes;
}
@Override
public Size[] dictatedSizes(Mapping mapping) throws MappingException {
//Not called at runtime so doesn't matter if its slow :)
final Size[] sizes = new Size[getColumnSpan( mapping )];
int soFar = 0;
for ( Type propertyType : propertyTypes ) {
final Size[] propertySizes = propertyType.dictatedSizes( mapping );
System.arraycopy( propertySizes, 0, sizes, soFar, propertySizes.length );
soFar += propertySizes.length;
}
return sizes;
}
@Override
public Size[] defaultSizes(Mapping mapping) throws MappingException {
//Not called at runtime so doesn't matter if its slow :)
final Size[] sizes = new Size[getColumnSpan( mapping )];
int soFar = 0;
for ( Type propertyType : propertyTypes ) {
final Size[] propertySizes = propertyType.defaultSizes( mapping );
System.arraycopy( propertySizes, 0, sizes, soFar, propertySizes.length );
soFar += propertySizes.length;
}
return sizes;
}
@Override
public final boolean isComponentType() {
return true;
}
public Class getReturnedClass() {
return componentTuplizer.getMappedClass();
}
@Override
public boolean isSame(Object x, Object y) throws HibernateException {
if ( x == y ) {
return true;
}
// null value and empty component are considered equivalent
Object[] xvalues = getPropertyValues( x, entityMode );
Object[] yvalues = getPropertyValues( y, entityMode );
for ( int i = 0; i < propertySpan; i++ ) {
if ( !propertyTypes[i].isSame( xvalues[i], yvalues[i] ) ) {
return false;
}
}
return true;
}
@Override
public boolean isEqual(final Object x, final Object y) throws HibernateException {
if ( x == y ) {
return true;
}
// null value and empty component are considered equivalent
for ( int i = 0; i < propertySpan; i++ ) {
if ( !propertyTypes[i].isEqual( getPropertyValue( x, i ), getPropertyValue( y, i ) ) ) {
return false;
}
}
return true;
}
@Override
public boolean isEqual(final Object x, final Object y, final SessionFactoryImplementor factory)
throws HibernateException {
if ( x == y ) {
return true;
}
// null value and empty component are considered equivalent
for ( int i = 0; i < propertySpan; i++ ) {
if ( !propertyTypes[i].isEqual( getPropertyValue( x, i ), getPropertyValue( y, i ), factory ) ) {
return false;
}
}
return true;
}
@Override
public int compare(final Object x, final Object y) {
if ( x == y ) {
return 0;
}
for ( int i = 0; i < propertySpan; i++ ) {
int propertyCompare = propertyTypes[i].compare( getPropertyValue( x, i ), getPropertyValue( y, i ) );
if ( propertyCompare != 0 ) {
return propertyCompare;
}
}
return 0;
}
public boolean isMethodOf(Method method) {
return false;
}
@Override
public int getHashCode(final Object x) {
int result = 17;
for ( int i = 0; i < propertySpan; i++ ) {
Object y = getPropertyValue( x, i );
result *= 37;
if ( y != null ) {
result += propertyTypes[i].getHashCode( y );
}
}
return result;
}
@Override
public int getHashCode(final Object x, final SessionFactoryImplementor factory) {
int result = 17;
for ( int i = 0; i < propertySpan; i++ ) {
Object y = getPropertyValue( x, i );
result *= 37;
if ( y != null ) {
result += propertyTypes[i].getHashCode( y, factory );
}
}
return result;
}
@Override
public boolean isDirty(final Object x, final Object y, final SharedSessionContractImplementor session) throws HibernateException {
if ( x == y ) {
return false;
}
// null value and empty component are considered equivalent
for ( int i = 0; i < propertySpan; i++ ) {
if ( propertyTypes[i].isDirty( getPropertyValue( x, i ), getPropertyValue( y, i ), session ) ) {
return true;
}
}
return false;
}
public boolean isDirty(final Object x, final Object y, final boolean[] checkable, final SharedSessionContractImplementor session)
throws HibernateException {
if ( x == y ) {
return false;
}
// null value and empty component are considered equivalent
int loc = 0;
for ( int i = 0; i < propertySpan; i++ ) {
int len = propertyTypes[i].getColumnSpan( session.getFactory() );
if ( len <= 1 ) {
final boolean dirty = ( len == 0 || checkable[loc] ) &&
propertyTypes[i].isDirty( getPropertyValue( x, i ), getPropertyValue( y, i ), session );
if ( dirty ) {
return true;
}
}
else {
boolean[] subcheckable = new boolean[len];
System.arraycopy( checkable, loc, subcheckable, 0, len );
final boolean dirty = propertyTypes[i].isDirty(
getPropertyValue( x, i ),
getPropertyValue( y, i ),
subcheckable,
session
);
if ( dirty ) {
return true;
}
}
loc += len;
}
return false;
}
@Override
public boolean isModified(
final Object old,
final Object current,
final boolean[] checkable,
final SharedSessionContractImplementor session) throws HibernateException {
if ( old == current ) {
return false;
}
// null value and empty components are considered equivalent
int loc = 0;
for ( int i = 0; i < propertySpan; i++ ) {
int len = propertyTypes[i].getColumnSpan( session.getFactory() );
boolean[] subcheckable = new boolean[len];
System.arraycopy( checkable, loc, subcheckable, 0, len );
if ( propertyTypes[i].isModified( getPropertyValue( old, i ), getPropertyValue( current, i ), subcheckable, session ) ) {
return true;
}
loc += len;
}
return false;
}
@Override
public 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 begin, SharedSessionContractImplementor session)
throws HibernateException, SQLException {
Object[] subvalues = nullSafeGetValues( value, entityMode );
for ( int i = 0; i < propertySpan; i++ ) {
propertyTypes[i].nullSafeSet( st, subvalues[i], begin, session );
begin += propertyTypes[i].getColumnSpan( session.getFactory() );
}
}
@Override
public void nullSafeSet(
PreparedStatement st,
Object value,
int begin,
boolean[] settable,
SharedSessionContractImplementor session)
throws HibernateException, SQLException {
Object[] subvalues = nullSafeGetValues( value, entityMode );
int loc = 0;
for ( int i = 0; i < propertySpan; i++ ) {
int len = propertyTypes[i].getColumnSpan( session.getFactory() );
//noinspection StatementWithEmptyBody
if ( len == 0 ) {
//noop
}
else if ( len == 1 ) {
if ( settable[loc] ) {
propertyTypes[i].nullSafeSet( st, subvalues[i], begin, session );
begin++;
}
}
else {
boolean[] subsettable = new boolean[len];
System.arraycopy( settable, loc, subsettable, 0, len );
propertyTypes[i].nullSafeSet( st, subvalues[i], begin, subsettable, session );
begin += ArrayHelper.countTrue( subsettable );
}
loc += len;
}
}
private Object[] nullSafeGetValues(Object value, EntityMode entityMode) throws HibernateException {
if ( value == null ) {
return new Object[propertySpan];
}
else {
return getPropertyValues( value, entityMode );
}
}
@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 Object getPropertyValue(Object component, int i, SharedSessionContractImplementor session)
throws HibernateException {
return getPropertyValue( component, i );
}
public Object getPropertyValue(Object component, int i, EntityMode entityMode)
throws HibernateException {
return getPropertyValue( component, i );
}
public Object getPropertyValue(Object component, int i)
throws HibernateException {
if (component == null) {
component = new Object[propertySpan];
}
if ( component instanceof Object[] ) {
// A few calls to hashCode pass the property values already in an
// Object[] (ex: QueryKey hash codes for cached queries).
// It's easiest to just check for the condition here prior to
// trying reflection.
return ( (Object[]) component )[i];
}
else {
return componentTuplizer.getPropertyValue( component, i );
}
}
@Override
public Object[] getPropertyValues(Object component, SharedSessionContractImplementor session)
throws HibernateException {
return getPropertyValues( component, entityMode );
}
@Override
public Object[] getPropertyValues(Object component, EntityMode entityMode)
throws HibernateException {
if (component == null) {
component = new Object[propertySpan];
}
if ( component instanceof Object[] ) {
// A few calls to hashCode pass the property values already in an
// Object[] (ex: QueryKey hash codes for cached queries).
// It's easiest to just check for the condition here prior to
// trying reflection.
return (Object[]) component;
}
else {
return componentTuplizer.getPropertyValues( component );
}
}
@Override
public void setPropertyValues(Object component, Object[] values, EntityMode entityMode)
throws HibernateException {
componentTuplizer.setPropertyValues( component, values );
}
@Override
public Type[] getSubtypes() {
return propertyTypes;
}
public ValueGeneration[] getPropertyValueGenerationStrategies() {
return propertyValueGenerationStrategies;
}
@Override
public String getName() {
return "component" + ArrayHelper.toString( propertyNames );
}
@Override
public String toLoggableString(Object value, SessionFactoryImplementor factory)
throws HibernateException {
if ( value == null ) {
return "null";
}
if ( entityMode == null ) {
throw new ClassCastException( value.getClass().getName() );
}
Map result = new HashMap<>();
Object[] values = getPropertyValues( value, entityMode );
for ( int i = 0; i < propertyTypes.length; i++ ) {
if ( values[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY ) {
result.put( propertyNames[i], "" );
}
else {
result.put( propertyNames[i], propertyTypes[i].toLoggableString( values[i], factory ) );
}
}
return StringHelper.unqualify( getName() ) + result.toString();
}
@Override
public String[] getPropertyNames() {
return propertyNames;
}
@Override
public Object deepCopy(Object component, SessionFactoryImplementor factory)
throws HibernateException {
if ( component == null ) {
return null;
}
Object[] values = getPropertyValues( component, entityMode );
for ( int i = 0; i < propertySpan; i++ ) {
values[i] = propertyTypes[i].deepCopy( values[i], factory );
}
Object result = instantiate( entityMode );
setPropertyValues( result, values, entityMode );
//not absolutely necessary, but helps for some
//equals()/hashCode() implementations
if ( componentTuplizer.hasParentProperty() ) {
componentTuplizer.setParent( result, componentTuplizer.getParent( component ), factory );
}
return result;
}
@Override
public Object replace(
Object original,
Object target,
SharedSessionContractImplementor session,
Object owner,
Map copyCache)
throws HibernateException {
if ( original == null ) {
return null;
}
//if ( original == target ) return target;
final Object result = target == null
? instantiate( owner, session )
: target;
Object[] values = TypeHelper.replace(
getPropertyValues( original, entityMode ),
getPropertyValues( result, entityMode ),
propertyTypes,
session,
owner,
copyCache
);
setPropertyValues( result, values, entityMode );
return result;
}
@Override
public Object replace(
Object original,
Object target,
SharedSessionContractImplementor session,
Object owner,
Map copyCache,
ForeignKeyDirection foreignKeyDirection)
throws HibernateException {
if ( original == null ) {
return null;
}
//if ( original == target ) return target;
final Object result = target == null ?
instantiate( owner, session ) :
target;
Object[] values = TypeHelper.replace(
getPropertyValues( original, entityMode ),
getPropertyValues( result, entityMode ),
propertyTypes,
session,
owner,
copyCache,
foreignKeyDirection
);
setPropertyValues( result, values, entityMode );
return result;
}
/**
* This method does not populate the component parent
*/
public Object instantiate(EntityMode entityMode) throws HibernateException {
return componentTuplizer.instantiate();
}
public Object instantiate(Object parent, SharedSessionContractImplementor session)
throws HibernateException {
Object result = instantiate( entityMode );
if ( componentTuplizer.hasParentProperty() && parent != null ) {
componentTuplizer.setParent(
result,
session.getPersistenceContext().proxyFor( parent ),
session.getFactory()
);
}
return result;
}
@Override
public CascadeStyle getCascadeStyle(int i) {
return cascade[i];
}
@Override
public boolean isMutable() {
return true;
}
@Override
public Serializable disassemble(Object value, SharedSessionContractImplementor session, Object owner)
throws HibernateException {
if ( value == null ) {
return null;
}
else {
Object[] values = getPropertyValues( value, entityMode );
for ( int i = 0; i < propertyTypes.length; i++ ) {
values[i] = propertyTypes[i].disassemble( values[i], session, owner );
}
return values;
}
}
@Override
public Object assemble(Serializable object, SharedSessionContractImplementor session, Object owner)
throws HibernateException {
if ( object == null ) {
return null;
}
else {
Object[] values = (Object[]) object;
Object[] assembled = new Object[values.length];
for ( int i = 0; i < propertyTypes.length; i++ ) {
assembled[i] = propertyTypes[i].assemble( (Serializable) values[i], session, owner );
}
Object result = instantiate( owner, session );
setPropertyValues( result, assembled, entityMode );
return result;
}
}
@Override
public FetchMode getFetchMode(int i) {
return joinedFetch[i];
}
@Override
public Object hydrate(
final ResultSet rs,
final String[] names,
final SharedSessionContractImplementor session,
final Object owner)
throws HibernateException, SQLException {
int begin = 0;
boolean notNull = false;
Object[] values = new Object[propertySpan];
for ( int i = 0; i < propertySpan; i++ ) {
int length = propertyTypes[i].getColumnSpan( session.getFactory() );
String[] range = ArrayHelper.slice( names, begin, length ); //cache this
Object val = propertyTypes[i].hydrate( rs, range, session, owner );
if ( val == null ) {
if ( isKey ) {
return null; //different nullability rules for pk/fk
}
}
else {
notNull = true;
}
values[i] = val;
begin += length;
}
return notNull ? values : null;
}
@Override
public Object resolve(Object value, SharedSessionContractImplementor session, Object owner)
throws HibernateException {
if ( value != null ) {
Object result = instantiate( owner, session );
Object[] values = (Object[]) value;
Object[] resolvedValues = new Object[values.length]; //only really need new array during semiresolve!
for ( int i = 0; i < values.length; i++ ) {
resolvedValues[i] = propertyTypes[i].resolve( values[i], session, owner );
}
setPropertyValues( result, resolvedValues, entityMode );
return result;
}
else if ( isCreateEmptyCompositesEnabled() ) {
return instantiate( owner, session );
}
else {
return null;
}
}
@Override
public Object semiResolve(Object value, SharedSessionContractImplementor session, Object owner)
throws HibernateException {
//note that this implementation is kinda broken
//for components with many-to-one associations
return resolve( value, session, owner );
}
@Override
public boolean[] getPropertyNullability() {
return propertyNullability;
}
@Override
public boolean[] toColumnNullness(Object value, Mapping mapping) {
boolean[] result = new boolean[getColumnSpan( mapping )];
if ( value == null ) {
return result;
}
Object[] values = getPropertyValues( value, EntityMode.POJO ); //TODO!!!!!!!
int loc = 0;
for ( int i = 0; i < propertyTypes.length; i++ ) {
boolean[] propertyNullness = propertyTypes[i].toColumnNullness( values[i], mapping );
System.arraycopy( propertyNullness, 0, result, loc, propertyNullness.length );
loc += propertyNullness.length;
}
return result;
}
@Override
public boolean isEmbedded() {
return false;
}
@Override
public int getPropertyIndex(String name) {
String[] names = getPropertyNames();
for ( int i = 0, max = names.length; i < max; i++ ) {
if ( names[i].equals( name ) ) {
return i;
}
}
throw new PropertyNotFoundException(
"Unable to locate property named " + name + " on " + getReturnedClass().getName()
);
}
private Boolean canDoExtraction;
@Override
public boolean canDoExtraction() {
if ( canDoExtraction == null ) {
canDoExtraction = determineIfProcedureParamExtractionCanBePerformed();
}
return canDoExtraction;
}
private boolean determineIfProcedureParamExtractionCanBePerformed() {
for ( Type propertyType : propertyTypes ) {
if ( !ProcedureParameterExtractionAware.class.isInstance( propertyType ) ) {
return false;
}
if ( !( (ProcedureParameterExtractionAware) propertyType ).canDoExtraction() ) {
return false;
}
}
return true;
}
@Override
public Object extract(CallableStatement statement, int startIndex, SharedSessionContractImplementor session) throws SQLException {
Object[] values = new Object[propertySpan];
int currentIndex = startIndex;
boolean notNull = false;
for ( int i = 0; i < propertySpan; i++ ) {
// we know this cast is safe from canDoExtraction
final Type propertyType = propertyTypes[i];
final Object value = ((ProcedureParameterExtractionAware) propertyType).extract(
statement,
currentIndex,
session
);
if ( value == null ) {
if ( isKey ) {
return null; //different nullability rules for pk/fk
}
}
else {
notNull = true;
}
values[i] = value;
currentIndex += propertyType.getColumnSpan( session.getFactory() );
}
if ( !notNull ) {
values = null;
}
return resolve( values, session, null );
}
@Override
public Object extract(CallableStatement statement, String[] paramNames, SharedSessionContractImplementor session)
throws SQLException {
// for this form to work all sub-property spans must be one (1)...
Object[] values = new Object[propertySpan];
int indx = 0;
boolean notNull = false;
for ( String paramName : paramNames ) {
// we know this cast is safe from canDoExtraction
final ProcedureParameterExtractionAware propertyType = (ProcedureParameterExtractionAware) propertyTypes[indx];
final Object value = propertyType.extract( statement, new String[] {paramName}, session );
if ( value == null ) {
if ( isKey ) {
return null; //different nullability rules for pk/fk
}
}
else {
notNull = true;
}
values[indx] = value;
}
if ( !notNull ) {
values = null;
}
return resolve( values, session, null );
}
@Override
public boolean hasNotNullProperty() {
return hasNotNullProperty;
}
private boolean isCreateEmptyCompositesEnabled() {
return createEmptyCompositesEnabled;
}
}