diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/ResultCheckStyle.java b/hibernate-core/src/main/java/org/hibernate/annotations/ResultCheckStyle.java index c4274953b3..d873a4af7b 100644 --- a/hibernate-core/src/main/java/org/hibernate/annotations/ResultCheckStyle.java +++ b/hibernate-core/src/main/java/org/hibernate/annotations/ResultCheckStyle.java @@ -6,6 +6,8 @@ */ package org.hibernate.annotations; +import org.hibernate.jdbc.Expectation; + /** * Enumerates strategies for checking JDBC return codes for custom SQL DML queries. *

@@ -19,13 +21,20 @@ package org.hibernate.annotations; * @see SQLUpdate#check() * @see SQLDelete#check() * @see SQLDeleteAll#check() + * + * @see Expectation + * + * @deprecated Use an {@link Expectation} class instead. */ +@Deprecated(since = "6.5") public enum ResultCheckStyle { /** * No return code checking. Might mean that no checks are required, or that * failure is indicated by a {@link java.sql.SQLException} being thrown, for * example, by a {@link java.sql.CallableStatement stored procedure} which * performs explicit checks. + * + * @see org.hibernate.jdbc.Expectation.None */ NONE, /** @@ -34,6 +43,8 @@ public enum ResultCheckStyle { * {@link java.sql.Statement#executeBatch()}. The row count is checked * against an expected value. For example, the expected row count for * an {@code INSERT} statement is always 1. + * + * @see org.hibernate.jdbc.Expectation.RowCount */ COUNT, /** @@ -42,6 +53,8 @@ public enum ResultCheckStyle { * stored procedure}. *

* Statement batching is disabled when {@code PARAM} is selected. + * + * @see org.hibernate.jdbc.Expectation.OutParameter */ PARAM } diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/SQLDelete.java b/hibernate-core/src/main/java/org/hibernate/annotations/SQLDelete.java index 94c4e3451e..aebdd5f840 100644 --- a/hibernate-core/src/main/java/org/hibernate/annotations/SQLDelete.java +++ b/hibernate-core/src/main/java/org/hibernate/annotations/SQLDelete.java @@ -6,6 +6,8 @@ */ package org.hibernate.annotations; +import org.hibernate.jdbc.Expectation; + import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.Target; @@ -42,8 +44,20 @@ public @interface SQLDelete { boolean callable() default false; /** - * For persistence operation what style of determining results (success/failure) is to be used. + * An {@link Expectation} class used to verify that the operation was successful. + * + * @see Expectation.None + * @see Expectation.RowCount + * @see Expectation.OutParameter */ + Class verify() default Expectation.class; + + /** + * A {@link ResultCheckStyle} used to verify that the operation was successful. + * + * @deprecated use {@link #verify()} with an {@link Expectation} class + */ + @Deprecated(since = "6.5") ResultCheckStyle check() default ResultCheckStyle.NONE; /** diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/SQLDeleteAll.java b/hibernate-core/src/main/java/org/hibernate/annotations/SQLDeleteAll.java index bbe20bfa07..9785dbbd5a 100644 --- a/hibernate-core/src/main/java/org/hibernate/annotations/SQLDeleteAll.java +++ b/hibernate-core/src/main/java/org/hibernate/annotations/SQLDeleteAll.java @@ -6,6 +6,8 @@ */ package org.hibernate.annotations; +import org.hibernate.jdbc.Expectation; + import java.lang.annotation.Retention; import java.lang.annotation.Target; @@ -34,8 +36,20 @@ public @interface SQLDeleteAll { boolean callable() default false; /** - * For persistence operation what style of determining results (success/failure) is to be used. + * An {@link Expectation} class used to verify that the operation was successful. + * + * @see Expectation.None + * @see Expectation.RowCount + * @see Expectation.OutParameter */ + Class verify() default Expectation.class; + + /** + * A {@link ResultCheckStyle} used to verify that the operation was successful. + * + * @deprecated use {@link #verify()} with an {@link Expectation} class + */ + @Deprecated(since = "6.5") ResultCheckStyle check() default ResultCheckStyle.NONE; /** diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/SQLInsert.java b/hibernate-core/src/main/java/org/hibernate/annotations/SQLInsert.java index a2a5b878ce..5d57a1a476 100644 --- a/hibernate-core/src/main/java/org/hibernate/annotations/SQLInsert.java +++ b/hibernate-core/src/main/java/org/hibernate/annotations/SQLInsert.java @@ -6,6 +6,8 @@ */ package org.hibernate.annotations; +import org.hibernate.jdbc.Expectation; + import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.Target; @@ -58,8 +60,20 @@ public @interface SQLInsert { boolean callable() default false; /** - * For persistence operation what style of determining results (success/failure) is to be used. + * An {@link Expectation} class used to verify that the operation was successful. + * + * @see Expectation.None + * @see Expectation.RowCount + * @see Expectation.OutParameter */ + Class verify() default Expectation.class; + + /** + * A {@link ResultCheckStyle} used to verify that the operation was successful. + * + * @deprecated use {@link #verify()} with an {@link Expectation} class + */ + @Deprecated(since = "6.5") ResultCheckStyle check() default ResultCheckStyle.NONE; /** diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/SQLUpdate.java b/hibernate-core/src/main/java/org/hibernate/annotations/SQLUpdate.java index 03340aeba4..1dceef81f2 100644 --- a/hibernate-core/src/main/java/org/hibernate/annotations/SQLUpdate.java +++ b/hibernate-core/src/main/java/org/hibernate/annotations/SQLUpdate.java @@ -6,6 +6,8 @@ */ package org.hibernate.annotations; +import org.hibernate.jdbc.Expectation; + import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.Target; @@ -61,8 +63,20 @@ public @interface SQLUpdate { boolean callable() default false; /** - * For persistence operation what style of determining results (success/failure) is to be used. + * An {@link Expectation} class used to verify that the operation was successful. + * + * @see Expectation.None + * @see Expectation.RowCount + * @see Expectation.OutParameter */ + Class verify() default Expectation.class; + + /** + * A {@link ResultCheckStyle} used to verify that the operation was successful. + * + * @deprecated use {@link #verify()} with an {@link Expectation} class + */ + @Deprecated(since = "6.5") ResultCheckStyle check() default ResultCheckStyle.NONE; /** diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java index 1ce1125f9c..f564e2d35f 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java @@ -65,6 +65,7 @@ import org.hibernate.annotations.OptimisticLock; import org.hibernate.annotations.Parameter; import org.hibernate.annotations.Persister; import org.hibernate.annotations.QueryCacheLayout; +import org.hibernate.annotations.ResultCheckStyle; import org.hibernate.annotations.SQLDelete; import org.hibernate.annotations.SQLDeleteAll; import org.hibernate.annotations.SQLInsert; @@ -95,6 +96,8 @@ import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.spi.FilterDefinition; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.collections.CollectionHelper; +import org.hibernate.jdbc.Expectation; +import org.hibernate.jdbc.Expectations; import org.hibernate.mapping.Any; import org.hibernate.mapping.Backref; import org.hibernate.mapping.CheckConstraint; @@ -1321,6 +1324,7 @@ public abstract class CollectionBinder { propertyHolder.addProperty( prop, declaringClass ); } + @SuppressWarnings("deprecation") private void bindLoader() { //SQL overriding @@ -1331,7 +1335,9 @@ public abstract class CollectionBinder { sqlInsert.callable(), fromResultCheckStyle( sqlInsert.check() ) ); - + if ( sqlInsert.verify() != Expectation.class ) { + collection.setInsertExpectation( sqlInsert.verify() ); + } } final SQLUpdate sqlUpdate = property.getAnnotation( SQLUpdate.class ); @@ -1341,6 +1347,9 @@ public abstract class CollectionBinder { sqlUpdate.callable(), fromResultCheckStyle( sqlUpdate.check() ) ); + if ( sqlUpdate.verify() != Expectation.class ) { + collection.setUpdateExpectation( sqlUpdate.verify() ); + } } final SQLDelete sqlDelete = property.getAnnotation( SQLDelete.class ); @@ -1350,6 +1359,9 @@ public abstract class CollectionBinder { sqlDelete.callable(), fromResultCheckStyle( sqlDelete.check() ) ); + if ( sqlDelete.verify() != Expectation.class ) { + collection.setDeleteExpectation( sqlDelete.verify() ); + } } final SQLDeleteAll sqlDeleteAll = property.getAnnotation( SQLDeleteAll.class ); @@ -1359,6 +1371,9 @@ public abstract class CollectionBinder { sqlDeleteAll.callable(), fromResultCheckStyle( sqlDeleteAll.check() ) ); + if ( sqlDeleteAll.verify() != Expectation.class ) { + collection.setDeleteAllExpectation( sqlDeleteAll.verify() ); + } } final SQLSelect sqlSelect = property.getAnnotation( SQLSelect.class ); diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java index 4b74698384..9938efee39 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java @@ -90,6 +90,7 @@ import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.spi.FilterDefinition; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.StringHelper; +import org.hibernate.jdbc.Expectation; import org.hibernate.jpa.event.spi.CallbackType; import org.hibernate.mapping.BasicValue; import org.hibernate.mapping.CheckConstraint; @@ -1387,7 +1388,9 @@ public class EntityBinder { sqlInsert.callable(), fromResultCheckStyle( sqlInsert.check() ) ); - + if ( sqlInsert.verify() != Expectation.class ) { + persistentClass.setInsertExpectation( sqlInsert.verify() ); + } } final SQLUpdate sqlUpdate = findMatchingSqlAnnotation( "", SQLUpdate.class, SQLUpdates.class ); @@ -1397,6 +1400,9 @@ public class EntityBinder { sqlUpdate.callable(), fromResultCheckStyle( sqlUpdate.check() ) ); + if ( sqlUpdate.verify() != Expectation.class ) { + persistentClass.setUpdateExpectation( sqlUpdate.verify() ); + } } final SQLDelete sqlDelete = findMatchingSqlAnnotation( "", SQLDelete.class, SQLDeletes.class ); @@ -1406,6 +1412,9 @@ public class EntityBinder { sqlDelete.callable(), fromResultCheckStyle( sqlDelete.check() ) ); + if ( sqlDelete.verify() != Expectation.class ) { + persistentClass.setDeleteExpectation( sqlDelete.verify() ); + } } final SQLDeleteAll sqlDeleteAll = annotatedClass.getAnnotation( SQLDeleteAll.class ); @@ -2265,6 +2274,9 @@ public class EntityBinder { sqlInsert.callable(), fromResultCheckStyle( sqlInsert.check() ) ); + if ( sqlInsert.verify() != Expectation.class ) { + join.setInsertExpectation( sqlInsert.verify() ); + } } else if ( matchingTable != null ) { final String insertSql = matchingTable.sqlInsert().sql(); @@ -2284,6 +2296,9 @@ public class EntityBinder { sqlUpdate.callable(), fromResultCheckStyle( sqlUpdate.check() ) ); + if ( sqlUpdate.verify() != Expectation.class ) { + join.setUpdateExpectation( sqlUpdate.verify() ); + } } else if ( matchingTable != null ) { final String updateSql = matchingTable.sqlUpdate().sql(); @@ -2303,6 +2318,9 @@ public class EntityBinder { sqlDelete.callable(), fromResultCheckStyle( sqlDelete.check() ) ); + if ( sqlDelete.verify() != Expectation.class ) { + join.setDeleteExpectation( sqlDelete.verify() ); + } } else if ( matchingTable != null ) { final String deleteSql = matchingTable.sqlDelete().sql(); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/ExecuteUpdateResultCheckStyle.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/ExecuteUpdateResultCheckStyle.java index 89cb9cc78e..62bf4105dc 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/ExecuteUpdateResultCheckStyle.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/ExecuteUpdateResultCheckStyle.java @@ -10,6 +10,7 @@ import org.hibernate.AssertionFailure; import org.hibernate.annotations.ResultCheckStyle; import org.checkerframework.checker.nullness.qual.Nullable; +import org.hibernate.jdbc.Expectation; /** * For persistence operations (INSERT, UPDATE, DELETE) what style of @@ -21,7 +22,10 @@ import org.checkerframework.checker.nullness.qual.Nullable; * new {@code org.hibernate.ResultCheck} enum. * * @author Steve Ebersole + * + * @deprecated Use an {@link org.hibernate.jdbc.Expectation} class */ +@Deprecated(since = "6.5", forRemoval = true) public enum ExecuteUpdateResultCheckStyle { /** * Do not perform checking. Either user simply does not want checking, or is @@ -85,4 +89,26 @@ public enum ExecuteUpdateResultCheckStyle { public static ExecuteUpdateResultCheckStyle determineDefault(@Nullable String customSql, boolean callable) { return customSql != null && callable ? PARAM : COUNT; } + + public static @Nullable Class expectationClass(@Nullable ExecuteUpdateResultCheckStyle style) { + if ( style == null ) { + return null; + } + else { + return style.expectationClass(); + } + } + + public Class expectationClass() { + switch (this) { + case NONE: + return Expectation.None.class; + case COUNT: + return Expectation.RowCount.class; + case PARAM: + return Expectation.OutParameter.class; + default: + throw new AssertionFailure( "Unrecognized ExecuteUpdateResultCheckStyle"); + } + } } diff --git a/hibernate-core/src/main/java/org/hibernate/jdbc/Expectation.java b/hibernate-core/src/main/java/org/hibernate/jdbc/Expectation.java index 54b32789f1..83d434b465 100644 --- a/hibernate-core/src/main/java/org/hibernate/jdbc/Expectation.java +++ b/hibernate-core/src/main/java/org/hibernate/jdbc/Expectation.java @@ -5,13 +5,43 @@ * See the lgpl.txt file in the root directory or . */ package org.hibernate.jdbc; + +import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.SQLException; +import java.sql.Types; import org.hibernate.HibernateException; +import org.hibernate.exception.GenericJDBCException; + +import static org.hibernate.jdbc.Expectations.checkBatched; +import static org.hibernate.jdbc.Expectations.checkNonBatched; +import static org.hibernate.jdbc.Expectations.sqlExceptionHelper; +import static org.hibernate.jdbc.Expectations.toCallableStatement; /** * Defines an expected DML operation outcome. + * Used to verify that a JDBC operation completed successfully. + *

+ * The two standard implementations are {@link RowCount} for + * row count checking, and {@link OutParameter} for checking + * the return code assigned to an output parameter of a + * {@link CallableStatement}. Custom implementations are + * permitted. + *

+ * An {@code Expectation} is usually selected via an annotation, + * for example: + *

+ * @Entity
+ * @SQLUpdate(sql = "update Record set uid = gen_random_uuid(), whatever = ? where id = ?",
+ *            verify = Expectation.RowCount.class)
+ * class Record { ... }
+ * 
+ * + * @see org.hibernate.annotations.SQLInsert#verify + * @see org.hibernate.annotations.SQLUpdate#verify + * @see org.hibernate.annotations.SQLDelete#verify + * @see org.hibernate.annotations.SQLDeleteAll#verify * * @author Steve Ebersole */ @@ -22,12 +52,14 @@ public interface Expectation { * * @return True if batching can be combined with this expectation; false otherwise. */ - boolean canBeBatched(); + default boolean canBeBatched() { + return true; + } /** * The number of parameters this expectation implies. E.g., - * {@link Expectations.BasicParamExpectation} requires a single - * OUT parameter for reading back the number of affected rows. + * {@link OutParameter} requires a single OUT parameter for + * reading back the number of affected rows. */ default int getNumberOfParametersUsed() { return 0; @@ -40,11 +72,12 @@ public interface Expectation { * @param rowCount The RDBMS reported "number of rows affected". * @param statement The statement representing the operation * @param batchPosition The position in the batch (if batching) - * @param statementSQL The SQL backing the prepared statement, for logging purposes + * @param sql The SQL backing the prepared statement, for logging purposes * @throws SQLException Exception from the JDBC driver * @throws HibernateException Problem processing the outcome. */ - void verifyOutcome(int rowCount, PreparedStatement statement, int batchPosition, String statementSQL) throws SQLException, HibernateException; + void verifyOutcome(int rowCount, PreparedStatement statement, int batchPosition, String sql) + throws SQLException, HibernateException; /** * Perform any special statement preparation. @@ -54,5 +87,101 @@ public interface Expectation { * @throws SQLException Exception from the JDBC driver * @throws HibernateException Problem performing preparation. */ - int prepare(PreparedStatement statement) throws SQLException, HibernateException; + default int prepare(PreparedStatement statement) throws SQLException, HibernateException { + return 0; + } + + /** + * No return code checking. Might mean that no checks are required, or that + * failure is indicated by a {@link java.sql.SQLException} being thrown, for + * example, by a {@link java.sql.CallableStatement stored procedure} which + * performs explicit checks. + * + * @since 6.5 + */ + class None implements Expectation { + @Override + public void verifyOutcome(int rowCount, PreparedStatement statement, int batchPosition, String sql) { + // nothing to do + } + } + + /** + * Row count checking. A row count is an integer value returned by + * {@link java.sql.PreparedStatement#executeUpdate()} or + * {@link java.sql.Statement#executeBatch()}. The row count is checked + * against an expected value. For example, the expected row count for + * an {@code INSERT} statement is always 1. + * + * @since 6.5 + */ + class RowCount implements Expectation { + @Override + public final void verifyOutcome(int rowCount, PreparedStatement statement, int batchPosition, String sql) { + if ( batchPosition < 0 ) { + checkNonBatched( expectedRowCount(), rowCount, sql ); + } + else { + checkBatched( expectedRowCount(), rowCount, batchPosition, sql ); + } + } + + protected int expectedRowCount() { + return 1; + } + } + + /** + * Essentially identical to {@link RowCount} except that the row count + * is obtained via an output parameter of a {@link CallableStatement + * stored procedure}. + *

+ * Statement batching is disabled when {@code OutParameter} is used. + * + * @since 6.5 + */ + class OutParameter implements Expectation { + @Override + public final void verifyOutcome(int rowCount, PreparedStatement statement, int batchPosition, String sql) { + final int result; + try { + result = toCallableStatement( statement ).getInt( parameterIndex() ); + } + catch ( SQLException sqle ) { + sqlExceptionHelper.logExceptions( sqle, "Could not extract row count from CallableStatement" ); + throw new GenericJDBCException( "Could not extract row count from CallableStatement", sqle ); + } + if ( batchPosition < 0 ) { + checkNonBatched( expectedRowCount(), result, sql ); + } + else { + checkBatched( expectedRowCount(), result, batchPosition, sql ); + } + } + + @Override + public int getNumberOfParametersUsed() { + return 1; + } + + @Override + public int prepare(PreparedStatement statement) throws SQLException, HibernateException { + toCallableStatement( statement ).registerOutParameter( parameterIndex(), Types.NUMERIC ); + return 1; + } + + @Override + public boolean canBeBatched() { + return false; + } + + protected int parameterIndex() { + return 1; + } + + protected int expectedRowCount() { + return 1; + } + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/jdbc/Expectations.java b/hibernate-core/src/main/java/org/hibernate/jdbc/Expectations.java index 0e287fa97a..2034c13a8a 100644 --- a/hibernate-core/src/main/java/org/hibernate/jdbc/Expectations.java +++ b/hibernate-core/src/main/java/org/hibernate/jdbc/Expectations.java @@ -11,7 +11,10 @@ import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Types; +import org.hibernate.AssertionFailure; import org.hibernate.HibernateException; +import org.hibernate.InstantiationException; +import org.hibernate.Internal; import org.hibernate.StaleStateException; import org.hibernate.engine.jdbc.spi.SqlExceptionHelper; import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle; @@ -27,85 +30,44 @@ import org.hibernate.internal.CoreMessageLogger; public class Expectations { private static final CoreMessageLogger LOG = CoreLogging.messageLogger( Expectations.class ); - private static final SqlExceptionHelper sqlExceptionHelper = new SqlExceptionHelper( false ); - - public static final int USUAL_EXPECTED_COUNT = 1; - public static final int USUAL_PARAM_POSITION = 1; - + static final SqlExceptionHelper sqlExceptionHelper = new SqlExceptionHelper( false ); // Base Expectation impls ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + /** + * @deprecated Use {@link RowCount} + */ + @Deprecated(since = "6.5") public static class BasicExpectation implements Expectation { - private final int expectedRowCount; + private final int expected; protected BasicExpectation(int expectedRowCount) { - this.expectedRowCount = expectedRowCount; + expected = 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, String statementSQL) { - rowCount = determineRowCount( rowCount, statement ); + @Override + public final void verifyOutcome(int rowCount, PreparedStatement statement, int batchPosition, String sql) { + final int result = determineRowCount( rowCount, statement ); if ( batchPosition < 0 ) { - checkNonBatched( rowCount, statementSQL ); + checkNonBatched( expected, result, sql ); } else { - checkBatched( rowCount, batchPosition, statementSQL ); + checkBatched( expected, result, batchPosition, sql ); } } - private void checkBatched(int rowCount, int batchPosition, String statementSQL) { - if ( rowCount == -2 ) { - LOG.debugf( "Success of batch update unknown: %s", batchPosition ); - } - else if ( rowCount == -3 ) { - throw new BatchFailedException( "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 + "; statement executed: " - + statementSQL - ); - } - if ( expectedRowCount < rowCount ) { - String msg = "Batch update returned unexpected row count from update [" + - batchPosition + "]; actual row count: " + rowCount + - "; expected: " + expectedRowCount; - throw new BatchedTooManyRowsAffectedException( msg, expectedRowCount, rowCount, batchPosition ); - } - } - } - - private void checkNonBatched(int rowCount, String statementSQL) { - if ( expectedRowCount > rowCount ) { - throw new StaleStateException( - "Unexpected row count: " + rowCount + "; expected: " + expectedRowCount - + "; statement executed: " + statementSQL - ); - } - if ( expectedRowCount < rowCount ) { - String msg = "Unexpected row count: " + rowCount + "; expected: " + expectedRowCount; - throw new TooManyRowsAffectedException( msg, expectedRowCount, rowCount ); - } - } - - public int prepare(PreparedStatement statement) throws SQLException, HibernateException { - return 0; - } - - public boolean canBeBatched() { - return true; - } - protected int determineRowCount(int reportedRowCount, PreparedStatement statement) { return reportedRowCount; } } + /** + * @deprecated Use {@link OutParameter} + */ + @Deprecated(since = "6.5") public static class BasicParamExpectation extends BasicExpectation { private final int parameterPosition; @@ -140,39 +102,88 @@ public class Expectations { throw new GenericJDBCException( "could not extract row counts from CallableStatement", sqle ); } } + } - private CallableStatement toCallableStatement(PreparedStatement statement) { - if ( !(statement instanceof CallableStatement) ) { - throw new HibernateException( - "BasicParamExpectation operates exclusively on CallableStatements : " + statement.getClass() - ); - } + static CallableStatement toCallableStatement(PreparedStatement statement) { + if ( statement instanceof CallableStatement ) { return (CallableStatement) statement; } + else { + throw new HibernateException( "Expectation.OutParameter operates exclusively on CallableStatements: " + + statement.getClass() ); + } } + static void checkBatched(int expected, int rowCount, int batchPosition, String sql) { + if ( rowCount == -2 ) { + LOG.debugf( "Success of batch update unknown: %s", batchPosition ); + } + else if ( rowCount == -3 ) { + throw new BatchFailedException( "Batch update failed: " + batchPosition ); + } + else if ( expected > rowCount ) { + throw new StaleStateException( + "Batch update returned unexpected row count from update [" + + batchPosition + "]; actual row count: " + rowCount + + "; expected: " + 1 + "; statement executed: " + + sql + ); + } + else if ( expected < rowCount ) { + String msg = "Batch update returned unexpected row count from update [" + + batchPosition + "]; actual row count: " + rowCount + + "; expected: " + 1; + throw new BatchedTooManyRowsAffectedException( msg, 1, rowCount, batchPosition ); + } + } + + static void checkNonBatched(int expected, int rowCount, String sql) { + if ( expected > rowCount ) { + throw new StaleStateException( + "Unexpected row count: " + rowCount + "; expected: " + 1 + + "; statement executed: " + sql + ); + } + if ( expected < rowCount ) { + String msg = "Unexpected row count: " + rowCount + "; expected: " + 1; + throw new TooManyRowsAffectedException( msg, 1, rowCount ); + } + } // Various Expectation instances ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - public static final Expectation NONE = new Expectation() { - public void verifyOutcome(int rowCount, PreparedStatement statement, int batchPosition, String statementSQL) { - // explicitly doAfterTransactionCompletion no checking... + /** + * @deprecated Use {@link Expectation.None} + */ + @Deprecated(since = "6.5") + public static final Expectation NONE = new Expectation.None(); + + /** + * @deprecated Use {@link Expectation.RowCount} + */ + @Deprecated(since = "6.5") + public static final Expectation BASIC = new Expectation.RowCount(); + + /** + * @deprecated Use {@link Expectation.OutParameter} + */ + @Deprecated(since = "6.5") + public static final Expectation PARAM = new Expectation.OutParameter(); + + @Internal + public static Expectation createExpectation(Class expectation, boolean callable) { + if ( expectation == null ) { + expectation = callable ? Expectation.OutParameter.class : Expectation.RowCount.class; } - - public int prepare(PreparedStatement statement) { - return 0; + try { + return expectation.newInstance(); } - - public boolean canBeBatched() { - return true; + catch ( Exception e ) { + throw new InstantiationException( "Could not instantiate Expectation", expectation, e ); } - }; - - public static final Expectation BASIC = new BasicExpectation( USUAL_EXPECTED_COUNT ); - - public static final Expectation PARAM = new BasicParamExpectation( USUAL_EXPECTED_COUNT, USUAL_PARAM_POSITION ); - + } + @Deprecated(since = "6.5", forRemoval = true) public static Expectation appropriateExpectation(ExecuteUpdateResultCheckStyle style) { switch ( style ) { case NONE: @@ -182,10 +193,19 @@ public class Expectations { case PARAM: return PARAM; default: - throw new HibernateException( "unknown check style : " + style ); + throw new AssertionFailure( "unknown result check style: " + style ); } } private Expectations() { } + + // Unused, for removal ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + @Deprecated(since = "6.5", forRemoval = true) + public static final int USUAL_EXPECTED_COUNT = 1; + + @Deprecated(since = "6.5", forRemoval = true) + public static final int USUAL_PARAM_POSITION = 1; + } diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Collection.java b/hibernate-core/src/main/java/org/hibernate/mapping/Collection.java index 6a473913e3..1fb23f2d6a 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Collection.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Collection.java @@ -27,7 +27,7 @@ import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle; import org.hibernate.engine.spi.Mapping; import org.hibernate.internal.FilterConfiguration; import org.hibernate.internal.util.StringHelper; -import org.hibernate.internal.util.collections.ArrayHelper; +import org.hibernate.jdbc.Expectation; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.resource.beans.spi.ManagedBean; import org.hibernate.service.ServiceRegistry; @@ -36,6 +36,9 @@ import org.hibernate.type.CustomCollectionType; import org.hibernate.type.Type; import org.hibernate.usertype.UserCollectionType; +import static org.hibernate.internal.util.collections.ArrayHelper.EMPTY_BOOLEAN_ARRAY; +import static org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle.expectationClass; + /** * A mapping model object representing a collection. Subclasses specialize to particular kinds of collection. * @@ -104,6 +107,11 @@ public abstract class Collection implements Fetchable, Value, Filterable, SoftDe private String loaderName; + private Class insertExpectation; + private Class updateExpectation; + private Class deleteExpectation; + private Class deleteAllExpectation; + /** * hbm.xml binding */ @@ -170,6 +178,10 @@ public abstract class Collection implements Fetchable, Value, Filterable, SoftDe this.customSQLDeleteAll = original.customSQLDeleteAll; this.customDeleteAllCallable = original.customDeleteAllCallable; this.deleteAllCheckStyle = original.deleteAllCheckStyle; + this.insertExpectation = original.insertExpectation; + this.updateExpectation = original.updateExpectation; + this.deleteExpectation = original.deleteExpectation; + this.deleteAllExpectation = original.deleteAllExpectation; this.loaderName = original.loaderName; } @@ -577,11 +589,11 @@ public abstract class Collection implements Fetchable, Value, Filterable, SoftDe this.queryCacheLayout = queryCacheLayout; } - public void setCustomSQLInsert(String customSQLInsert, boolean callable, ExecuteUpdateResultCheckStyle checkStyle) { this.customSQLInsert = customSQLInsert; this.customInsertCallable = callable; this.insertCheckStyle = checkStyle; + this.insertExpectation = expectationClass( checkStyle ); } public String getCustomSQLInsert() { @@ -592,6 +604,10 @@ public abstract class Collection implements Fetchable, Value, Filterable, SoftDe return customInsertCallable; } + /** + * @deprecated use {@link #getInsertExpectation()} + */ + @Deprecated(since = "6.5", forRemoval = true) public ExecuteUpdateResultCheckStyle getCustomSQLInsertCheckStyle() { return insertCheckStyle; } @@ -600,6 +616,7 @@ public abstract class Collection implements Fetchable, Value, Filterable, SoftDe this.customSQLUpdate = customSQLUpdate; this.customUpdateCallable = callable; this.updateCheckStyle = checkStyle; + this.updateExpectation = expectationClass( checkStyle ); } public String getCustomSQLUpdate() { @@ -610,6 +627,10 @@ public abstract class Collection implements Fetchable, Value, Filterable, SoftDe return customUpdateCallable; } + /** + * @deprecated use {@link #getUpdateExpectation()} + */ + @Deprecated(since = "6.5", forRemoval = true) public ExecuteUpdateResultCheckStyle getCustomSQLUpdateCheckStyle() { return updateCheckStyle; } @@ -618,6 +639,7 @@ public abstract class Collection implements Fetchable, Value, Filterable, SoftDe this.customSQLDelete = customSQLDelete; this.customDeleteCallable = callable; this.deleteCheckStyle = checkStyle; + this.deleteExpectation = expectationClass( checkStyle ); } public String getCustomSQLDelete() { @@ -628,6 +650,10 @@ public abstract class Collection implements Fetchable, Value, Filterable, SoftDe return customDeleteCallable; } + /** + * @deprecated use {@link #getDeleteExpectation()} + */ + @Deprecated(since = "6.5", forRemoval = true) public ExecuteUpdateResultCheckStyle getCustomSQLDeleteCheckStyle() { return deleteCheckStyle; } @@ -639,6 +665,7 @@ public abstract class Collection implements Fetchable, Value, Filterable, SoftDe this.customSQLDeleteAll = customSQLDeleteAll; this.customDeleteAllCallable = callable; this.deleteAllCheckStyle = checkStyle; + this.deleteAllExpectation = expectationClass( checkStyle ); } public String getCustomSQLDeleteAll() { @@ -755,19 +782,19 @@ public abstract class Collection implements Fetchable, Value, Filterable, SoftDe } @SuppressWarnings("rawtypes") - public void setTypeParameters(java.util.Map parameterMap) { - if ( parameterMap instanceof Properties ) { - this.typeParameters = (Properties) parameterMap; + public void setTypeParameters(java.util.Map typeParameters) { + if ( typeParameters instanceof Properties ) { + this.typeParameters = (Properties) typeParameters; } else { this.typeParameters = new Properties(); - typeParameters.putAll( parameterMap ); + this.typeParameters.putAll( typeParameters ); } } @Override public boolean[] getColumnInsertability() { - return ArrayHelper.EMPTY_BOOLEAN_ARRAY; + return EMPTY_BOOLEAN_ARRAY; } @Override @@ -777,7 +804,7 @@ public abstract class Collection implements Fetchable, Value, Filterable, SoftDe @Override public boolean[] getColumnUpdateability() { - return ArrayHelper.EMPTY_BOOLEAN_ARRAY; + return EMPTY_BOOLEAN_ARRAY; } @Override @@ -848,4 +875,36 @@ public abstract class Collection implements Fetchable, Value, Filterable, SoftDe public Column getSoftDeleteColumn() { return softDeleteColumn; } + + public Class getInsertExpectation() { + return insertExpectation; + } + + public void setInsertExpectation(Class insertExpectation) { + this.insertExpectation = insertExpectation; + } + + public Class getUpdateExpectation() { + return updateExpectation; + } + + public void setUpdateExpectation(Class updateExpectation) { + this.updateExpectation = updateExpectation; + } + + public Class getDeleteExpectation() { + return deleteExpectation; + } + + public void setDeleteExpectation(Class deleteExpectation) { + this.deleteExpectation = deleteExpectation; + } + + public Class getDeleteAllExpectation() { + return deleteAllExpectation; + } + + public void setDeleteAllExpectation(Class deleteAllExpectation) { + this.deleteAllExpectation = deleteAllExpectation; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Join.java b/hibernate-core/src/main/java/org/hibernate/mapping/Join.java index fb9c8b9008..fe7ddb6062 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Join.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Join.java @@ -12,8 +12,11 @@ import java.util.Iterator; import java.util.List; import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle; +import org.hibernate.jdbc.Expectation; import org.hibernate.sql.Alias; +import static org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle.expectationClass; + /** * A mapping model object representing some sort of auxiliary table, for * example, an {@linkplain jakarta.persistence.JoinTable association table}, @@ -47,6 +50,10 @@ public class Join implements AttributeContainer, Serializable { private boolean customDeleteCallable; private ExecuteUpdateResultCheckStyle deleteCheckStyle; + private Class insertExpectation; + private Class updateExpectation; + private Class deleteExpectation; + @Override public void addProperty(Property property) { properties.add( property ); @@ -133,6 +140,7 @@ public class Join implements AttributeContainer, Serializable { this.customSQLInsert = customSQLInsert; this.customInsertCallable = callable; this.insertCheckStyle = checkStyle; + this.insertExpectation = expectationClass( checkStyle ); } public String getCustomSQLInsert() { @@ -143,16 +151,8 @@ public class Join implements AttributeContainer, Serializable { return customInsertCallable; } - public void setInsertCheckStyle(ExecuteUpdateResultCheckStyle insertCheckStyle) { - this.insertCheckStyle = insertCheckStyle; - } - - public ExecuteUpdateResultCheckStyle getInsertCheckStyle() { - return insertCheckStyle; - } - /** - * @deprecated use {@link #getInsertCheckStyle()} + * @deprecated use {@link #getInsertExpectation()} */ @Deprecated(since = "6.5", forRemoval = true) public ExecuteUpdateResultCheckStyle getCustomSQLInsertCheckStyle() { @@ -163,6 +163,7 @@ public class Join implements AttributeContainer, Serializable { this.customSQLUpdate = customSQLUpdate; this.customUpdateCallable = callable; this.updateCheckStyle = checkStyle; + this.updateExpectation = expectationClass( checkStyle ); } public String getCustomSQLUpdate() { @@ -173,16 +174,8 @@ public class Join implements AttributeContainer, Serializable { return customUpdateCallable; } - public void setUpdateCheckStyle(ExecuteUpdateResultCheckStyle updateCheckStyle) { - this.updateCheckStyle = updateCheckStyle; - } - - public ExecuteUpdateResultCheckStyle getUpdateCheckStyle() { - return updateCheckStyle; - } - /** - * @deprecated use {@link #getUpdateCheckStyle()} + * @deprecated use {@link #getUpdateExpectation()} */ @Deprecated(since = "6.5", forRemoval = true) public ExecuteUpdateResultCheckStyle getCustomSQLUpdateCheckStyle() { @@ -193,6 +186,7 @@ public class Join implements AttributeContainer, Serializable { this.customSQLDelete = customSQLDelete; this.customDeleteCallable = callable; this.deleteCheckStyle = checkStyle; + this.deleteExpectation = expectationClass( checkStyle ); } public String getCustomSQLDelete() { @@ -203,16 +197,8 @@ public class Join implements AttributeContainer, Serializable { return customDeleteCallable; } - public void setDeleteCheckStyle(ExecuteUpdateResultCheckStyle deleteCheckStyle) { - this.deleteCheckStyle = deleteCheckStyle; - } - - public ExecuteUpdateResultCheckStyle getDeleteCheckStyle() { - return deleteCheckStyle; - } - /** - * @deprecated use {@link #getDeleteCheckStyle()} + * @deprecated use {@link #getDeleteExpectation()} */ @Deprecated(since = "6.5", forRemoval = true) public ExecuteUpdateResultCheckStyle getCustomSQLDeleteCheckStyle() { @@ -243,7 +229,32 @@ public class Join implements AttributeContainer, Serializable { public boolean isOptional() { return optional; } + public void setOptional(boolean nullable) { this.optional = nullable; } + + public Class getInsertExpectation() { + return insertExpectation; + } + + public void setInsertExpectation(Class insertExpectation) { + this.insertExpectation = insertExpectation; + } + + public Class getUpdateExpectation() { + return updateExpectation; + } + + public void setUpdateExpectation(Class updateExpectation) { + this.updateExpectation = updateExpectation; + } + + public Class getDeleteExpectation() { + return deleteExpectation; + } + + public void setDeleteExpectation(Class deleteExpectation) { + this.deleteExpectation = deleteExpectation; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/PersistentClass.java b/hibernate-core/src/main/java/org/hibernate/mapping/PersistentClass.java index 3f3d07b2ef..917e3cd85e 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/PersistentClass.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/PersistentClass.java @@ -19,7 +19,6 @@ import org.hibernate.Internal; import org.hibernate.MappingException; import org.hibernate.annotations.CacheLayout; import org.hibernate.boot.Metadata; -import org.hibernate.boot.model.CustomSql; import org.hibernate.boot.registry.classloading.spi.ClassLoadingException; import org.hibernate.boot.spi.ClassLoaderAccess; import org.hibernate.boot.spi.MetadataBuildingContext; @@ -28,6 +27,7 @@ import org.hibernate.engine.OptimisticLockStyle; import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle; import org.hibernate.internal.FilterConfiguration; import org.hibernate.internal.util.collections.JoinedList; +import org.hibernate.jdbc.Expectation; import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.jpa.event.spi.CallbackDefinition; @@ -43,6 +43,7 @@ import static java.util.Collections.unmodifiableList; import static java.util.Comparator.comparing; import static org.hibernate.internal.util.StringHelper.qualify; import static org.hibernate.internal.util.StringHelper.root; +import static org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle.expectationClass; import static org.hibernate.mapping.MappingHelper.checkPropertyColumnDuplication; import static org.hibernate.sql.Template.collectColumnNames; @@ -120,6 +121,10 @@ public abstract class PersistentClass implements IdentifiableTypeClass, Attribut private Component declaredIdentifierMapper; private OptimisticLockStyle optimisticLockStyle; + private Class insertExpectation; + private Class updateExpectation; + private Class deleteExpectation; + private boolean isCached; private CacheLayout queryCacheLayout; @@ -793,20 +798,11 @@ public abstract class PersistentClass implements IdentifiableTypeClass, Attribut return properties; } - public void setCustomSqlInsert(CustomSql customSql) { - if ( customSql != null ) { - setCustomSQLInsert( - customSql.getSql(), - customSql.isCallable(), - customSql.getCheckStyle() - ); - } - } - public void setCustomSQLInsert(String customSQLInsert, boolean callable, ExecuteUpdateResultCheckStyle checkStyle) { this.customSQLInsert = customSQLInsert; this.customInsertCallable = callable; this.insertCheckStyle = checkStyle; + this.insertExpectation = expectationClass( checkStyle ); } public String getCustomSQLInsert() { @@ -817,36 +813,19 @@ public abstract class PersistentClass implements IdentifiableTypeClass, Attribut return customInsertCallable; } - public void setInsertCheckStyle(ExecuteUpdateResultCheckStyle insertCheckStyle) { - this.insertCheckStyle = insertCheckStyle; - } - - public ExecuteUpdateResultCheckStyle getInsertCheckStyle() { - return insertCheckStyle; - } - /** - * @deprecated use {@link #getInsertCheckStyle()} + * @deprecated use {@link #getInsertExpectation()} */ @Deprecated(since = "6.5", forRemoval = true) public ExecuteUpdateResultCheckStyle getCustomSQLInsertCheckStyle() { return insertCheckStyle; } - public void setCustomSqlUpdate(CustomSql customSql) { - if ( customSql != null ) { - setCustomSQLUpdate( - customSql.getSql(), - customSql.isCallable(), - customSql.getCheckStyle() - ); - } - } - public void setCustomSQLUpdate(String customSQLUpdate, boolean callable, ExecuteUpdateResultCheckStyle checkStyle) { this.customSQLUpdate = customSQLUpdate; this.customUpdateCallable = callable; this.updateCheckStyle = checkStyle; + this.updateExpectation = expectationClass( checkStyle ); } public String getCustomSQLUpdate() { @@ -857,36 +836,19 @@ public abstract class PersistentClass implements IdentifiableTypeClass, Attribut return customUpdateCallable; } - public void setUpdateCheckStyle(ExecuteUpdateResultCheckStyle updateCheckStyle) { - this.updateCheckStyle = updateCheckStyle; - } - - public ExecuteUpdateResultCheckStyle getUpdateCheckStyle() { - return updateCheckStyle; - } - /** - * @deprecated use {@link #getUpdateCheckStyle()} + * @deprecated use {@link #getUpdateExpectation()} */ @Deprecated(since = "6.5", forRemoval = true) public ExecuteUpdateResultCheckStyle getCustomSQLUpdateCheckStyle() { return updateCheckStyle; } - public void setCustomSqlDelete(CustomSql customSql) { - if ( customSql != null ) { - setCustomSQLDelete( - customSql.getSql(), - customSql.isCallable(), - customSql.getCheckStyle() - ); - } - } - public void setCustomSQLDelete(String customSQLDelete, boolean callable, ExecuteUpdateResultCheckStyle checkStyle) { this.customSQLDelete = customSQLDelete; this.customDeleteCallable = callable; this.deleteCheckStyle = checkStyle; + this.deleteExpectation = expectationClass( checkStyle ); } public String getCustomSQLDelete() { @@ -897,16 +859,8 @@ public abstract class PersistentClass implements IdentifiableTypeClass, Attribut return customDeleteCallable; } - public void setDeleteCheckStyle(ExecuteUpdateResultCheckStyle deleteCheckStyle) { - this.deleteCheckStyle = deleteCheckStyle; - } - - public ExecuteUpdateResultCheckStyle getDeleteCheckStyle() { - return deleteCheckStyle; - } - /** - * @deprecated use {@link #getDeleteCheckStyle()} + * @deprecated use {@link #getDeleteExpectation()} */ @Deprecated(since = "6.5", forRemoval = true) public ExecuteUpdateResultCheckStyle getCustomSQLDeleteCheckStyle() { @@ -1307,4 +1261,28 @@ public abstract class PersistentClass implements IdentifiableTypeClass, Attribut } return null; } + + public Class getInsertExpectation() { + return insertExpectation; + } + + public void setInsertExpectation(Class insertExpectation) { + this.insertExpectation = insertExpectation; + } + + public Class getUpdateExpectation() { + return updateExpectation; + } + + public void setUpdateExpectation(Class updateExpectation) { + this.updateExpectation = updateExpectation; + } + + public Class getDeleteExpectation() { + return deleteExpectation; + } + + public void setDeleteExpectation(Class deleteExpectation) { + this.deleteExpectation = deleteExpectation; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java index a4d0734792..02f092a43e 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java @@ -45,7 +45,6 @@ import org.hibernate.engine.jdbc.mutation.internal.MutationQueryOptions; import org.hibernate.engine.jdbc.spi.JdbcCoordinator; import org.hibernate.engine.jdbc.spi.SqlExceptionHelper; import org.hibernate.engine.profile.internal.FetchProfileAffectee; -import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -58,7 +57,6 @@ import org.hibernate.internal.FilterAliasGenerator; import org.hibernate.internal.FilterHelper; import org.hibernate.internal.util.StringHelper; import org.hibernate.jdbc.Expectation; -import org.hibernate.jdbc.Expectations; import org.hibernate.loader.ast.internal.CollectionElementLoaderByIndex; import org.hibernate.loader.ast.internal.CollectionLoaderNamedQuery; import org.hibernate.loader.ast.internal.CollectionLoaderSingleKey; @@ -137,6 +135,7 @@ import org.hibernate.type.Type; import org.checkerframework.checker.nullness.qual.Nullable; import static org.hibernate.internal.util.collections.CollectionHelper.arrayList; +import static org.hibernate.jdbc.Expectations.createExpectation; import static org.hibernate.sql.model.ModelMutationLogging.MODEL_MUTATION_LOGGER; /** @@ -1688,74 +1687,38 @@ public abstract class AbstractCollectionPersister collectionBootDescriptor.isInverse(), new MutationDetails( MutationType.INSERT, - determineExpectation( - collectionBootDescriptor.getCustomSQLInsertCheckStyle(), - collectionBootDescriptor.getCustomSQLInsert(), - collectionBootDescriptor.isCustomInsertCallable() - ), + createExpectation( collectionBootDescriptor.getInsertExpectation(), + collectionBootDescriptor.isCustomInsertCallable()), collectionBootDescriptor.getCustomSQLInsert(), collectionBootDescriptor.isCustomInsertCallable() ), new MutationDetails( MutationType.UPDATE, - determineExpectation( - collectionBootDescriptor.getCustomSQLUpdateCheckStyle(), - collectionBootDescriptor.getCustomSQLUpdate(), - collectionBootDescriptor.isCustomUpdateCallable() - ), + createExpectation( collectionBootDescriptor.getUpdateExpectation(), + collectionBootDescriptor.isCustomUpdateCallable()), collectionBootDescriptor.getCustomSQLUpdate(), collectionBootDescriptor.isCustomUpdateCallable() ), collectionBootDescriptor.getKey().isCascadeDeleteEnabled(), new MutationDetails( MutationType.DELETE, - determineExpectation( - collectionBootDescriptor.getCustomSQLDeleteAllCheckStyle(), - collectionBootDescriptor.getCustomSQLDeleteAll(), - collectionBootDescriptor.isCustomDeleteAllCallable(), - Expectations.NONE - ), + collectionBootDescriptor.isCustomDeleteAllCallable() || collectionBootDescriptor.getDeleteAllExpectation() != null + ? createExpectation( collectionBootDescriptor.getDeleteAllExpectation(), + collectionBootDescriptor.isCustomDeleteAllCallable() ) + : new Expectation.None(), collectionBootDescriptor.getCustomSQLDeleteAll(), collectionBootDescriptor.isCustomDeleteAllCallable() ), new MutationDetails( MutationType.DELETE, - determineExpectation( - collectionBootDescriptor.getCustomSQLDeleteCheckStyle(), - collectionBootDescriptor.getCustomSQLDelete(), - collectionBootDescriptor.isCustomDeleteCallable() - ), + createExpectation( collectionBootDescriptor.getDeleteExpectation(), + collectionBootDescriptor.isCustomDeleteCallable()), collectionBootDescriptor.getCustomSQLDelete(), collectionBootDescriptor.isCustomDeleteCallable() ) ); } - private static Expectation determineExpectation( - ExecuteUpdateResultCheckStyle explicitStyle, - String customSql, - boolean customSqlCallable, - Expectation fallback) { - if ( explicitStyle != null ) { - return Expectations.appropriateExpectation( explicitStyle ); - } - - if ( customSql == null ) { - return fallback; - } - - return Expectations.appropriateExpectation( - ExecuteUpdateResultCheckStyle.determineDefault( customSql, customSqlCallable ) - ); - } - - private static Expectation determineExpectation( - ExecuteUpdateResultCheckStyle explicitStyle, - String customSql, - boolean customSqlCallable) { - return determineExpectation( explicitStyle, customSql, customSqlCallable, Expectations.BASIC ); - } - protected JdbcMutationOperation buildDeleteAllOperation(MutatingTableReference tableReference) { if ( tableMapping.getDeleteDetails().getCustomSql() != null ) { return buildCustomSqlDeleteAllOperation( tableReference ); diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java index 9154b2a4c8..229b8b92d3 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java @@ -7,7 +7,6 @@ package org.hibernate.persister.entity; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -23,7 +22,6 @@ import org.hibernate.boot.model.relational.SqlStringGenerationContext; import org.hibernate.cache.spi.access.EntityDataAccess; import org.hibernate.cache.spi.access.NaturalIdDataAccess; import org.hibernate.dialect.Dialect; -import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle; import org.hibernate.internal.DynamicFilterAliasGenerator; import org.hibernate.internal.FilterAliasGenerator; import org.hibernate.internal.util.collections.ArrayHelper; @@ -52,7 +50,6 @@ import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.persister.internal.SqlFragmentPredicate; import org.hibernate.persister.spi.PersisterCreationContext; import org.hibernate.query.sqm.function.SqmFunctionRegistry; -import org.hibernate.spi.NavigablePath; import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.tree.from.NamedTableReference; import org.hibernate.sql.ast.tree.from.TableGroup; @@ -61,8 +58,6 @@ import org.hibernate.sql.ast.tree.from.TableReferenceJoin; import org.hibernate.sql.ast.tree.from.UnknownTableReferenceException; import org.hibernate.sql.model.ast.builder.MutationGroupBuilder; import org.hibernate.sql.model.ast.builder.TableInsertBuilder; -import org.hibernate.sql.results.graph.DomainResult; -import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.type.BasicType; import org.hibernate.type.BasicTypeRegistry; import org.hibernate.type.CompositeType; @@ -78,7 +73,7 @@ import static org.hibernate.internal.util.collections.ArrayHelper.toIntArray; import static org.hibernate.internal.util.collections.ArrayHelper.toStringArray; import static org.hibernate.internal.util.collections.CollectionHelper.linkedMapOfSize; import static org.hibernate.internal.util.collections.CollectionHelper.mapOfSize; -import static org.hibernate.jdbc.Expectations.appropriateExpectation; +import static org.hibernate.jdbc.Expectations.createExpectation; import static org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper.buildEncapsulatedCompositeIdentifierMapping; import static org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper.buildNonEncapsulatedCompositeIdentifierMapping; import static org.hibernate.persister.entity.DiscriminatorHelper.NOT_NULL_DISCRIMINATOR; @@ -162,7 +157,7 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { // Span of the tables directly mapped by this entity and super-classes, if any private final int coreTableSpan; private final int subclassCoreTableSpan; - // only contains values for SecondaryTables, ie. not tables part of the "coreTableSpan" + // only contains values for SecondaryTables, i.e. not tables part of the "coreTableSpan" private final boolean[] isNullableTable; private final boolean[] isInverseTable; @@ -409,28 +404,16 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { isInverseTable[jk] = false; customSQLInsert[jk] = currentClass.getCustomSQLInsert(); - insertCallable[jk] = customSQLInsert[jk] != null && currentClass.isCustomInsertCallable(); - insertExpectations[jk] = appropriateExpectation( - currentClass.getInsertCheckStyle() == null - ? ExecuteUpdateResultCheckStyle.determineDefault( customSQLInsert[jk], insertCallable[jk] ) - : currentClass.getInsertCheckStyle() - ); + insertCallable[jk] = currentClass.isCustomInsertCallable(); + insertExpectations[jk] = createExpectation( currentClass.getInsertExpectation(), insertCallable[jk] ); customSQLUpdate[jk] = currentClass.getCustomSQLUpdate(); - updateCallable[jk] = customSQLUpdate[jk] != null && currentClass.isCustomUpdateCallable(); - updateExpectations[jk] = appropriateExpectation( - currentClass.getUpdateCheckStyle() == null - ? ExecuteUpdateResultCheckStyle.determineDefault( customSQLUpdate[jk], updateCallable[jk] ) - : currentClass.getUpdateCheckStyle() - ); + updateCallable[jk] = currentClass.isCustomUpdateCallable(); + updateExpectations[jk] = createExpectation( currentClass.getUpdateExpectation(), updateCallable[jk] ); customSQLDelete[jk] = currentClass.getCustomSQLDelete(); - deleteCallable[jk] = customSQLDelete[jk] != null && currentClass.isCustomDeleteCallable(); - deleteExpectations[jk] = appropriateExpectation( - currentClass.getDeleteCheckStyle() == null - ? ExecuteUpdateResultCheckStyle.determineDefault( customSQLDelete[jk], deleteCallable[jk] ) - : currentClass.getDeleteCheckStyle() - ); + deleteCallable[jk] = currentClass.isCustomDeleteCallable(); + deleteExpectations[jk] = createExpectation( currentClass.getDeleteExpectation(), deleteCallable[jk] ); jk--; currentClass = currentClass.getSuperclass(); @@ -446,28 +429,16 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { isNullableTable[j] = join.isOptional(); customSQLInsert[j] = join.getCustomSQLInsert(); - insertCallable[j] = customSQLInsert[j] != null && join.isCustomInsertCallable(); - insertExpectations[j] = appropriateExpectation( - join.getInsertCheckStyle() == null - ? ExecuteUpdateResultCheckStyle.determineDefault( customSQLInsert[j], insertCallable[j] ) - : join.getInsertCheckStyle() - ); + insertCallable[j] = join.isCustomInsertCallable(); + insertExpectations[j] = createExpectation( join.getInsertExpectation(), insertCallable[j] ); customSQLUpdate[j] = join.getCustomSQLUpdate(); - updateCallable[j] = customSQLUpdate[j] != null && join.isCustomUpdateCallable(); - updateExpectations[j] = appropriateExpectation( - join.getUpdateCheckStyle() == null - ? ExecuteUpdateResultCheckStyle.determineDefault( customSQLUpdate[j], updateCallable[j] ) - : join.getUpdateCheckStyle() - ); + updateCallable[j] = join.isCustomUpdateCallable(); + updateExpectations[j] = createExpectation( join.getUpdateExpectation(), updateCallable[j] ); customSQLDelete[j] = join.getCustomSQLDelete(); - deleteCallable[j] = customSQLDelete[j] != null && join.isCustomDeleteCallable(); - deleteExpectations[j] = appropriateExpectation( - join.getDeleteCheckStyle() == null - ? ExecuteUpdateResultCheckStyle.determineDefault( customSQLDelete[j], deleteCallable[j] ) - : join.getDeleteCheckStyle() - ); + deleteCallable[j] = join.isCustomDeleteCallable(); + deleteExpectations[j] = createExpectation( join.getDeleteExpectation(), deleteCallable[j] ); j++; } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java index 460f3fdd67..aa48bd8769 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java @@ -18,7 +18,6 @@ import org.hibernate.Remove; import org.hibernate.cache.spi.access.EntityDataAccess; import org.hibernate.cache.spi.access.NaturalIdDataAccess; import org.hibernate.dialect.Dialect; -import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle; import org.hibernate.internal.DynamicFilterAliasGenerator; import org.hibernate.internal.FilterAliasGenerator; import org.hibernate.internal.util.collections.ArrayHelper; @@ -49,7 +48,7 @@ import static org.hibernate.internal.util.collections.ArrayHelper.toBooleanArray import static org.hibernate.internal.util.collections.ArrayHelper.toIntArray; import static org.hibernate.internal.util.collections.ArrayHelper.toStringArray; import static org.hibernate.internal.util.collections.CollectionHelper.toSmallMap; -import static org.hibernate.jdbc.Expectations.appropriateExpectation; +import static org.hibernate.jdbc.Expectations.createExpectation; import static org.hibernate.persister.entity.DiscriminatorHelper.NOT_NULL_DISCRIMINATOR; import static org.hibernate.persister.entity.DiscriminatorHelper.NULL_DISCRIMINATOR; import static org.hibernate.sql.model.ast.builder.TableMutationBuilder.NULL; @@ -170,28 +169,16 @@ public class SingleTableEntityPersister extends AbstractEntityPersister { deleteExpectations = new Expectation[joinSpan]; customSQLInsert[0] = persistentClass.getCustomSQLInsert(); - insertCallable[0] = customSQLInsert[0] != null && persistentClass.isCustomInsertCallable(); - insertExpectations[0] = appropriateExpectation( - persistentClass.getInsertCheckStyle() == null - ? ExecuteUpdateResultCheckStyle.determineDefault( customSQLInsert[0], insertCallable[0] ) - : persistentClass.getInsertCheckStyle() - ); + insertCallable[0] = persistentClass.isCustomInsertCallable(); + insertExpectations[0] = createExpectation( persistentClass.getInsertExpectation(), insertCallable[0] ); customSQLUpdate[0] = persistentClass.getCustomSQLUpdate(); - updateCallable[0] = customSQLUpdate[0] != null && persistentClass.isCustomUpdateCallable(); - updateExpectations[0] = appropriateExpectation( - persistentClass.getUpdateCheckStyle() == null - ? ExecuteUpdateResultCheckStyle.determineDefault( customSQLUpdate[0], updateCallable[0] ) - : persistentClass.getUpdateCheckStyle() - ); + updateCallable[0] = persistentClass.isCustomUpdateCallable(); + updateExpectations[0] = createExpectation( persistentClass.getUpdateExpectation(), updateCallable[0] ); customSQLDelete[0] = persistentClass.getCustomSQLDelete(); - deleteCallable[0] = customSQLDelete[0] != null && persistentClass.isCustomDeleteCallable(); - deleteExpectations[0] = appropriateExpectation( - persistentClass.getDeleteCheckStyle() == null - ? ExecuteUpdateResultCheckStyle.determineDefault( customSQLDelete[0], deleteCallable[0] ) - : persistentClass.getDeleteCheckStyle() - ); + deleteCallable[0] = persistentClass.isCustomDeleteCallable(); + deleteExpectations[0] = createExpectation( persistentClass.getDeleteExpectation(), deleteCallable[0] ); // JOINS @@ -207,28 +194,16 @@ public class SingleTableEntityPersister extends AbstractEntityPersister { cascadeDeleteEnabled[j] = join.getKey().isCascadeDeleteEnabled() && dialect.supportsCascadeDelete(); customSQLInsert[j] = join.getCustomSQLInsert(); - insertCallable[j] = customSQLInsert[j] != null && join.isCustomInsertCallable(); - insertExpectations[j] = appropriateExpectation( - join.getInsertCheckStyle() == null - ? ExecuteUpdateResultCheckStyle.determineDefault( customSQLInsert[j], insertCallable[j] ) - : join.getInsertCheckStyle() - ); + insertCallable[j] = join.isCustomInsertCallable(); + insertExpectations[j] = createExpectation( join.getInsertExpectation(), insertCallable[j] ); customSQLUpdate[j] = join.getCustomSQLUpdate(); - updateCallable[j] = customSQLUpdate[j] != null && join.isCustomUpdateCallable(); - updateExpectations[j] = appropriateExpectation( - join.getUpdateCheckStyle() == null - ? ExecuteUpdateResultCheckStyle.determineDefault( customSQLUpdate[j], updateCallable[j] ) - : join.getUpdateCheckStyle() - ); + updateCallable[j] = join.isCustomUpdateCallable(); + updateExpectations[j] = createExpectation( join.getUpdateExpectation(), updateCallable[j] ); customSQLDelete[j] = join.getCustomSQLDelete(); - deleteCallable[j] = customSQLDelete[j] != null && join.isCustomDeleteCallable(); - deleteExpectations[j] = appropriateExpectation( - join.getDeleteCheckStyle() == null - ? ExecuteUpdateResultCheckStyle.determineDefault( customSQLDelete[j], deleteCallable[j] ) - : join.getDeleteCheckStyle() - ); + deleteCallable[j] = join.isCustomDeleteCallable(); + deleteExpectations[j] = createExpectation( join.getDeleteExpectation(), deleteCallable[j] ); keyColumnNames[j] = new String[join.getKey().getColumnSpan()]; diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java index 680285ff98..01420cab10 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java @@ -30,7 +30,6 @@ import org.hibernate.boot.Metadata; import org.hibernate.cache.spi.access.EntityDataAccess; import org.hibernate.cache.spi.access.NaturalIdDataAccess; import org.hibernate.dialect.Dialect; -import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle; import org.hibernate.id.IdentityGenerator; import org.hibernate.internal.FilterAliasGenerator; import org.hibernate.internal.StaticFilterAliasGenerator; @@ -65,7 +64,7 @@ import org.hibernate.type.StandardBasicTypes; import static org.hibernate.internal.util.collections.ArrayHelper.to2DStringArray; import static org.hibernate.internal.util.collections.ArrayHelper.toStringArray; -import static org.hibernate.jdbc.Expectations.appropriateExpectation; +import static org.hibernate.jdbc.Expectations.createExpectation; /** * An {@link EntityPersister} implementing the @@ -125,41 +124,20 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister { subclassTableNames = new String[]{tableName}; //Custom SQL - String sql; - boolean callable; - ExecuteUpdateResultCheckStyle checkStyle; - sql = persistentClass.getCustomSQLInsert(); - callable = sql != null && persistentClass.isCustomInsertCallable(); - checkStyle = sql == null - ? ExecuteUpdateResultCheckStyle.COUNT - : persistentClass.getInsertCheckStyle() == null - ? ExecuteUpdateResultCheckStyle.determineDefault( sql, callable ) - : persistentClass.getInsertCheckStyle(); - customSQLInsert = new String[] {sql}; - insertCallable = new boolean[] {callable}; - insertExpectations = new Expectation[] { appropriateExpectation( checkStyle ) }; + customSQLInsert = new String[] { persistentClass.getCustomSQLInsert() }; + insertCallable = new boolean[] { persistentClass.isCustomInsertCallable() }; + insertExpectations = new Expectation[] { createExpectation( persistentClass.getInsertExpectation(), + persistentClass.isCustomInsertCallable() ) }; - sql = persistentClass.getCustomSQLUpdate(); - callable = sql != null && persistentClass.isCustomUpdateCallable(); - checkStyle = sql == null - ? ExecuteUpdateResultCheckStyle.COUNT - : persistentClass.getUpdateCheckStyle() == null - ? ExecuteUpdateResultCheckStyle.determineDefault( sql, callable ) - : persistentClass.getUpdateCheckStyle(); - customSQLUpdate = new String[] {sql}; - updateCallable = new boolean[] {callable}; - updateExpectations = new Expectation[] { appropriateExpectation( checkStyle ) }; + customSQLUpdate = new String[] { persistentClass.getCustomSQLUpdate() }; + updateCallable = new boolean[] { persistentClass.isCustomUpdateCallable() }; + updateExpectations = new Expectation[] { createExpectation( persistentClass.getUpdateExpectation(), + persistentClass.isCustomUpdateCallable() ) }; - sql = persistentClass.getCustomSQLDelete(); - callable = sql != null && persistentClass.isCustomDeleteCallable(); - checkStyle = sql == null - ? ExecuteUpdateResultCheckStyle.COUNT - : persistentClass.getDeleteCheckStyle() == null - ? ExecuteUpdateResultCheckStyle.determineDefault( sql, callable ) - : persistentClass.getDeleteCheckStyle(); - customSQLDelete = new String[] {sql}; - deleteCallable = new boolean[] {callable}; - deleteExpectations = new Expectation[] { appropriateExpectation( checkStyle ) }; + customSQLDelete = new String[] { persistentClass.getCustomSQLDelete() }; + deleteCallable = new boolean[] { persistentClass.isCustomDeleteCallable() }; + deleteExpectations = new Expectation[] { createExpectation( persistentClass.getDeleteExpectation(), + persistentClass.isCustomDeleteCallable() ) }; discriminatorValue = persistentClass.getSubclassId(); discriminatorSQLValue = String.valueOf( persistentClass.getSubclassId() ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/size/WhereClauseOrderBySizeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/hql/size/WhereClauseOrderBySizeTest.java index 96b8bb5a26..1d8ae2fcc5 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/size/WhereClauseOrderBySizeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/hql/size/WhereClauseOrderBySizeTest.java @@ -28,6 +28,7 @@ import org.hibernate.annotations.SQLRestriction; import org.hibernate.annotations.Where; import org.hibernate.dialect.H2Dialect; import org.hibernate.dialect.PostgreSQLDialect; +import org.hibernate.jdbc.Expectation; import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase; import org.hibernate.testing.RequiresDialect; @@ -141,7 +142,8 @@ public class WhereClauseOrderBySizeTest extends BaseEntityManagerFunctionalTestC } @Entity(name = "Book") - @SQLDelete(sql = "UPDATE Book SET deleted = true WHERE id = ?", check = ResultCheckStyle.COUNT) + @SQLDelete(sql = "UPDATE Book SET deleted = true WHERE id = ?", + verify = Expectation.RowCount.class) @SQLRestriction("deleted = false") public static class Book { @Id diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/attributebinder/typebinder/NoResultCheck.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/attributebinder/typebinder/NoResultCheck.java index 1f9f35a0e0..969b261cff 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/attributebinder/typebinder/NoResultCheck.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/attributebinder/typebinder/NoResultCheck.java @@ -3,6 +3,7 @@ package org.hibernate.orm.test.mapping.attributebinder.typebinder; import org.hibernate.annotations.TypeBinderType; import org.hibernate.binder.TypeBinder; import org.hibernate.boot.spi.MetadataBuildingContext; +import org.hibernate.jdbc.Expectation; import org.hibernate.mapping.Component; import org.hibernate.mapping.PersistentClass; @@ -11,7 +12,6 @@ import java.lang.annotation.Target; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import static org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle.NONE; @TypeBinderType(binder = NoResultCheck.Binder.class) @Retention(RUNTIME) @@ -20,9 +20,9 @@ public @interface NoResultCheck { class Binder implements TypeBinder { @Override public void bind(NoResultCheck annotation, MetadataBuildingContext buildingContext, PersistentClass persistentClass) { - persistentClass.setInsertCheckStyle(NONE); - persistentClass.setUpdateCheckStyle(NONE); - persistentClass.setDeleteCheckStyle(NONE); + persistentClass.setInsertExpectation(Expectation.None.class); + persistentClass.setUpdateExpectation(Expectation.None.class); + persistentClass.setDeleteExpectation(Expectation.None.class); } @Override diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/CustomSQLTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/CustomSQLTest.java index 946dc83a9c..a930e305e9 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/CustomSQLTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/CustomSQLTest.java @@ -26,6 +26,7 @@ import org.hibernate.annotations.SQLSelect; import org.hibernate.annotations.SQLUpdate; import org.hibernate.dialect.H2Dialect; import org.hibernate.dialect.PostgreSQLDialect; +import org.hibernate.jdbc.Expectation; import org.hibernate.metamodel.CollectionClassification; import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase; @@ -109,7 +110,7 @@ public class CustomSQLTest extends BaseEntityManagerFunctionalTestCase { @Entity(name = "Person") @SQLInsert( sql = "INSERT INTO person (name, id, valid) VALUES (?, ?, true) ", - check = ResultCheckStyle.COUNT + verify = Expectation.RowCount.class ) @SQLUpdate( sql = "UPDATE person SET name = ? where id = ? " diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/write/CustomSqlWithExpectationTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/write/CustomSqlWithExpectationTests.java new file mode 100644 index 0000000000..12cf385745 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/write/CustomSqlWithExpectationTests.java @@ -0,0 +1,107 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html. + */ +package org.hibernate.orm.test.write; + +import jakarta.persistence.Basic; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import org.hibernate.HibernateException; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLInsert; +import org.hibernate.annotations.SQLUpdate; +import org.hibernate.jdbc.Expectation; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +import java.sql.PreparedStatement; +import java.sql.SQLException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Steve Ebersole + */ +@DomainModel( annotatedClasses = CustomSqlWithExpectationTests.CustomEntity.class ) +@SessionFactory +public class CustomSqlWithExpectationTests { + @Test + public void testBasicOperations(SessionFactoryScope scope) { + + assertEquals(0,count); + + // insert + scope.inTransaction( (session) -> { + session.persist( new CustomEntity( 1, "csutmo" ) ); + } ); + + assertEquals(1,count); + + // update + scope.inTransaction( (session) -> { + final CustomEntity customEntity = session.get( CustomEntity.class, 1 ); + customEntity.setName( "custom" ); + } ); + + assertEquals(2,count); + + // delete + scope.inTransaction( (session) -> { + final CustomEntity customEntity = session.get( CustomEntity.class, 1 ); + assertThat( customEntity.getName() ).isEqualTo( "custom" ); + session.remove( customEntity ); + } ); + + assertEquals(3,count); + } + + static int count = 0; + + public static class Custom extends Expectation.RowCount { + @Override + public int prepare(PreparedStatement statement) throws SQLException, HibernateException { + count ++; + return super.prepare(statement); + } + } + + @Entity( name = "CustomEntity" ) + @Table( name = "custom_entity" ) + @SQLInsert( sql = "insert into custom_entity (name, id) values (?, ?)", verify = Custom.class ) + @SQLDelete( sql = "delete from custom_entity where id = ?", verify = Custom.class ) + @SQLUpdate( sql = "update custom_entity set name = ? where id = ? ", verify = Custom.class ) + public static class CustomEntity { + @Id + private Integer id; + @Basic + private String name; + + CustomEntity() { + // for use by Hibernate + } + + public CustomEntity(Integer id, String name) { + this.id = id; + this.name = name; + } + + public Integer getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } +}