HHH-15587 replace use of enums for specifying Expectations

use of class objects is simpler, more elegant, and more flexible
This commit is contained in:
Gavin King 2024-03-13 01:21:17 +01:00
parent 2451268f16
commit 2b8f363e7b
21 changed files with 676 additions and 354 deletions

View File

@ -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.
* <p>
@ -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}.
* <p>
* Statement batching is disabled when {@code PARAM} is selected.
*
* @see org.hibernate.jdbc.Expectation.OutParameter
*/
PARAM
}

View File

@ -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<? extends Expectation> 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;
/**

View File

@ -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<? extends Expectation> 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;
/**

View File

@ -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<? extends Expectation> 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;
/**

View File

@ -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<? extends Expectation> 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;
/**

View File

@ -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 );

View File

@ -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();

View File

@ -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<? extends Expectation> expectationClass(@Nullable ExecuteUpdateResultCheckStyle style) {
if ( style == null ) {
return null;
}
else {
return style.expectationClass();
}
}
public Class<? extends Expectation> 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");
}
}
}

View File

@ -5,13 +5,43 @@
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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.
* <p>
* 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.
* <p>
* An {@code Expectation} is usually selected via an annotation,
* for example:
* <pre>
* &#064;Entity
* &#064;SQLUpdate(sql = "update Record set uid = gen_random_uuid(), whatever = ? where id = ?",
* verify = Expectation.RowCount.class)
* class Record { ... }
* </pre>
*
* @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}.
* <p>
* 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;
}
}
}

View File

@ -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<? extends Expectation> 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;
}

View File

@ -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<? extends Expectation> insertExpectation;
private Class<? extends Expectation> updateExpectation;
private Class<? extends Expectation> deleteExpectation;
private Class<? extends Expectation> 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<? extends Expectation> getInsertExpectation() {
return insertExpectation;
}
public void setInsertExpectation(Class<? extends Expectation> insertExpectation) {
this.insertExpectation = insertExpectation;
}
public Class<? extends Expectation> getUpdateExpectation() {
return updateExpectation;
}
public void setUpdateExpectation(Class<? extends Expectation> updateExpectation) {
this.updateExpectation = updateExpectation;
}
public Class<? extends Expectation> getDeleteExpectation() {
return deleteExpectation;
}
public void setDeleteExpectation(Class<? extends Expectation> deleteExpectation) {
this.deleteExpectation = deleteExpectation;
}
public Class<? extends Expectation> getDeleteAllExpectation() {
return deleteAllExpectation;
}
public void setDeleteAllExpectation(Class<? extends Expectation> deleteAllExpectation) {
this.deleteAllExpectation = deleteAllExpectation;
}
}

View File

@ -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<? extends Expectation> insertExpectation;
private Class<? extends Expectation> updateExpectation;
private Class<? extends Expectation> 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<? extends Expectation> getInsertExpectation() {
return insertExpectation;
}
public void setInsertExpectation(Class<? extends Expectation> insertExpectation) {
this.insertExpectation = insertExpectation;
}
public Class<? extends Expectation> getUpdateExpectation() {
return updateExpectation;
}
public void setUpdateExpectation(Class<? extends Expectation> updateExpectation) {
this.updateExpectation = updateExpectation;
}
public Class<? extends Expectation> getDeleteExpectation() {
return deleteExpectation;
}
public void setDeleteExpectation(Class<? extends Expectation> deleteExpectation) {
this.deleteExpectation = deleteExpectation;
}
}

View File

@ -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<? extends Expectation> insertExpectation;
private Class<? extends Expectation> updateExpectation;
private Class<? extends Expectation> 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<? extends Expectation> getInsertExpectation() {
return insertExpectation;
}
public void setInsertExpectation(Class<? extends Expectation> insertExpectation) {
this.insertExpectation = insertExpectation;
}
public Class<? extends Expectation> getUpdateExpectation() {
return updateExpectation;
}
public void setUpdateExpectation(Class<? extends Expectation> updateExpectation) {
this.updateExpectation = updateExpectation;
}
public Class<? extends Expectation> getDeleteExpectation() {
return deleteExpectation;
}
public void setDeleteExpectation(Class<? extends Expectation> deleteExpectation) {
this.deleteExpectation = deleteExpectation;
}
}

View File

@ -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 );

View File

@ -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++;
}

View File

@ -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()];

View File

@ -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() );

View File

@ -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

View File

@ -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<NoResultCheck> {
@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

View File

@ -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 = ? "

View File

@ -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;
}
}
}