Update of /cvsroot/hibernate/Hibernate/cirrus/hibernate/persister In directory usw-pr-cvs1:/tmp/cvs-serv30487/cirrus/hibernate/persister Modified Files: Tag: dynamic_update AbstractEntityPersister.java ClassPersister.java EntityPersister.java MultiTableEntityPersister.java Log Message: dynamically generated update and insert SQL to: * update only columns that changed * insert only non-null values Index: AbstractEntityPersister.java =================================================================== RCS file: /cvsroot/hibernate/Hibernate/cirrus/hibernate/persister/AbstractEntityPersister.java,v retrieving revision 1.9 retrieving revision 1.9.2.1 diff -C2 -d -r1.9 -r1.9.2.1 *** AbstractEntityPersister.java 26 Oct 2002 16:43:27 -0000 1.9 --- AbstractEntityPersister.java 31 Oct 2002 08:36:18 -0000 1.9.2.1 *************** *** 234,238 **** * Determine if the given field values are dirty */ ! public boolean isDirty(Object[] x, Object[] y, Object owner, SessionImplementor session) throws HibernateException { int prop = TypeFactory.findDirty( propertyTypes, x, y, owner, session.getFactory() ); if ( prop==-1) { --- 234,238 ---- * Determine if the given field values are dirty */ ! public boolean isDirty(Object[] x, Object[] y, boolean[] dirtyFields, Object owner, SessionImplementor session) throws HibernateException { int prop = TypeFactory.findDirty( propertyTypes, x, y, owner, session.getFactory() ); if ( prop==-1) { *************** *** 241,244 **** --- 241,246 ---- else { if ( log.isTraceEnabled() ) log.trace( className + "."+ propertyNames[prop] + " is dirty" ); + dirtyFields[prop] = true; + for ( int i=prop+1; i<hydrateSpan; i++ ) dirtyFields[i] = propertyTypes[i].isDirty( x[i], y[i], owner, session.getFactory() ); return true; } Index: ClassPersister.java =================================================================== RCS file: /cvsroot/hibernate/Hibernate/cirrus/hibernate/persister/ClassPersister.java,v retrieving revision 1.18 retrieving revision 1.18.2.1 diff -C2 -d -r1.18 -r1.18.2.1 *** ClassPersister.java 25 Oct 2002 18:26:11 -0000 1.18 --- ClassPersister.java 31 Oct 2002 08:36:18 -0000 1.18.2.1 *************** *** 141,145 **** * Compare two snapshots of the state of an instance to determine if the persistent state was modified */ ! public boolean isDirty(Object[] x, Object[] y, Object owner, SessionImplementor session) throws HibernateException; /** --- 141,145 ---- * Compare two snapshots of the state of an instance to determine if the persistent state was modified */ ! public boolean isDirty(Object[] x, Object[] y, boolean[] dirtyFields, Object owner, SessionImplementor session) throws HibernateException; /** *************** *** 221,225 **** * Update a persistent instance */ ! public void update(Serializable id, Object[] fields, Object oldVersion, Object object, SessionImplementor session) throws SQLException, HibernateException; /** --- 221,225 ---- * Update a persistent instance */ ! public void update(Serializable id, Object[] fields, boolean[] dirty, Object oldVersion, Object object, SessionImplementor session) throws SQLException, HibernateException; /** Index: EntityPersister.java =================================================================== RCS file: /cvsroot/hibernate/Hibernate/cirrus/hibernate/persister/EntityPersister.java,v retrieving revision 1.32 retrieving revision 1.32.2.1 diff -C2 -d -r1.32 -r1.32.2.1 *** EntityPersister.java 27 Oct 2002 03:11:33 -0000 1.32 --- EntityPersister.java 31 Oct 2002 08:36:18 -0000 1.32.2.1 *************** *** 24,27 **** --- 24,28 ---- import java.sql.SQLException; import java.util.ArrayList; + import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; *************** *** 65,70 **** --- 66,73 ---- private transient final String identityInsertString; private transient final String identitySelectString; + private transient final String identityColumnInsertString; private transient final String[] identifierColumnNames; + private transient final int identifierColumnSpan; private transient final Cascades.IdentifierValue unsavedIdentifierValue; *************** *** 302,310 **** * Generate the SQL that inserts a row */ ! protected String generateInsertString(boolean identityString, String identityColumnInsertString) { - boolean hasPropertyColumns = columnNames.length > 0; boolean insertIdentifierColumns = !identityString || identityColumnInsertString!=null; ! String commaAfterProperties = hasPropertyColumns && ( insertIdentifierColumns || isPolymorphic() ) ? ", " : ""; String commaAfterIdentifiers = insertIdentifierColumns && isPolymorphic() ? ", " : ""; --- 305,327 ---- * Generate the SQL that inserts a row */ ! protected String generateInsertString(boolean identityString, String identityColumnInsertString, boolean[] notNullProps) { boolean insertIdentifierColumns = !identityString || identityColumnInsertString!=null; ! ! StringBuffer setters = new StringBuffer(50); ! int cols = 0; ! for ( int i=0; i<hydrateSpan; i++) { ! if ( notNullProps[i] ) { ! int len = propertyColumnNames[i].length; ! if (len>0) { ! if (cols>0) setters.append(", "); ! setters.append( StringHelper.join( ", ", propertyColumnNames[i] ) ); ! cols += len; ! } ! } ! } ! ! boolean hasPropertyColumns = cols>0; ! String commaAfterProperties = hasPropertyColumns && ( insertIdentifierColumns || isPolymorphic() ) ? ", " : ""; String commaAfterIdentifiers = insertIdentifierColumns && isPolymorphic() ? ", " : ""; *************** *** 312,327 **** .append(qualifiedTableName) .append(" ( ") ! .append( cirrus.hibernate.helpers.StringHelper.join( ", ", columnNames ) ) ! .append(commaAfterProperties) ! .append( insertIdentifierColumns ? StringHelper.join( ", ", identifierColumnNames ) : "" ) ! .append(commaAfterIdentifiers) ! .append( isPolymorphic() ? getDiscriminatorColumnName() : "" ) //to handle discriminator column ! .append(" ) values ( ") ! .append( hasPropertyColumns ? StringHelper.repeat( "?, ", columnNames.length-1 ) : "" ) ! .append( hasPropertyColumns ? "?" : "" ) .append(commaAfterProperties); if ( !identityString ) { ! buf.append( StringHelper.repeat( "?, ", identifierColumnNames.length-1 ) ) ! .append( identifierColumnNames.length > 0 ? "?" : "" ); } else if ( identityColumnInsertString!=null ) { --- 329,347 ---- .append(qualifiedTableName) .append(" ( ") ! .append( setters.toString() ) .append(commaAfterProperties); + if (insertIdentifierColumns) buf.append( StringHelper.join( ", ", identifierColumnNames ) ); + buf.append(commaAfterIdentifiers); + if ( isPolymorphic() ) buf.append( getDiscriminatorColumnName() ); + + buf.append(" ) values ( "); + if (hasPropertyColumns) { + buf.append( StringHelper.repeat( "?, ", cols-1 ) ) + .append("?"); + buf.append(commaAfterProperties); + } if ( !identityString ) { ! buf.append( StringHelper.repeat( "?, ", identifierColumnNames.length-1 ) ); ! /*if (identifierColumnNames.length > 0) */buf.append("?"); } else if ( identityColumnInsertString!=null ) { *************** *** 329,333 **** } buf.append(commaAfterIdentifiers); ! if ( isPolymorphic() ) buf.append(discriminatorSQLString); //to handle discriminator column return buf.append(" )").toString(); } --- 349,353 ---- } buf.append(commaAfterIdentifiers); ! if ( isPolymorphic() ) buf.append(discriminatorSQLString); return buf.append(" )").toString(); } *************** *** 368,377 **** * Generate the SQL that updates a row by id (and version) */ ! protected String generateUpdateString() { ! StringBuffer setters = new StringBuffer(20); ! for (int i=0; i<columnNames.length; i++) { ! setters.append( columnNames[i] ).append(" = ?"); ! if (i!=columnNames.length-1) setters.append(", "); } StringBuffer buf = new StringBuffer(100) .append("update ") --- 388,407 ---- * Generate the SQL that updates a row by id (and version) */ ! protected String generateUpdateString( boolean[] dirtyProps ) { ! //String setters = StringHelper.join(" = ?, ", columnNames); ! //if (columnNames.length!=0) setters = setters + " = ?" ! StringBuffer setters = new StringBuffer(50); ! boolean cols = false; ! for ( int i=0; i<dirtyProps.length; i++ ) { ! if ( dirtyProps[i] ) { ! if ( propertyColumnNames[i].length > 0 ) { ! if (cols) setters.append(", "); ! cols = true; ! setters.append( StringHelper.join(" = ?, ", propertyColumnNames[i] ) ) ! .append(" = ?"); ! } ! } } + StringBuffer buf = new StringBuffer(100) .append("update ") *************** *** 413,417 **** * Marshall the fields of a persistent instance to a prepared statement */ ! protected void dehydrate(Serializable id, Object[] fields, PreparedStatement st, SessionImplementor session) throws SQLException, HibernateException { if ( log.isTraceEnabled() ) log.trace("Dehydrating entity: " + getClassName() + '#' + id); --- 443,447 ---- * Marshall the fields of a persistent instance to a prepared statement */ ! protected int dehydrate(Serializable id, boolean[] dirty, Object[] fields, PreparedStatement st, SessionImplementor session) throws SQLException, HibernateException { if ( log.isTraceEnabled() ) log.trace("Dehydrating entity: " + getClassName() + '#' + id); *************** *** 419,428 **** int index = 1; for (int j=0; j<hydrateSpan; j++) { ! getPropertyTypes()[j].nullSafeSet( st, fields[j], index, session ); ! index += propertyColumnSpans[j]; } ! if ( id!=null ) getIdentifierType().nullSafeSet( st, id, index, session ); ! } --- 449,465 ---- int index = 1; for (int j=0; j<hydrateSpan; j++) { ! if ( dirty[j] ) { ! getPropertyTypes()[j].nullSafeSet( st, fields[j], index, session ); ! index += propertyColumnSpans[j]; ! } } ! if ( id!=null ) { ! getIdentifierType().nullSafeSet( st, id, index, session ); ! index += identifierColumnSpan; ! } ! ! return index; ! } *************** *** 479,482 **** --- 516,525 ---- } + private boolean[] notNull(Object[] fields) { + boolean[] notNull = new boolean[hydrateSpan]; + for ( int i=0; i<hydrateSpan; i++ ) notNull[i] = fields[i]!=null; + return notNull; + } + /** * Persist an object *************** *** 488,494 **** if ( isVersioned() ) log.trace( "Version: " + Versioning.getVersion(fields, this) ); } ! // Render the SQL query ! PreparedStatement statement = session.getBatcher().prepareBatchStatement( sqlInsert() ); try { --- 531,541 ---- if ( isVersioned() ) log.trace( "Version: " + Versioning.getVersion(fields, this) ); } ! ! boolean[] notNull = notNull(fields); ! // Render the SQL query ! PreparedStatement statement = session.getBatcher().prepareBatchStatement( ! generateInsertString(false, null, notNull) ! ); //sqlInsert() try { *************** *** 497,501 **** // insert was issued (cos of foreign key constraints). Not necessarily the object's current state ! dehydrate(id, fields, statement, session); session.getBatcher().addToBatch(1); --- 544,548 ---- // insert was issued (cos of foreign key constraints). Not necessarily the object's current state ! dehydrate(id, notNull, fields, statement, session); session.getBatcher().addToBatch(1); *************** *** 507,511 **** throw sqle; } ! } --- 554,558 ---- throw sqle; } ! } *************** *** 519,529 **** if ( isVersioned() ) log.trace( "Version: " + Versioning.getVersion(fields, this) ); } // Render the SQL query ! PreparedStatement statement = session.getBatcher().prepareStatement( sqlIdentityInsert() ); try { ! dehydrate(null, fields, statement, session); statement.executeUpdate(); --- 566,580 ---- if ( isVersioned() ) log.trace( "Version: " + Versioning.getVersion(fields, this) ); } + + boolean[] notNull = notNull(fields); // Render the SQL query ! PreparedStatement statement = session.getBatcher().prepareStatement( ! generateInsertString(true, identityColumnInsertString, notNull) ! ); //sqlIdentityInsert() try { ! dehydrate(null, notNull, fields, statement, session); statement.executeUpdate(); *************** *** 616,620 **** * Update an object */ ! public void update(Serializable id, Object[] fields, Object oldVersion, Object object, SessionImplementor session) throws SQLException, HibernateException { if ( log.isTraceEnabled() ) { --- 667,671 ---- * Update an object */ ! public void update(Serializable id, Object[] fields, boolean[] dirty, Object oldVersion, Object object, SessionImplementor session) throws SQLException, HibernateException { if ( log.isTraceEnabled() ) { *************** *** 623,630 **** } ! if (!hasColumns) return; //Render the SQL query ! String sql = sqlUpdate(); final PreparedStatement statement; if ( isVersioned() ) { --- 674,683 ---- } ! if (!hasColumns) return; //TODO: fix this somehow - I think its only needed for use with update() ! ! if ( isVersioned() ) dirty[ getVersionProperty() ] = true; //Render the SQL query ! String sql = generateUpdateString(dirty); //sqlUpdate() final PreparedStatement statement; if ( isVersioned() ) { *************** *** 639,646 **** //Now write the values of fields onto the prepared statement ! dehydrate(id, fields, statement, session); if ( isVersioned() ) { ! getVersionType().nullSafeSet( statement, oldVersion, columnNames.length + identifierColumnNames.length + 1, session ); check( statement.executeUpdate(), id ); } --- 692,699 ---- //Now write the values of fields onto the prepared statement ! int versionIndex = dehydrate(id, dirty, fields, statement, session); if ( isVersioned() ) { ! getVersionType().nullSafeSet( statement, oldVersion, versionIndex, session ); check( statement.executeUpdate(), id ); } *************** *** 676,681 **** // IDENTIFIER ! int idColumnSpan = model.getIdentifier().getColumnSpan(); ! identifierColumnNames = new String[idColumnSpan]; Value idValue = model.getIdentifier(); --- 729,734 ---- // IDENTIFIER ! identifierColumnSpan = model.getIdentifier().getColumnSpan(); ! identifierColumnNames = new String[identifierColumnSpan]; Value idValue = model.getIdentifier(); *************** *** 716,720 **** ! final String identityColumnInsertString; IdentifierGenerator idgen = model.getIdentifier().getIdentifierGenerator(); useIdentityColumn = dialect.supportsIdentityColumns() && ( idgen instanceof NativeGenerator ); --- 769,773 ---- ! //final String identityColumnInsertString; IdentifierGenerator idgen = model.getIdentifier().getIdentifierGenerator(); useIdentityColumn = dialect.supportsIdentityColumns() && ( idgen instanceof NativeGenerator ); *************** *** 848,855 **** while ( iter.hasNext() ) joinedFetch[j++] = ( (Integer) iter.next() ).intValue(); deleteString = generateDeleteString(); ! insertString = generateInsertString(false, null); ! identityInsertString = useIdentityColumn ? generateInsertString(true, identityColumnInsertString) : null; ! updateString = generateUpdateString(); String lockString = generateLockString(); --- 901,911 ---- while ( iter.hasNext() ) joinedFetch[j++] = ( (Integer) iter.next() ).intValue(); + boolean[] allProps = new boolean[hydrateSpan]; + Arrays.fill(allProps, true); + deleteString = generateDeleteString(); ! insertString = generateInsertString(false, null, allProps); ! identityInsertString = useIdentityColumn ? generateInsertString(true, identityColumnInsertString, allProps): null; ! updateString = generateUpdateString(allProps); String lockString = generateLockString(); Index: MultiTableEntityPersister.java =================================================================== RCS file: /cvsroot/hibernate/Hibernate/cirrus/hibernate/persister/MultiTableEntityPersister.java,v retrieving revision 1.23 retrieving revision 1.23.2.1 diff -C2 -d -r1.23 -r1.23.2.1 *** MultiTableEntityPersister.java 27 Oct 2002 03:11:33 -0000 1.23 --- MultiTableEntityPersister.java 31 Oct 2002 08:36:18 -0000 1.23.2.1 *************** *** 686,690 **** * Update an object */ ! public void update(Serializable id, Object[] fields, Object oldVersion, Object object, SessionImplementor session) throws SQLException, HibernateException { if ( log.isTraceEnabled() ) { --- 686,690 ---- * Update an object */ ! public void update(Serializable id, Object[] fields, boolean[] dirty, Object oldVersion, Object object, SessionImplementor session) throws SQLException, HibernateException { if ( log.isTraceEnabled() ) { |