From: <hib...@li...> - 2006-06-22 19:56:00
|
Author: ste...@jb... Date: 2006-06-22 15:51:43 -0400 (Thu, 22 Jun 2006) New Revision: 10040 Added: trunk/Hibernate3/src/org/hibernate/engine/ExecuteUpdateResultCheckStyle.java trunk/Hibernate3/src/org/hibernate/jdbc/Expectation.java trunk/Hibernate3/src/org/hibernate/jdbc/Expectations.java Modified: trunk/Hibernate3/src/org/hibernate/cfg/HbmBinder.java trunk/Hibernate3/src/org/hibernate/hibernate-mapping-3.0.dtd trunk/Hibernate3/src/org/hibernate/jdbc/Batcher.java trunk/Hibernate3/src/org/hibernate/jdbc/BatchingBatcher.java trunk/Hibernate3/src/org/hibernate/jdbc/NonBatchingBatcher.java trunk/Hibernate3/src/org/hibernate/mapping/Collection.java trunk/Hibernate3/src/org/hibernate/mapping/Join.java trunk/Hibernate3/src/org/hibernate/mapping/PersistentClass.java trunk/Hibernate3/src/org/hibernate/persister/collection/AbstractCollectionPersister.java trunk/Hibernate3/src/org/hibernate/persister/collection/BasicCollectionPersister.java trunk/Hibernate3/src/org/hibernate/persister/collection/OneToManyPersister.java trunk/Hibernate3/src/org/hibernate/persister/entity/AbstractEntityPersister.java trunk/Hibernate3/src/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java trunk/Hibernate3/src/org/hibernate/persister/entity/SingleTableEntityPersister.java trunk/Hibernate3/src/org/hibernate/persister/entity/UnionSubclassEntityPersister.java trunk/Hibernate3/src/org/hibernate/tool/hbm2ddl/SchemaExport.java Log: HHH-1792 - proper CallableStatement processing; HHH-1507 - mapping DTD typo; log SQLWarnings on SchemaExport Modified: trunk/Hibernate3/src/org/hibernate/cfg/HbmBinder.java =================================================================== --- trunk/Hibernate3/src/org/hibernate/cfg/HbmBinder.java 2006-06-21 22:29:24 UTC (rev 10039) +++ trunk/Hibernate3/src/org/hibernate/cfg/HbmBinder.java 2006-06-22 19:51:43 UTC (rev 10040) @@ -22,6 +22,7 @@ import org.hibernate.engine.FilterDefinition; import org.hibernate.engine.NamedQueryDefinition; import org.hibernate.engine.Versioning; +import org.hibernate.engine.ExecuteUpdateResultCheckStyle; import org.hibernate.id.PersistentIdentifierGenerator; import org.hibernate.mapping.Any; import org.hibernate.mapping.Array; @@ -668,23 +669,20 @@ throws MappingException { Element element = node.element( "sql-insert" ); if ( element != null ) { - boolean callable = false; - callable = isCallable( element ); - model.setCustomSQLInsert( element.getTextTrim(), callable ); + boolean callable = isCallable( element ); + model.setCustomSQLInsert( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) ); } element = node.element( "sql-delete" ); if ( element != null ) { - boolean callable = false; - callable = isCallable( element ); - model.setCustomSQLDelete( element.getTextTrim(), callable ); + boolean callable = isCallable( element ); + model.setCustomSQLDelete( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) ); } element = node.element( "sql-update" ); if ( element != null ) { - boolean callable = false; - callable = isCallable( element ); - model.setCustomSQLUpdate( element.getTextTrim(), callable ); + boolean callable = isCallable( element ); + model.setCustomSQLUpdate( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) ); } element = node.element( "loader" ); @@ -696,53 +694,46 @@ private static void handleCustomSQL(Element node, Join model) throws MappingException { Element element = node.element( "sql-insert" ); if ( element != null ) { - boolean callable = false; - callable = isCallable( element ); - model.setCustomSQLInsert( element.getTextTrim(), callable ); + boolean callable = isCallable( element ); + model.setCustomSQLInsert( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) ); } element = node.element( "sql-delete" ); if ( element != null ) { - boolean callable = false; - callable = isCallable( element ); - model.setCustomSQLDelete( element.getTextTrim(), callable ); + boolean callable = isCallable( element ); + model.setCustomSQLDelete( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) ); } element = node.element( "sql-update" ); if ( element != null ) { - boolean callable = false; - callable = isCallable( element ); - model.setCustomSQLUpdate( element.getTextTrim(), callable ); + boolean callable = isCallable( element ); + model.setCustomSQLUpdate( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) ); } } private static void handleCustomSQL(Element node, Collection model) throws MappingException { Element element = node.element( "sql-insert" ); if ( element != null ) { - boolean callable = false; - callable = isCallable( element, true ); - model.setCustomSQLInsert( element.getTextTrim(), callable ); + boolean callable = isCallable( element, true ); + model.setCustomSQLInsert( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) ); } element = node.element( "sql-delete" ); if ( element != null ) { - boolean callable = false; - callable = isCallable( element, true ); - model.setCustomSQLDelete( element.getTextTrim(), callable ); + boolean callable = isCallable( element, true ); + model.setCustomSQLDelete( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) ); } element = node.element( "sql-update" ); if ( element != null ) { - boolean callable = false; - callable = isCallable( element, true ); - model.setCustomSQLUpdate( element.getTextTrim(), callable ); + boolean callable = isCallable( element, true ); + model.setCustomSQLUpdate( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) ); } element = node.element( "sql-delete-all" ); if ( element != null ) { - boolean callable = false; - callable = isCallable( element, true ); - model.setCustomSQLDeleteAll( element.getTextTrim(), callable ); + boolean callable = isCallable( element, true ); + model.setCustomSQLDeleteAll( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) ); } } @@ -762,6 +753,16 @@ return false; } + private static ExecuteUpdateResultCheckStyle getResultCheckStyle(Element element, boolean callable) throws MappingException { + Attribute attr = element.attribute( "check" ); + if ( attr == null ) { + // use COUNT as the default. This mimics the old behavior, although + // NONE might be a better option moving forward in the case of callable + return ExecuteUpdateResultCheckStyle.COUNT; + } + return ExecuteUpdateResultCheckStyle.parse( attr.getValue() ); + } + public static void bindUnionSubclass(Element node, UnionSubclass unionSubclass, Mappings mappings, java.util.Map inheritedMetas) throws MappingException { Added: trunk/Hibernate3/src/org/hibernate/engine/ExecuteUpdateResultCheckStyle.java =================================================================== --- trunk/Hibernate3/src/org/hibernate/engine/ExecuteUpdateResultCheckStyle.java 2006-06-21 22:29:24 UTC (rev 10039) +++ trunk/Hibernate3/src/org/hibernate/engine/ExecuteUpdateResultCheckStyle.java 2006-06-22 19:51:43 UTC (rev 10040) @@ -0,0 +1,73 @@ +package org.hibernate.engine; + +import java.io.Serializable; +import java.io.ObjectStreamException; +import java.io.InvalidObjectException; + +/** + * For persistence operations (INSERT, UPDATE, DELETE) what style of determining + * results (success/failure) is to be used. + * + * @author Steve Ebersole + */ +public class ExecuteUpdateResultCheckStyle implements Serializable { + /** + * Do not perform checking. Either user simply does not want checking, or is + * indicating a {@link java.sql.CallableStatement} execution in which the + * checks are being performed explicitly and failures are handled through + * propogation of {@link java.sql.SQLException}s. + */ + public static final ExecuteUpdateResultCheckStyle NONE = new ExecuteUpdateResultCheckStyle( "none" ); + /** + * Perform row-count checking. Row counts are the int values returned by both + * {@link java.sql.PreparedStatement#executeUpdate()} and + * {@link java.sql.Statement#executeBatch()}. These values are checked + * against some expected count. + */ + public static final ExecuteUpdateResultCheckStyle COUNT = new ExecuteUpdateResultCheckStyle( "rowcount" ); + /** + * Essentially the same as {@link #COUNT} except that the row count actually + * comes from an output parameter registered as part of a + * {@link java.sql.CallableStatement}. This style explicitly prohibits + * statement batching from being used... + */ + public static final ExecuteUpdateResultCheckStyle PARAM = new ExecuteUpdateResultCheckStyle( "param" ); + + private final String name; + + private ExecuteUpdateResultCheckStyle(String name) { + this.name = name; + } + + private Object readResolve() throws ObjectStreamException { + Object resolved = parse( name ); + if ( resolved == null ) { + throw new InvalidObjectException( "unknown result style [" + name + "]" ); + } + return resolved; + } + + public static ExecuteUpdateResultCheckStyle parse(String name) { + if ( name.equals( NONE.name ) ) { + return NONE; + } + else if ( name.equals( COUNT.name ) ) { + return COUNT; + } + else if ( name.equals( PARAM.name ) ) { + return PARAM; + } + else { + return null; + } + } + + public static ExecuteUpdateResultCheckStyle determineDefault(String customSql, boolean callable) { + if ( customSql == null ) { + return COUNT; + } + else { + return callable ? PARAM : COUNT; + } + } +} Modified: trunk/Hibernate3/src/org/hibernate/hibernate-mapping-3.0.dtd =================================================================== --- trunk/Hibernate3/src/org/hibernate/hibernate-mapping-3.0.dtd 2006-06-21 22:29:24 UTC (rev 10039) +++ trunk/Hibernate3/src/org/hibernate/hibernate-mapping-3.0.dtd 2006-06-22 19:51:43 UTC (rev 10040) @@ -377,7 +377,7 @@ <!-- Declares a one-to-one association between two entities (Or from a component, component element, etc. to an entity). --> -<!ELEMENT one-to-one (meta*|formula*)> +<!ELEMENT one-to-one (meta*,formula*)> <!ATTLIST one-to-one name CDATA #REQUIRED> <!ATTLIST one-to-one formula CDATA #IMPLIED> <!ATTLIST one-to-one access CDATA #IMPLIED> @@ -990,16 +990,20 @@ <!-- custom sql operations --> <!ELEMENT sql-insert (#PCDATA)> - <!ATTLIST sql-insert callable (true|false) "false"> + <!ATTLIST sql-insert callable (true|false) "false"> + <!ATTLIST sql-insert check (none|rowcount|param) #IMPLIED> <!ELEMENT sql-update (#PCDATA)> - <!ATTLIST sql-update callable (true|false) "false"> + <!ATTLIST sql-update callable (true|false) "false"> + <!ATTLIST sql-update check (none|rowcount|param) #IMPLIED> <!ELEMENT sql-delete (#PCDATA)> - <!ATTLIST sql-delete callable (true|false) "false"> + <!ATTLIST sql-delete callable (true|false) "false"> + <!ATTLIST sql-delete check (none|rowcount|param) #IMPLIED> <!ELEMENT sql-delete-all (#PCDATA)> <!ATTLIST sql-delete-all callable (true|false) "false"> + <!ATTLIST sql-delete-all check (none|rowcount|param) #IMPLIED> <!-- Element for defining "auxiliary" database objects. Must be one of two forms: Modified: trunk/Hibernate3/src/org/hibernate/jdbc/Batcher.java =================================================================== --- trunk/Hibernate3/src/org/hibernate/jdbc/Batcher.java 2006-06-21 22:29:24 UTC (rev 10039) +++ trunk/Hibernate3/src/org/hibernate/jdbc/Batcher.java 2006-06-22 19:51:43 UTC (rev 10040) @@ -86,7 +86,7 @@ * (might be called many times before a single call to <tt>executeBatch()</tt>). * After setting parameters, call <tt>addToBatch</tt> - do not execute the * statement explicitly. - * @see Batcher#addToBatch(int) + * @see Batcher#addToBatch */ public PreparedStatement prepareBatchStatement(String sql) throws SQLException, HibernateException; @@ -95,7 +95,7 @@ * (might be called many times before a single call to <tt>executeBatch()</tt>). * After setting parameters, call <tt>addToBatch</tt> - do not execute the * statement explicitly. - * @see Batcher#addToBatch(int) + * @see Batcher#addToBatch */ public CallableStatement prepareBatchCallableStatement(String sql) throws SQLException, HibernateException; @@ -103,7 +103,7 @@ * Add an insert / delete / update to the current batch (might be called multiple times * for single <tt>prepareBatchStatement()</tt>) */ - public void addToBatch(int expectedRowCount) throws SQLException, HibernateException; + public void addToBatch(Expectation expectation) throws SQLException, HibernateException; /** * Execute the batch Modified: trunk/Hibernate3/src/org/hibernate/jdbc/BatchingBatcher.java =================================================================== --- trunk/Hibernate3/src/org/hibernate/jdbc/BatchingBatcher.java 2006-06-21 22:29:24 UTC (rev 10039) +++ trunk/Hibernate3/src/org/hibernate/jdbc/BatchingBatcher.java 2006-06-22 19:51:43 UTC (rev 10040) @@ -16,96 +16,58 @@ public class BatchingBatcher extends AbstractBatcher { private int batchSize; - private int[] expectedRowCounts; + private Expectation[] expectations; public BatchingBatcher(ConnectionManager connectionManager, Interceptor interceptor) { - super( connectionManager, interceptor ); - expectedRowCounts = new int[ getFactory().getSettings().getJdbcBatchSize() ]; + super( connectionManager, interceptor ); + expectations = new Expectation[ getFactory().getSettings().getJdbcBatchSize() ]; } - public void addToBatch(int expectedRowCount) throws SQLException, HibernateException { - - log.trace("Adding to batch"); + public void addToBatch(Expectation expectation) throws SQLException, HibernateException { + if ( !expectation.canBeBatched() ) { + throw new HibernateException( "attempting to batch an operation which cannot be batched" ); + } PreparedStatement batchUpdate = getStatement(); batchUpdate.addBatch(); - expectedRowCounts[ batchSize++ ] = expectedRowCount; - if ( batchSize==getFactory().getSettings().getJdbcBatchSize() ) { - //try { - doExecuteBatch(batchUpdate); - /*} - catch (SQLException sqle) { - closeStatement(batchUpdate); - throw sqle; - } - catch (HibernateException he) { - closeStatement(batchUpdate); - throw he; - }*/ + expectations[ batchSize++ ] = expectation; + if ( batchSize == getFactory().getSettings().getJdbcBatchSize() ) { + doExecuteBatch( batchUpdate ); } - } protected void doExecuteBatch(PreparedStatement ps) throws SQLException, HibernateException { - - if (batchSize==0) { - log.debug("no batched statements to execute"); + if ( batchSize == 0 ) { + log.debug( "no batched statements to execute" ); } else { - - if ( log.isDebugEnabled() ) log.debug("Executing batch size: " + batchSize ); - + if ( log.isDebugEnabled() ) { + log.debug( "Executing batch size: " + batchSize ); + } + try { - checkRowCounts( ps.executeBatch() ); + checkRowCounts( ps.executeBatch(), ps ); } catch (RuntimeException re) { - log.error("Exception executing batch: ", re); + log.error( "Exception executing batch: ", re ); throw re; } finally { - batchSize=0; - //ps.clearBatch(); + batchSize = 0; } - + } } - private void checkRowCounts(int[] rowCounts) { - int rowCountLength = rowCounts.length; - if ( rowCountLength!=batchSize ) { - log.warn("JDBC driver did not return the expected number of row counts"); + private void checkRowCounts(int[] rowCounts, PreparedStatement ps) throws SQLException, HibernateException { + int numberOfRowCounts = rowCounts.length; + if ( numberOfRowCounts != batchSize ) { + log.warn( "JDBC driver did not return the expected number of row counts" ); } - for ( int i=0; i<rowCountLength; i++ ) { - checkRowCount( rowCounts[i], expectedRowCounts[i], i ); + for ( int i = 0; i < numberOfRowCounts; i++ ) { + expectations[i].verifyOutcome( rowCounts[i], ps, i ); } } - - private void checkRowCount(int rowCount, int expectedRowCount, int i) { - if ( rowCount==-2 ) { - if ( log.isDebugEnabled() ) log.debug("success of batch update unknown: " + i); - } - else if ( rowCount==-3 ) { - throw new HibernateException("Batch update failed: " + i); - } - else { - if ( expectedRowCount>=0 ) { - if ( rowCount<expectedRowCount ) { - throw new StaleStateException( - "Batch update returned unexpected row count from update: " + i + - " actual row count: " + rowCount + - " expected: " + expectedRowCount - ); - } - if ( rowCount>expectedRowCount ) { - throw new HibernateException( - "Batch update returned unexpected row count from update: " + i + - " actual row count: " + rowCount + - " expected: " + expectedRowCount - ); - } - } - } - } } Added: trunk/Hibernate3/src/org/hibernate/jdbc/Expectation.java =================================================================== --- trunk/Hibernate3/src/org/hibernate/jdbc/Expectation.java 2006-06-21 22:29:24 UTC (rev 10039) +++ trunk/Hibernate3/src/org/hibernate/jdbc/Expectation.java 2006-06-22 19:51:43 UTC (rev 10040) @@ -0,0 +1,17 @@ +package org.hibernate.jdbc; + +import org.hibernate.HibernateException; + +import java.sql.SQLException; +import java.sql.PreparedStatement; + +/** + * Defines an expected DML operation outcome. + * + * @author Steve Ebersole + */ +public interface Expectation { + public void verifyOutcome(int rowCount, PreparedStatement statement, int batchPosition) throws SQLException, HibernateException; + public int prepare(PreparedStatement statement) throws SQLException, HibernateException; + public boolean canBeBatched(); +} Added: trunk/Hibernate3/src/org/hibernate/jdbc/Expectations.java =================================================================== --- trunk/Hibernate3/src/org/hibernate/jdbc/Expectations.java 2006-06-21 22:29:24 UTC (rev 10039) +++ trunk/Hibernate3/src/org/hibernate/jdbc/Expectations.java 2006-06-22 19:51:43 UTC (rev 10040) @@ -0,0 +1,176 @@ +package org.hibernate.jdbc; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.hibernate.StaleStateException; +import org.hibernate.HibernateException; +import org.hibernate.engine.ExecuteUpdateResultCheckStyle; +import org.hibernate.util.JDBCExceptionReporter; +import org.hibernate.exception.GenericJDBCException; + +import java.sql.CallableStatement; +import java.sql.SQLException; +import java.sql.PreparedStatement; +import java.sql.Types; + +/** + * Holds various often used {@link Expectation} definitions. + * + * @author Steve Ebersole + */ +public class Expectations { + private static final Log log = LogFactory.getLog( Expectations.class ); + + public static final int USUAL_EXPECTED_COUNT = 1; + public static final int USUAL_PARAM_POSITION = 1; + + + // Base Expectation impls ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + public static class BasicExpectation implements Expectation { + private final int expectedRowCount; + + protected BasicExpectation(int expectedRowCount) { + this.expectedRowCount = expectedRowCount; + if ( expectedRowCount < 0 ) { + throw new IllegalArgumentException( "Expected row count must be greater than zero" ); + } + } + + public final void verifyOutcome(int rowCount, PreparedStatement statement, int batchPosition) { + rowCount = determineRowCount( rowCount, statement ); + if ( batchPosition < 0 ) { + checkNonBatched( rowCount ); + } + else { + checkBatched( rowCount, batchPosition ); + } + } + + private void checkBatched(int rowCount, int batchPosition) { + if ( rowCount == -2 ) { + if ( log.isDebugEnabled() ) { + log.debug( "success of batch update unknown: " + batchPosition ); + } + } + else if ( rowCount == -3 ) { + throw new HibernateException( "Batch update failed: " + batchPosition ); + } + else { + if ( expectedRowCount > rowCount ) { + throw new StaleStateException( + "Batch update returned unexpected row count from update [" + batchPosition + + "]; actual row count: " + rowCount + + "; expected: " + expectedRowCount + ); + } + if ( expectedRowCount < rowCount ) { + throw new HibernateException( + "Batch update returned unexpected row count from update [" + batchPosition + + "]; actual row count: " + rowCount + + "; expected: " + expectedRowCount + ); + } + } + } + + private void checkNonBatched(int rowCount) { + if ( expectedRowCount > rowCount ) { + throw new StaleStateException( + "Unexpected row count: " + rowCount + "; expected: " + expectedRowCount + ); + } + if ( expectedRowCount < rowCount ) { + throw new HibernateException( + "Unexpected row count: " + rowCount + "; expected: " + expectedRowCount + ); + } + } + + public int prepare(PreparedStatement statement) throws SQLException, HibernateException { + return 0; + } + + public boolean canBeBatched() { + return true; + } + + protected int determineRowCount(int reportedRowCount, PreparedStatement statement) { + return reportedRowCount; + } + } + + public static class BasicParamExpectation extends BasicExpectation { + private final int parameterPosition; + protected BasicParamExpectation(int expectedRowCount, int parameterPosition) { + super( expectedRowCount ); + this.parameterPosition = parameterPosition; + } + + public int prepare(PreparedStatement statement) throws SQLException, HibernateException { + toCallableStatement( statement ).registerOutParameter( parameterPosition, Types.NUMERIC ); + return 1; + } + + public boolean canBeBatched() { + return false; + } + + protected int determineRowCount(int reportedRowCount, PreparedStatement statement) { + try { + return toCallableStatement( statement ).getInt( parameterPosition ); + } + catch( SQLException sqle ) { + JDBCExceptionReporter.logExceptions( sqle, "could not extract row counts from CallableStatement" ); + throw new GenericJDBCException( "could not extract row counts from CallableStatement", sqle ); + } + } + + private CallableStatement toCallableStatement(PreparedStatement statement) { + if ( ! CallableStatement.class.isInstance( statement ) ) { + throw new HibernateException( "BasicParamExpectation operates exclusively on CallableStatements : " + statement.getClass() ); + } + return ( CallableStatement ) statement; + } + } + + + // Various Expectation instances ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + public static final Expectation NONE = new Expectation() { + public void verifyOutcome(int rowCount, PreparedStatement statement, int batchPosition) { + // explicitly perform no checking... + } + + public int prepare(PreparedStatement statement) { + return 0; + } + + public boolean canBeBatched() { + return true; + } + }; + + public static final Expectation BASIC = new BasicExpectation( USUAL_EXPECTED_COUNT ); + + public static final Expectation PARAM = new BasicParamExpectation( USUAL_EXPECTED_COUNT, USUAL_PARAM_POSITION ); + + + public static Expectation appropriateExpectation(ExecuteUpdateResultCheckStyle style) { + if ( style == ExecuteUpdateResultCheckStyle.NONE ) { + return NONE; + } + else if ( style == ExecuteUpdateResultCheckStyle.COUNT ) { + return BASIC; + } + else if ( style == ExecuteUpdateResultCheckStyle.PARAM ) { + return PARAM; + } + else { + throw new HibernateException( "unknown check style : " + style ); + } + } + + private Expectations() { + } +} Modified: trunk/Hibernate3/src/org/hibernate/jdbc/NonBatchingBatcher.java =================================================================== --- trunk/Hibernate3/src/org/hibernate/jdbc/NonBatchingBatcher.java 2006-06-21 22:29:24 UTC (rev 10039) +++ trunk/Hibernate3/src/org/hibernate/jdbc/NonBatchingBatcher.java 2006-06-22 19:51:43 UTC (rev 10040) @@ -6,7 +6,6 @@ import org.hibernate.HibernateException; import org.hibernate.Interceptor; -import org.hibernate.StaleStateException; /** * An implementation of the <tt>Batcher</tt> interface that does no batching @@ -19,23 +18,10 @@ super( connectionManager, interceptor ); } - public void addToBatch(int expectedRowCount) throws SQLException, HibernateException { - final int rowCount = getStatement().executeUpdate(); - //negative expected row count means we don't know how many rows to expect - if ( expectedRowCount>0 ) { - if ( expectedRowCount>rowCount ) { - throw new StaleStateException( - "Unexpected row count: " + rowCount + - " expected: " + expectedRowCount - ); - } - if ( expectedRowCount<rowCount ) { - throw new HibernateException( - "Unexpected row count: " + rowCount + - " expected: " + expectedRowCount - ); - } - } + public void addToBatch(Expectation expectation) throws SQLException, HibernateException { + PreparedStatement statement = getStatement(); + final int rowCount = statement.executeUpdate(); + expectation.verifyOutcome( rowCount, statement, 0 ); } protected void doExecuteBatch(PreparedStatement ps) throws SQLException, HibernateException { Modified: trunk/Hibernate3/src/org/hibernate/mapping/Collection.java =================================================================== --- trunk/Hibernate3/src/org/hibernate/mapping/Collection.java 2006-06-21 22:29:24 UTC (rev 10039) +++ trunk/Hibernate3/src/org/hibernate/mapping/Collection.java 2006-06-22 19:51:43 UTC (rev 10040) @@ -9,6 +9,7 @@ import org.hibernate.FetchMode; import org.hibernate.MappingException; import org.hibernate.engine.Mapping; +import org.hibernate.engine.ExecuteUpdateResultCheckStyle; import org.hibernate.type.CollectionType; import org.hibernate.type.Type; import org.hibernate.type.TypeFactory; @@ -60,13 +61,17 @@ private final java.util.Set synchronizedTables = new HashSet(); private String customSQLInsert; + private boolean customInsertCallable; + private ExecuteUpdateResultCheckStyle insertCheckStyle; private String customSQLUpdate; + private boolean customUpdateCallable; + private ExecuteUpdateResultCheckStyle updateCheckStyle; private String customSQLDelete; + private boolean customDeleteCallable; + private ExecuteUpdateResultCheckStyle deleteCheckStyle; private String customSQLDeleteAll; - private boolean customInsertCallable; - private boolean customUpdateCallable; - private boolean customDeleteCallable; private boolean customDeleteAllCallable; + private ExecuteUpdateResultCheckStyle deleteAllCheckStyle; private String loaderName; @@ -402,58 +407,80 @@ this.cacheRegionName = cacheRegionName; } - public String getCustomSQLDelete() { - return customSQLDelete; - } - public void setCustomSQLDelete(String customSQLDelete, boolean callable) { - this.customSQLDelete = customSQLDelete; - this.customDeleteCallable = callable; + + public void setCustomSQLInsert(String customSQLInsert, boolean callable, ExecuteUpdateResultCheckStyle checkStyle) { + this.customSQLInsert = customSQLInsert; + this.customInsertCallable = callable; + this.insertCheckStyle = checkStyle; } - public String getCustomSQLDeleteAll() { - return customSQLDeleteAll; + public String getCustomSQLInsert() { + return customSQLInsert; } - public void setCustomSQLDeleteAll(String customSQLDeleteAll, boolean callable) { - this.customSQLDeleteAll = customSQLDeleteAll; - this.customDeleteAllCallable = callable; + public boolean isCustomInsertCallable() { + return customInsertCallable; } - public String getCustomSQLInsert() { - return customSQLInsert; + public ExecuteUpdateResultCheckStyle getCustomSQLInsertCheckStyle() { + return insertCheckStyle; } - public void setCustomSQLInsert(String customSQLInsert, boolean callable) { - this.customSQLInsert = customSQLInsert; - this.customInsertCallable = callable; + public void setCustomSQLUpdate(String customSQLUpdate, boolean callable, ExecuteUpdateResultCheckStyle checkStyle) { + this.customSQLUpdate = customSQLUpdate; + this.customUpdateCallable = callable; + this.updateCheckStyle = checkStyle; } public String getCustomSQLUpdate() { return customSQLUpdate; } - public void setCustomSQLUpdate(String customSQLUpdate, boolean callable) { - this.customSQLUpdate = customSQLUpdate; - this.customUpdateCallable = callable; + public boolean isCustomUpdateCallable() { + return customUpdateCallable; } + public ExecuteUpdateResultCheckStyle getCustomSQLUpdateCheckStyle() { + return updateCheckStyle; + } + + public void setCustomSQLDelete(String customSQLDelete, boolean callable, ExecuteUpdateResultCheckStyle checkStyle) { + this.customSQLDelete = customSQLDelete; + this.customDeleteCallable = callable; + this.deleteCheckStyle = checkStyle; + } + + public String getCustomSQLDelete() { + return customSQLDelete; + } + public boolean isCustomDeleteCallable() { return customDeleteCallable; } + public ExecuteUpdateResultCheckStyle getCustomSQLDeleteCheckStyle() { + return deleteCheckStyle; + } + + public void setCustomSQLDeleteAll(String customSQLDeleteAll, boolean callable, ExecuteUpdateResultCheckStyle checkStyle) { + this.customSQLDeleteAll = customSQLDeleteAll; + this.customDeleteAllCallable = callable; + this.deleteAllCheckStyle = checkStyle; + } + + public String getCustomSQLDeleteAll() { + return customSQLDeleteAll; + } + public boolean isCustomDeleteAllCallable() { return customDeleteAllCallable; } - public boolean isCustomInsertCallable() { - return customInsertCallable; + public ExecuteUpdateResultCheckStyle getCustomSQLDeleteAllCheckStyle() { + return deleteAllCheckStyle; } - public boolean isCustomUpdateCallable() { - return customUpdateCallable; - } - public void addFilter(String name, String condition) { filters.put( name, condition ); } Modified: trunk/Hibernate3/src/org/hibernate/mapping/Join.java =================================================================== --- trunk/Hibernate3/src/org/hibernate/mapping/Join.java 2006-06-21 22:29:24 UTC (rev 10039) +++ trunk/Hibernate3/src/org/hibernate/mapping/Join.java 2006-06-22 19:51:43 UTC (rev 10040) @@ -6,6 +6,7 @@ import java.util.Iterator; import org.hibernate.sql.Alias; +import org.hibernate.engine.ExecuteUpdateResultCheckStyle; /** * @author Gavin King @@ -24,11 +25,14 @@ // Custom SQL private String customSQLInsert; + private boolean customInsertCallable; + private ExecuteUpdateResultCheckStyle insertCheckStyle; private String customSQLUpdate; + private boolean customUpdateCallable; + private ExecuteUpdateResultCheckStyle updateCheckStyle; private String customSQLDelete; - private boolean customInsertCallable; - private boolean customUpdateCallable; private boolean customDeleteCallable; + private ExecuteUpdateResultCheckStyle deleteCheckStyle; public void addProperty(Property prop) { properties.add(prop); @@ -81,44 +85,60 @@ return properties.size(); } - public String getCustomSQLDelete() { - return customSQLDelete; + public void setCustomSQLInsert(String customSQLInsert, boolean callable, ExecuteUpdateResultCheckStyle checkStyle) { + this.customSQLInsert = customSQLInsert; + this.customInsertCallable = callable; + this.insertCheckStyle = checkStyle; } - public void setCustomSQLDelete(String customSQLDelete, boolean callable) { - this.customSQLDelete = customSQLDelete; - this.customDeleteCallable = callable; - } - public String getCustomSQLInsert() { return customSQLInsert; } - public void setCustomSQLInsert(String customSQLInsert, boolean callable) { - this.customSQLInsert = customSQLInsert; - this.customInsertCallable = callable; + public boolean isCustomInsertCallable() { + return customInsertCallable; } - public String getCustomSQLUpdate() { - return customSQLUpdate; + public ExecuteUpdateResultCheckStyle getCustomSQLInsertCheckStyle() { + return insertCheckStyle; } - public void setCustomSQLUpdate(String customSQLUpdate, boolean callable) { + public void setCustomSQLUpdate(String customSQLUpdate, boolean callable, ExecuteUpdateResultCheckStyle checkStyle) { this.customSQLUpdate = customSQLUpdate; this.customUpdateCallable = callable; + this.updateCheckStyle = checkStyle; } + public String getCustomSQLUpdate() { + return customSQLUpdate; + } + + public boolean isCustomUpdateCallable() { + return customUpdateCallable; + } + + public ExecuteUpdateResultCheckStyle getCustomSQLUpdateCheckStyle() { + return updateCheckStyle; + } + + public void setCustomSQLDelete(String customSQLDelete, boolean callable, ExecuteUpdateResultCheckStyle checkStyle) { + this.customSQLDelete = customSQLDelete; + this.customDeleteCallable = callable; + this.deleteCheckStyle = checkStyle; + } + + public String getCustomSQLDelete() { + return customSQLDelete; + } + public boolean isCustomDeleteCallable() { return customDeleteCallable; } - public boolean isCustomInsertCallable() { - return customInsertCallable; + public ExecuteUpdateResultCheckStyle getCustomSQLDeleteCheckStyle() { + return deleteCheckStyle; } - public boolean isCustomUpdateCallable() { - return customUpdateCallable; - } public boolean isSequentialSelect() { return sequentialSelect; } @@ -129,7 +149,7 @@ public boolean isInverse() { return inverse; } - + public void setInverse(boolean leftJoin) { this.inverse = leftJoin; } @@ -137,7 +157,7 @@ public String toString() { return getClass().getName() + '(' + table.toString() + ')'; } - + public boolean isLazy() { Iterator iter = getPropertyIterator(); while ( iter.hasNext() ) { @@ -146,7 +166,7 @@ } return true; } - + public boolean isOptional() { return optional; } Modified: trunk/Hibernate3/src/org/hibernate/mapping/PersistentClass.java =================================================================== --- trunk/Hibernate3/src/org/hibernate/mapping/PersistentClass.java 2006-06-21 22:29:24 UTC (rev 10039) +++ trunk/Hibernate3/src/org/hibernate/mapping/PersistentClass.java 2006-06-22 19:51:43 UTC (rev 10040) @@ -13,6 +13,7 @@ import org.hibernate.EntityMode; import org.hibernate.dialect.Dialect; import org.hibernate.engine.Mapping; +import org.hibernate.engine.ExecuteUpdateResultCheckStyle; import org.hibernate.sql.Alias; import org.hibernate.util.EmptyIterator; import org.hibernate.util.JoinedIterator; @@ -61,11 +62,14 @@ // Custom SQL private String customSQLInsert; + private boolean customInsertCallable; + private ExecuteUpdateResultCheckStyle insertCheckStyle; private String customSQLUpdate; + private boolean customUpdateCallable; + private ExecuteUpdateResultCheckStyle updateCheckStyle; private String customSQLDelete; - private boolean customInsertCallable; - private boolean customUpdateCallable; private boolean customDeleteCallable; + private ExecuteUpdateResultCheckStyle deleteCheckStyle; private String temporaryIdTableName; private String temporaryIdTableDDL; @@ -490,9 +494,10 @@ return properties.iterator(); } - public void setCustomSQLInsert(String customSQLInsert, boolean callable) { + public void setCustomSQLInsert(String customSQLInsert, boolean callable, ExecuteUpdateResultCheckStyle checkStyle) { this.customSQLInsert = customSQLInsert; this.customInsertCallable = callable; + this.insertCheckStyle = checkStyle; } public String getCustomSQLInsert() { @@ -503,9 +508,14 @@ return customInsertCallable; } - public void setCustomSQLUpdate(String customSQLUpdate, boolean callable) { + public ExecuteUpdateResultCheckStyle getCustomSQLInsertCheckStyle() { + return insertCheckStyle; + } + + public void setCustomSQLUpdate(String customSQLUpdate, boolean callable, ExecuteUpdateResultCheckStyle checkStyle) { this.customSQLUpdate = customSQLUpdate; this.customUpdateCallable = callable; + this.updateCheckStyle = checkStyle; } public String getCustomSQLUpdate() { @@ -516,9 +526,14 @@ return customUpdateCallable; } - public void setCustomSQLDelete(String customSQLDelete, boolean callable) { + public ExecuteUpdateResultCheckStyle getCustomSQLUpdateCheckStyle() { + return updateCheckStyle; + } + + public void setCustomSQLDelete(String customSQLDelete, boolean callable, ExecuteUpdateResultCheckStyle checkStyle) { this.customSQLDelete = customSQLDelete; this.customDeleteCallable = callable; + this.deleteCheckStyle = checkStyle; } public String getCustomSQLDelete() { @@ -529,6 +544,10 @@ return customDeleteCallable; } + public ExecuteUpdateResultCheckStyle getCustomSQLDeleteCheckStyle() { + return deleteCheckStyle; + } + public void addFilter(String name, String condition) { filters.put(name, condition); } Modified: trunk/Hibernate3/src/org/hibernate/persister/collection/AbstractCollectionPersister.java =================================================================== --- trunk/Hibernate3/src/org/hibernate/persister/collection/AbstractCollectionPersister.java 2006-06-21 22:29:24 UTC (rev 10039) +++ trunk/Hibernate3/src/org/hibernate/persister/collection/AbstractCollectionPersister.java 2006-06-22 19:51:43 UTC (rev 10040) @@ -2,11 +2,9 @@ package org.hibernate.persister.collection; import java.io.Serializable; -import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.Types; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; @@ -20,6 +18,8 @@ import org.hibernate.MappingException; import org.hibernate.QueryException; import org.hibernate.TransientObjectException; +import org.hibernate.jdbc.Expectation; +import org.hibernate.jdbc.Expectations; import org.hibernate.cache.CacheConcurrencyStrategy; import org.hibernate.cache.CacheException; import org.hibernate.cache.entry.CacheEntryStructure; @@ -34,7 +34,7 @@ import org.hibernate.engine.SessionFactoryImplementor; import org.hibernate.engine.SessionImplementor; import org.hibernate.engine.SubselectFetch; -import org.hibernate.engine.ForeignKeys; +import org.hibernate.engine.ExecuteUpdateResultCheckStyle; import org.hibernate.exception.JDBCExceptionHelper; import org.hibernate.exception.SQLExceptionConverter; import org.hibernate.id.IdentifierGenerator; @@ -181,6 +181,10 @@ private final boolean updateCallable; private final boolean deleteCallable; private final boolean deleteAllCallable; + private ExecuteUpdateResultCheckStyle insertCheckStyle; + private ExecuteUpdateResultCheckStyle updateCheckStyle; + private ExecuteUpdateResultCheckStyle deleteCheckStyle; + private ExecuteUpdateResultCheckStyle deleteAllCheckStyle; private final Serializable[] spaces; @@ -418,40 +422,56 @@ //GENERATE THE SQL: //sqlSelectString = sqlSelectString(); - if ( collection.getCustomSQLDeleteAll() == null ) { - sqlDeleteString = generateDeleteString(); - deleteAllCallable = false; - } - else { - sqlDeleteString = collection.getCustomSQLDeleteAll(); - deleteAllCallable = collection.isCustomDeleteAllCallable(); - } //sqlSelectRowString = sqlSelectRowString(); + if ( collection.getCustomSQLInsert() == null ) { sqlInsertRowString = generateInsertRowString(); insertCallable = false; + insertCheckStyle = ExecuteUpdateResultCheckStyle.COUNT; } else { sqlInsertRowString = collection.getCustomSQLInsert(); insertCallable = collection.isCustomInsertCallable(); + insertCheckStyle = collection.getCustomSQLInsertCheckStyle() == null + ? ExecuteUpdateResultCheckStyle.determineDefault( collection.getCustomSQLInsert(), insertCallable ) + : collection.getCustomSQLInsertCheckStyle(); } if ( collection.getCustomSQLUpdate() == null ) { sqlUpdateRowString = generateUpdateRowString(); updateCallable = false; + updateCheckStyle = ExecuteUpdateResultCheckStyle.COUNT; } else { sqlUpdateRowString = collection.getCustomSQLUpdate(); updateCallable = collection.isCustomUpdateCallable(); + updateCheckStyle = collection.getCustomSQLUpdateCheckStyle() == null + ? ExecuteUpdateResultCheckStyle.determineDefault( collection.getCustomSQLUpdate(), insertCallable ) + : collection.getCustomSQLUpdateCheckStyle(); } + if ( collection.getCustomSQLDelete() == null ) { sqlDeleteRowString = generateDeleteRowString(); deleteCallable = false; + deleteCheckStyle = ExecuteUpdateResultCheckStyle.NONE; } else { sqlDeleteRowString = collection.getCustomSQLDelete(); deleteCallable = collection.isCustomDeleteCallable(); + deleteCheckStyle = ExecuteUpdateResultCheckStyle.NONE; } + + if ( collection.getCustomSQLDeleteAll() == null ) { + sqlDeleteString = generateDeleteString(); + deleteAllCallable = false; + deleteAllCheckStyle = ExecuteUpdateResultCheckStyle.NONE; + } + else { + sqlDeleteString = collection.getCustomSQLDeleteAll(); + deleteAllCallable = collection.isCustomDeleteAllCallable(); + deleteAllCheckStyle = ExecuteUpdateResultCheckStyle.NONE; + } + sqlSelectSizeString = generateSelectSizeString( collection.isIndexed() && !collection.isMap() ); sqlDetectRowByIndexString = generateDetectRowByIndexString(); sqlDetectRowByElementString = generateDetectRowByElementString(); @@ -678,8 +698,7 @@ public Object readElement(ResultSet rs, Object owner, String[] aliases, SessionImplementor session) throws HibernateException, SQLException { - Object element = getElementType().nullSafeGet( rs, aliases, session, owner ); - return element; + return getElementType().nullSafeGet( rs, aliases, session, owner ); } public Object readIndex(ResultSet rs, String[] aliases, SessionImplementor session) @@ -891,10 +910,9 @@ } protected SelectFragment generateSelectFragment(String alias, String columnSuffix) { - SelectFragment frag = new SelectFragment() + return new SelectFragment() .setSuffix( columnSuffix ) .addColumns( alias, keyColumnNames, keyColumnAliases ); - return frag; } protected void appendElementColumns(SelectFragment frag, String elemAlias) { @@ -944,7 +962,7 @@ return qualify(alias, elementColumnNames, elementFormulaTemplates); } - private static final String[] qualify(String alias, String[] columnNames, String[] formulaTemplates) { + private static String[] qualify(String alias, String[] columnNames, String[] formulaTemplates) { int span = columnNames.length; String[] result = new String[span]; for (int i=0; i<span; i++) { @@ -998,24 +1016,50 @@ try { int offset = 1; PreparedStatement st = null; - if ( isDeleteCallable() ) { - CallableStatement callstatement = session.getBatcher() - .prepareBatchCallableStatement( getSQLDeleteString() ); - callstatement.registerOutParameter( offset++, Types.NUMERIC ); // TODO: should we require users to return number of update rows ? - st = callstatement; + Expectation expectation = Expectations.appropriateExpectation( getDeleteAllCheckStyle() ); + boolean callable = isDeleteAllCallable(); + boolean useBatch = expectation.canBeBatched(); + String sql = getSQLDeleteString(); + if ( useBatch ) { + if ( callable ) { + st = session.getBatcher().prepareBatchCallableStatement( sql ); + } + else { + st = session.getBatcher().prepareBatchStatement( sql ); + } } else { - st = session.getBatcher().prepareBatchStatement( getSQLDeleteString() ); + if ( callable ) { + st = session.getBatcher().prepareCallableStatement( sql ); + } + else { + st = session.getBatcher().prepareStatement( sql ); + } } + try { + offset+= expectation.prepare( st ); + writeKey( st, id, offset, session ); - session.getBatcher().addToBatch( -1 ); + if ( useBatch ) { + session.getBatcher().addToBatch( expectation ); + } + else { + expectation.verifyOutcome( st.executeUpdate(), st, -1 ); + } } catch ( SQLException sqle ) { - session.getBatcher().abortBatch( sqle ); + if ( useBatch ) { + session.getBatcher().abortBatch( sqle ); + } throw sqle; } + finally { + if ( !useBatch ) { + session.getBatcher().closeStatement( st ); + } + } if ( log.isDebugEnabled() ) { log.debug( "done deleting collection" ); @@ -1051,25 +1095,41 @@ //create all the new entries Iterator entries = collection.entries(this); if ( entries.hasNext() ) { - try { - collection.preInsert( this ); - int i = 0; - int count = 0; - while ( entries.hasNext() ) { + collection.preInsert( this ); + int i = 0; + int count = 0; + while ( entries.hasNext() ) { - final Object entry = entries.next(); - if ( collection.entryExists( entry, i ) ) { - int offset = 1; - PreparedStatement st = null; - if ( isInsertCallable() ) { - CallableStatement callstatement = session.getBatcher() - .prepareBatchCallableStatement( getSQLInsertRowString() ); - callstatement.registerOutParameter( offset++, Types.NUMERIC ); // TODO: should we require users to return number of update rows ? - st = callstatement; + final Object entry = entries.next(); + if ( collection.entryExists( entry, i ) ) { + int offset = 1; + PreparedStatement st = null; + Expectation expectation = Expectations.appropriateExpectation( getInsertCheckStyle() ); + boolean callable = isInsertCallable(); + boolean useBatch = expectation.canBeBatched(); + String sql = getSQLInsertRowString(); + + if ( useBatch ) { + if ( callable ) { + st = session.getBatcher().prepareBatchCallableStatement( sql ); } else { - st = session.getBatcher().prepareBatchStatement( getSQLInsertRowString() ); + st = session.getBatcher().prepareBatchStatement( sql ); } + } + else { + if ( callable ) { + st = session.getBatcher().prepareCallableStatement( sql ); + } + else { + st = session.getBatcher().prepareStatement( sql ); + } + } + + + try { + offset+= expectation.prepare( st ); + //TODO: copy/paste from insertRows() int loc = writeKey( st, id, offset, session ); if ( hasIdentifier ) { @@ -1078,22 +1138,36 @@ if ( hasIndex /*&& !indexIsFormula*/ ) { loc = writeIndex( st, collection.getIndex(entry, i, this), loc, session ); } - //if ( !elementIsFormula ) { - loc = writeElement(st, collection.getElement(entry), loc, session ); - //} - session.getBatcher().addToBatch( 1 ); + loc = writeElement(st, collection.getElement(entry), loc, session ); + + if ( useBatch ) { + session.getBatcher().addToBatch( expectation ); + } + else { + expectation.verifyOutcome( st.executeUpdate(), st, -1 ); + } + collection.afterRowInsert( this, entry, i ); count++; } - i++; + catch ( SQLException sqle ) { + if ( useBatch ) { + session.getBatcher().abortBatch( sqle ); + } + throw sqle; + } + finally { + if ( !useBatch ) { + session.getBatcher().closeStatement( st ); + } + } + } - if ( log.isDebugEnabled() ) { - log.debug( "done inserting collection: " + count + " rows inserted" ); - } + i++; } - catch ( SQLException sqle ) { - session.getBatcher().abortBatch( sqle ); - throw sqle; + + if ( log.isDebugEnabled() ) { + log.debug( "done inserting collection: " + count + " rows inserted" ); } } @@ -1139,48 +1213,71 @@ if ( deletes.hasNext() ) { int offset = 1; int count = 0; - PreparedStatement st = null; - if ( isDeleteCallable() ) { - CallableStatement callstatement = session.getBatcher() - .prepareBatchCallableStatement( getSQLDeleteRowString() ); - callstatement.registerOutParameter( offset++, Types.NUMERIC ); // TODO: should we require users to return number of update rows ? - st = callstatement; - } - else { - st = session.getBatcher().prepareBatchStatement( getSQLDeleteRowString() ); - } + while ( deletes.hasNext() ) { + PreparedStatement st = null; + Expectation expectation = Expectations.appropriateExpectation( getDeleteCheckStyle() ); + boolean callable = isDeleteCallable(); + boolean useBatch = expectation.canBeBatched(); + String sql = getSQLDeleteRowString(); - try { - int i=0; - while ( deletes.hasNext() ) { + if ( useBatch ) { + if ( callable ) { + st = session.getBatcher().prepareBatchCallableStatement( sql ); + } + else { + st = session.getBatcher().prepareBatchStatement( sql ); + } + } + else { + if ( callable ) { + st = session.getBatcher().prepareCallableStatement( sql ); + } + else { + st = session.getBatcher().prepareStatement( sql ); + } + } + + try { + expectation.prepare( st ); + Object entry = deletes.next(); int loc = offset; if ( hasIdentifier ) { - loc = writeIdentifier( st, entry, loc, session ); + writeIdentifier( st, entry, loc, session ); } else { - //if ( !isOneToMany() ) { - loc = writeKey( st, id, loc, session ); - //} - if (deleteByIndex) { - loc = writeIndexToWhere( st, entry, loc, session ); + loc = writeKey( st, id, loc, session ); + if ( deleteByIndex ) { + writeIndexToWhere( st, entry, loc, session ); } else { - loc = writeElementToWhere( st, entry, loc, session ); + writeElementToWhere( st, entry, loc, session ); } } - session.getBatcher().addToBatch( -1 ); + + if ( useBatch ) { + session.getBatcher().addToBatch( expectation ); + } + else { + expectation.verifyOutcome( st.executeUpdate(), st, -1 ); + } count++; - i++; } - } - catch ( SQLException sqle ) { - session.getBatcher().abortBatch( sqle ); - throw sqle; - } + catch ( SQLException sqle ) { + if ( useBatch ) { + session.getBatcher().abortBatch( sqle ); + } + throw sqle; + } + finally { + if ( !useBatch ) { + session.getBatcher().closeStatement( st ); + } + } - if ( log.isDebugEnabled() ) { - log.debug( "done deleting collection rows: " + count + " deleted" ); + if ( log.isDebugEnabled() ) { + log.debug( "done deleting collection rows: " + count + " deleted" ); + } } } else { @@ -1219,52 +1316,76 @@ try { //insert all the new entries - Iterator entries = collection.entries(this); + collection.preInsert( this ); + Iterator entries = collection.entries( this ); + Expectation expectation = Expectations.appropriateExpectation( getInsertCheckStyle() ); boolean callable = isInsertCallable(); - try { - collection.preInsert( this ); - int i = 0; - int count = 0; + boolean useBatch = expectation.canBeBatched(); + String sql = getSQLInsertRowString(); + int i = 0; + int count = 0; + while ( entries.hasNext() ) { int offset = 1; - while ( entries.hasNext() ) { - Object entry = entries.next(); - PreparedStatement st = null; - if ( collection.needsInserting( entry, i, elementType ) ) { + Object entry = entries.next(); + PreparedStatement st = null; + if ( collection.needsInserting( entry, i, elementType ) ) { + + if ( useBatch ) { if ( st == null ) { if ( callable ) { - CallableStatement callstatement = session.getBatcher() - .prepareBatchCallableStatement( getSQLInsertRowString() ); - callstatement.registerOutParameter( offset++, Types.NUMERIC ); // TODO: should we require users to return number of update rows ? - st = callstatement; + st = session.getBatcher().prepareBatchCallableStatement( sql ); } else { - st = session.getBatcher().prepareBatchStatement( getSQLInsertRowString() ); + st = session.getBatcher().prepareBatchStatement( sql ); } } + } + else { + if ( callable ) { + st = session.getBatcher().prepareCallableStatement( sql ); + } + else { + st = session.getBatcher().prepareStatement( sql ); + } + } + + try { + offset += expectation.prepare( st ); //TODO: copy/paste from recreate() - int loc = writeKey( st, id, offset, session ); + offset = writeKey( st, id, offset, session ); if ( hasIdentifier ) { - loc = writeIdentifier( st, collection.getIdentifier(entry, i), loc, session ); + offset = writeIdentifier( st, collection.getIdentifier(entry, i), offset, session ); } if ( hasIndex /*&& !indexIsFormula*/ ) { - loc = writeIndex( st, collection.getIndex(entry, i, this), loc, session ); + offset = writeIndex( st, collection.getIndex(entry, i, this), offset, session ); } - //if ( !elementIsFormula ) { - loc = writeElement(st, collection.getElement(entry), loc, session ); - //} - session.getBatcher().addToBatch( 1 ); + writeElement(st, collection.getElement(entry), offset, session ); + + if ( useBatch ) { + session.getBatcher().addToBatch( expectation ); + } + else { + expectation.verifyOutcome( st.executeUpdate(), st, -1 ); + } collection.afterRowInsert( this, entry, i ); count++; } - i++; + catch ( SQLException sqle ) { + if ( useBatch ) { + session.getBatcher().abortBatch( sqle ); + } + throw sqle; + } + finally { + if ( !useBatch ) { + session.getBatcher().closeStatement( st ); + } + } } - if ( log.isDebugEnabled() ) { - log.debug( "done inserting rows: " + count + " inserted" ); - } + i++; } - catch ( SQLException sqle ) { - session.getBatcher().abortBatch( sqle ); - throw sqle; + if ( log.isDebugEnabled() ) { + log.debug( "done inserting rows: " + count + " inserted" ); } } catch ( SQLException sqle ) { @@ -1432,18 +1553,34 @@ return insertCallable; } + protected ExecuteUpdateResultCheckStyle getInsertCheckStyle() { + return insertCheckStyle; + } + protected boolean isUpdateCallable() { return updateCallable; } + protected ExecuteUpdateResultCheckStyle getUpdateCheckStyle() { + return updateCheckStyle; + } + protected boolean isDeleteCallable() { return deleteCallable; } + protected ExecuteUpdateResultCheckStyle getDeleteCheckStyle() { + return deleteCheckStyle; + } + protected boolean isDeleteAllCallable() { return deleteAllCallable; } + protected ExecuteUpdateResultCheckStyle getDeleteAllCheckStyle() { + return deleteAllCheckStyle; + } + public String toString() { return StringHelper.unqualify( getClass().getName() ) + '(' + role + ')'; } Modified: trunk/Hibernate3/src/org/hibernate/persister/collection/BasicCollectionPersister.java =================================================================== --- trunk/Hibernate3/src/org/hibernate/persister/collection/BasicCollectionPersister.java 2006-06-21 22:29:24 UTC (rev 10039) +++ trunk/Hibernate3/src/org/hibernate/persister/collection/BasicCollectionPersister.java 2006-06-22 19:51:43 UTC (rev 10040) @@ -2,14 +2,14 @@ package org.hibernate.persister.collection; import java.io.Serializable; -import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.SQLException; -import java.sql.Types; import java.util.Iterator; import org.hibernate.HibernateException; import org.hibernate.MappingException; +import org.hibernate.jdbc.Expectations; +import org.hibernate.jdbc.Expectation; import org.hibernate.type.AssociationType; import org.hibernate.persister.entity.Joinable; import org.hibernate.cache.CacheConcurrencyStrategy; @@ -172,51 +172,76 @@ try { PreparedStatement st = null; + Expectation expectation = Expectations.appropriateExpectation( getUpdateCheckStyle() ); boolean callable = isUpdateCallable(); - Iterator entries = collection.entries(this); - try { - int i = 0; - int count = 0; - while ( entries.hasNext() ) { + boolean useBatch = expectation.canBeBatched(); + Iterator entries = collection.entries( this ); + String sql = getSQLUpdateRowString(); + int i = 0; + int count = 0; + while ( entries.hasNext() ) { + Object entry = entries.next(); + if ( collection.needsUpdating( entry, i, elementType ) ) { int offset = 1; - Object entry = entries.next(); - if ( collection.needsUpdating( entry, i, elementType ) ) { + + if ( useBatch ) { if ( st == null ) { if ( callable ) { - CallableStatement callstatement = session.getBatcher() - .prepareBatchCallableStatement( getSQLUpdateRowString() ); - callstatement.registerOutParameter( offset++, Types.NUMERIC ); // TODO: should we require users to return number of update rows ? (we cant make it return this without changing collectionpersister interface) - st = callstatement; + st = session.getBatcher().prepareBatchCallableStatement( sql ); } else { - st = session.getBatcher().prepareBatchStatement( getSQLUpdateRowString() ); + st = session.getBatcher().prepareBatchStatement( sql ); } } - - int loc = writeElement(st, collection.getElement(entry), offset, session ); + } + else { + if ( callable ) { + st = session.getBatcher().prepareCallableStatement( sql ); + } + else { + st = session.getBatcher().prepareStatement( sql ); + } + } + + try { + offset+= expectation.prepare( st ); + int loc = writeElement( st, collection.getElement( entry ), offset, session ); if ( hasIdentifier ) { - loc = writeIdentifier(st, collection.getIdentifier(entry, i), loc, session); + writeIdentifier( st, collection.getIdentifier( entry, i ), loc, session ); } else { loc = writeKey( st, id, loc, session ); if ( hasIndex && !indexContainsFormula ) { - loc = writeIndexToWhere( st, collection.getIndex(entry, i, this), loc, session ); + writeIndexToWhere( st, collection.getIndex( entry, i, this ), loc, session ); } else { - loc = writeElementToWhere( st, collection.getSnapshotElement(entry, i), loc, session ); + writeElementToWhere( st, collection.getSnapshotElement( entry, i ), loc, session ); } } - session.getBatcher().addToBatch( 1 ); - count++; + + if ( useBatch ) { + session.getBatcher().addToBatch( expectation ); + } + else { + expectation.verifyOutcome( st.executeUpdate(), st, -1 ); + } } - i++; + catch ( SQLException sqle ) { + if ( useBatch ) { + session.getBatcher().abortBatch( sqle ); + } + throw sqle; + } + finally { + if ( !useBatch ) { + session.getBatcher().closeStatement( st ); + } + } + count++; } - return count; + i++; } - catch ( SQLException sqle ) { - session.getBatcher().abortBatch( sqle ); - throw sqle; - } + return count; } catch ( SQLException sqle ) { throw JDBCExceptionHelper.convert( Modified: trunk/Hibernate3/src/org/hibernate/persister/collection/OneToManyPersister.java =================================================================== --- trunk/Hibernate3/src/org/hibernate/persister/collection/OneToManyPersister.java 2006-06-21 22:29:24 UTC (rev 10039) +++ trunk/Hibernate3/src/org/hibernate/persister/collection/OneToManyPersister.java 2006-06-22 19:51:43 UTC (rev 10040) @@ -2,14 +2,14 @@ package org.hibernate.persister.collection; import java.io.Serializable; -import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.SQLException; -import java.sql.Types; import java.util.Iterator; import org.hibernate.HibernateException; import org.hibern... [truncated message content] |