Hi,
I made a simple modification in the ComponentType class to consider
null component equals to a components with all his properties nulls.
I do this to address 2 problems I have:
1. For simplicity in all entities, when a components is null the
getters builds a new instance and then returning it. But with this
pattern hibernate detects this entities dirty and generates
updates to the db that aren't need.
2. I have a very long process where I traverse a long dataset make
some computations and generates very few result. This process
always generates OutofMemory exception because hibernate detects
to many dirty entities (one solution it to flush the session
often, but I don't want to update the entity and the process with
the spurious updates is slower)
---------------------------------------
Some code to explain better:
/**
* return the properties' values if the components != null, or and
array of null values.
*/
private Object[] getPropertyValuesArray(Object component, EntityMode
entityMode) {
if (component == null) {
return new Object[propertyTypes.length];
} else {
return getPropertyValues(component, entityMode);
}
}
/**
* And somethig similar for isDirty, isSame
*/
public boolean isEqual(Object x, Object y, EntityMode entityMode)
throws HibernateException {
if ( x == y ) return true;
//if ( x == null || y == null ) return false;
Object[] xvalues = getPropertyValuesArray(x, entityMode);
Object[] yvalues = getPropertyValuesArray(y, entityMode);
for ( int i = 0; i < propertySpan; i++ ) {
if ( !propertyTypes[i].isEqual( xvalues[i], yvalues[i],
entityMode ) ) return false;
}
return true;
}
-----------------------------------
Regars,
------------------------------------------------------------------------
//$Id: ComponentType.java,v 1.37 2005/07/29 05:36:14 oneovthafew Exp $
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.HashMap;
import java.util.Map;
import org.dom4j.Element;
import org.dom4j.Node;
import org.hibernate.EntityMode;
import org.hibernate.FetchMode;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.engine.CascadeStyle;
import org.hibernate.engine.Mapping;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.tuple.ComponentTuplizer;
import org.hibernate.tuple.TuplizerLookup;
import org.hibernate.util.ArrayHelper;
import org.hibernate.util.StringHelper;
/**
* Handles "component" mappings
*
* @author Gavin King
*/
public class ComponentType extends AbstractType implements
AbstractComponentType {
private final Type[] propertyTypes;
private final String[] propertyNames;
private final boolean[] propertyNullability;
protected final int propertySpan;
private final CascadeStyle[] cascade;
private final FetchMode[] joinedFetch;
private final boolean isKey;
protected TuplizerLookup tuplizers;
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 j = 0; j < subtypes.length; j++ ) {
sqlTypes[n++] = subtypes[j];
}
}
return sqlTypes;
}
public int getColumnSpan(Mapping mapping) throws MappingException {
int span = 0;
for ( int i = 0; i < propertySpan; i++ ) {
span += propertyTypes[i].getColumnSpan( mapping );
}
return span;
}
public ComponentType(
final String[] propertyNames,
final Type[] propertyTypes,
final boolean[] nullabilities,
final FetchMode[] joinedFetch,
final CascadeStyle[] cascade,
final boolean key,
final TuplizerLookup tuplizers)
throws MappingException {
this.propertyTypes = propertyTypes;
this.propertyNullability = nullabilities;
this.propertyNames = propertyNames;
this.cascade = cascade;
this.joinedFetch = joinedFetch;
isKey = key;
propertySpan = propertyTypes.length;
this.tuplizers = tuplizers;
}
public final boolean isComponentType() {
return true;
}
public Class getReturnedClass() {
return tuplizers.getTuplizer(EntityMode.POJO).getMappedClass();
//TODO
}
private Object[] getPropertyValuesArray(Object component, EntityMode
entityMode) {
if (component == null) {
return new Object[propertyTypes.length];
} else {
return getPropertyValues(component, entityMode);
}
}
public boolean isSame(Object x, Object y, EntityMode entityMode) throws
HibernateException {
if ( x == y ) return true;
Object[] xvalues = getPropertyValuesArray(y, entityMode);
Object[] yvalues = getPropertyValuesArray(y, entityMode);
for ( int i = 0; i < propertySpan; i++ ) {
if ( !propertyTypes[i].isSame( xvalues[i], yvalues[i],
entityMode ) ) {
return false;
}
}
return true;
}
public boolean isEqual(Object x, Object y, EntityMode entityMode)
throws HibernateException {
if ( x == y ) return true;
//if ( x == null || y == null ) return false;
Object[] xvalues = getPropertyValuesArray(x, entityMode);
Object[] yvalues = getPropertyValuesArray(y, entityMode);
for ( int i = 0; i < propertySpan; i++ ) {
if ( !propertyTypes[i].isEqual( xvalues[i], yvalues[i],
entityMode ) ) return false;
}
return true;
}
public boolean isEqual(Object x, Object y, EntityMode entityMode, SessionFactoryImplementor factory)
throws HibernateException {
if ( x == y ) return true;
//if ( x == null || y == null ) return false;
Object[] xvalues = getPropertyValuesArray(x, entityMode);
Object[] yvalues = getPropertyValuesArray(y, entityMode);
for ( int i = 0; i < propertySpan; i++ ) {
if ( !propertyTypes[i].isEqual( xvalues[i], yvalues[i],
entityMode, factory ) ) return false;
}
return true;
}
public int compare(Object x, Object y, EntityMode entityMode) {
if ( x == y ) return 0;
Object[] xvalues = getPropertyValuesArray(x, entityMode);
Object[] yvalues = getPropertyValuesArray(y, entityMode);
for ( int i = 0; i < propertySpan; i++ ) {
int propertyCompare = propertyTypes[i].compare(
xvalues[i], yvalues[i], entityMode );
if ( propertyCompare != 0 ) return propertyCompare;
}
return 0;
}
public boolean isMethodOf(Method method) {
return false;
}
public int getHashCode(Object x, EntityMode entityMode) {
int result = 17;
Object[] values = getPropertyValues(x, entityMode);
for ( int i = 0; i < propertySpan; i++ ) {
Object y = values[i];
result *= 37;
if ( y != null ) result +=
propertyTypes[i].getHashCode( y, entityMode );
}
return result;
}
public int getHashCode(Object x, EntityMode entityMode,
SessionFactoryImplementor factory) {
int result = 17;
Object[] values = getPropertyValues(x, entityMode);
for ( int i = 0; i < propertySpan; i++ ) {
Object y = values[i];
result *= 37;
if ( y != null ) result +=
propertyTypes[i].getHashCode( y, entityMode, factory );
}
return result;
}
public boolean isDirty(Object x, Object y, SessionImplementor session)
throws HibernateException {
if ( x == y ) return false;
//if ( x == null || y == null ) return true;
EntityMode entityMode = session.getEntityMode();
Object[] xvalues = getPropertyValues(x, entityMode);
Object[] yvalues = getPropertyValues(y, entityMode);
for ( int i = 0; i < xvalues.length; i++ ) {
if ( propertyTypes[i].isDirty( xvalues[i], yvalues[i],
session ) ) return true;
}
return false;
}
public boolean isDirty(Object x, Object y, boolean[] checkable, SessionImplementor session)
throws HibernateException {
if ( x == y ) return false;
//if ( x == null || y == null ) return true;
EntityMode entityMode = session.getEntityMode();
Object[] xvalues = getPropertyValuesArray(x, entityMode);
Object[] yvalues = getPropertyValuesArray(y, entityMode);
int loc = 0;
for ( int i = 0; i < xvalues.length; i++ ) {
int len = propertyTypes[i].getColumnSpan(
session.getFactory() );
if (len<=1) {
final boolean dirty = ( len==0 || checkable[loc] ) &&
propertyTypes[i].isDirty( xvalues[i], yvalues[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(
xvalues[i], yvalues[i], subcheckable, session );
if ( dirty ) return true;
}
loc += len;
}
return false;
}
public boolean isModified(Object old, Object current, boolean[]
checkable, SessionImplementor session)
throws HibernateException {
if (old == null && current == null) return false;
if (current == null) return true;
Object[] currentValues;
Object[] oldValues;
if (old == null && current != null) {
currentValues = getPropertyValues(current, session);
oldValues = new Object[currentValues.length];
} else {
currentValues = getPropertyValues( current, session );
oldValues = ( Object[] ) old;
}
int loc = 0;
for ( int i = 0; i < currentValues.length; i++ ) {
int len = propertyTypes[i].getColumnSpan(
session.getFactory() );
boolean[] subcheckable = new boolean[len];
System.arraycopy(checkable, loc, subcheckable, 0, len);
if ( propertyTypes[i].isModified( oldValues[i],
currentValues[i], subcheckable, session ) ) {
return true;
}
loc += len;
}
return false;
}
public Object nullSafeGet(ResultSet rs, String[] names,
SessionImplementor session, Object owner)
throws HibernateException, SQLException {
return resolve( hydrate( rs, names, session, owner ), session,
owner );
}
public void nullSafeSet(PreparedStatement st, Object value, int begin,
SessionImplementor session)
throws HibernateException, SQLException {
Object[] subvalues = nullSafeGetValues( value,
session.getEntityMode() );
for ( int i = 0; i < propertySpan; i++ ) {
propertyTypes[i].nullSafeSet( st, subvalues[i], begin,
session );
begin += propertyTypes[i].getColumnSpan(
session.getFactory() );
}
}
public void nullSafeSet(
PreparedStatement st,
Object value,
int begin,
boolean[] settable,
SessionImplementor session)
throws HibernateException, SQLException {
Object[] subvalues = nullSafeGetValues( value,
session.getEntityMode() );
int loc = 0;
for ( int i = 0; i < propertySpan; i++ ) {
int len = propertyTypes[i].getColumnSpan(
session.getFactory() );
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 );
}
}
public Object nullSafeGet(ResultSet rs, String name, SessionImplementor
session, Object owner)
throws HibernateException, SQLException {
return nullSafeGet( rs, new String[]{name}, session, owner );
}
public Object getPropertyValue(Object component, int i,
SessionImplementor session)
throws HibernateException {
return getPropertyValue( component, i, session.getEntityMode()
);
}
public Object getPropertyValue(Object component, int i, EntityMode
entityMode)
throws HibernateException {
return tuplizers.getTuplizer(entityMode).getPropertyValue(
component, i );
}
public Object[] getPropertyValues(Object component, SessionImplementor
session)
throws HibernateException {
return getPropertyValues( component, session.getEntityMode() );
}
public Object[] getPropertyValues(Object component, EntityMode entityMode)
throws HibernateException {
return
tuplizers.getTuplizer(entityMode).getPropertyValues(component);
}
public void setPropertyValues(Object component, Object[] values, EntityMode entityMode)
throws HibernateException {
tuplizers.getTuplizer(entityMode).setPropertyValues(component,
values);
}
public Type[] getSubtypes() {
return propertyTypes;
}
public String getName() {
return "component" + ArrayHelper.toString(propertyNames);
}
public String toLoggableString(Object value, SessionFactoryImplementor
factory)
throws HibernateException {
if ( value == null ) return "null";
Map result = new HashMap();
EntityMode entityMode = tuplizers.guessEntityMode(value);
if (entityMode==null) {
throw new ClassCastException(
value.getClass().getName() );
}
Object[] values = getPropertyValues( value, entityMode );
for ( int i = 0; i < propertyTypes.length; i++ ) {
result.put( propertyNames[i],
propertyTypes[i].toLoggableString( values[i], factory ) );
}
return StringHelper.unqualify( getName() ) + result.toString();
}
public String[] getPropertyNames() {
return propertyNames;
}
public Object deepCopy(Object component, EntityMode entityMode, 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],
entityMode, factory );
}
Object result = instantiate(entityMode);
setPropertyValues( result, values, entityMode );
//not absolutely necessary, but helps for some
//equals()/hashCode() implementations
ComponentTuplizer ct = (ComponentTuplizer)
tuplizers.getTuplizer(entityMode);
if ( ct.hasParentProperty() ) {
ct.setParent( result, ct.getParent(component), factory
);
}
return result;
}
public Object replace(
Object original,
Object target,
SessionImplementor 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;
final EntityMode entityMode = session.getEntityMode();
Object[] values = TypeFactory.replace(
getPropertyValues( original, entityMode ),
getPropertyValues( result, entityMode ),
propertyTypes,
session,
owner,
copyCache
);
setPropertyValues( result, values, entityMode );
return result;
}
public Object replace(
Object original,
Object target,
SessionImplementor 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;
final EntityMode entityMode = session.getEntityMode();
Object[] values = TypeFactory.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 tuplizers.getTuplizer(entityMode).instantiate();
}
public Object instantiate(Object parent, SessionImplementor session)
throws HibernateException {
Object result = instantiate( session.getEntityMode() );
ComponentTuplizer ct = (ComponentTuplizer)
tuplizers.getTuplizer( session.getEntityMode() );
if ( ct.hasParentProperty() && parent != null ) {
ct.setParent(
result,
session.getPersistenceContext().proxyFor( parent ),
session.getFactory()
);
}
return result;
}
public CascadeStyle getCascadeStyle(int i) {
return cascade[i];
}
public boolean isMutable() {
return true;
}
public Serializable disassemble(Object value, SessionImplementor
session, Object owner)
throws HibernateException {
if ( value == null ) {
return null;
}
else {
Object[] values = getPropertyValues( value,
session.getEntityMode() );
for ( int i = 0; i < propertyTypes.length; i++ ) {
values[i] = propertyTypes[i].disassemble(
values[i], session, owner );
}
return values;
}
}
public Object assemble(Serializable object, SessionImplementor 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,
session.getEntityMode() );
return result;
}
}
public FetchMode getFetchMode(int i) {
return joinedFetch[i];
}
public Object hydrate(
final ResultSet rs,
final String[] names,
final SessionImplementor 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;
}
public Object resolve(Object value, SessionImplementor 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,
session.getEntityMode() );
return result;
}
else {
return null;
}
}
public Object semiResolve(Object value, SessionImplementor session,
Object owner)
throws HibernateException {
//note that this implementation is kinda broken
//for components with many-to-one associations
return resolve( value, session, owner );
}
public boolean[] getPropertyNullability() {
return propertyNullability;
}
public boolean isXMLElement() {
return true;
}
public Object fromXMLNode(Node xml, Mapping factory) throws
HibernateException {
return xml;
}
public void setToXMLNode(Node node, Object value,
SessionFactoryImplementor factory) throws HibernateException {
replaceNode( node, (Element) value );
}
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;
}
public boolean isEmbedded() {
return false;
}
}