HHH-12387 - Immutable entities can be updated via bulk update queries
This commit is contained in:
parent
ae0dfdc779
commit
b0e591f01d
|
@ -55,6 +55,7 @@ import org.hibernate.jpa.JpaCompliance;
|
||||||
import org.hibernate.jpa.spi.JpaComplianceImpl;
|
import org.hibernate.jpa.spi.JpaComplianceImpl;
|
||||||
import org.hibernate.loader.BatchFetchStyle;
|
import org.hibernate.loader.BatchFetchStyle;
|
||||||
import org.hibernate.proxy.EntityNotFoundDelegate;
|
import org.hibernate.proxy.EntityNotFoundDelegate;
|
||||||
|
import org.hibernate.query.ImmutableEntityUpdateQueryHandlingMode;
|
||||||
import org.hibernate.query.criteria.LiteralHandlingMode;
|
import org.hibernate.query.criteria.LiteralHandlingMode;
|
||||||
import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode;
|
import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode;
|
||||||
import org.hibernate.resource.jdbc.spi.StatementInspector;
|
import org.hibernate.resource.jdbc.spi.StatementInspector;
|
||||||
|
@ -88,6 +89,7 @@ import static org.hibernate.cfg.AvailableSettings.FAIL_ON_PAGINATION_OVER_COLLEC
|
||||||
import static org.hibernate.cfg.AvailableSettings.FLUSH_BEFORE_COMPLETION;
|
import static org.hibernate.cfg.AvailableSettings.FLUSH_BEFORE_COMPLETION;
|
||||||
import static org.hibernate.cfg.AvailableSettings.GENERATE_STATISTICS;
|
import static org.hibernate.cfg.AvailableSettings.GENERATE_STATISTICS;
|
||||||
import static org.hibernate.cfg.AvailableSettings.HQL_BULK_ID_STRATEGY;
|
import static org.hibernate.cfg.AvailableSettings.HQL_BULK_ID_STRATEGY;
|
||||||
|
import static org.hibernate.cfg.AvailableSettings.IMMUTABLE_ENTITY_UPDATE_QUERY_HANDLING_MODE;
|
||||||
import static org.hibernate.cfg.AvailableSettings.INTERCEPTOR;
|
import static org.hibernate.cfg.AvailableSettings.INTERCEPTOR;
|
||||||
import static org.hibernate.cfg.AvailableSettings.JDBC_TIME_ZONE;
|
import static org.hibernate.cfg.AvailableSettings.JDBC_TIME_ZONE;
|
||||||
import static org.hibernate.cfg.AvailableSettings.JDBC_TYLE_PARAMS_ZERO_BASE;
|
import static org.hibernate.cfg.AvailableSettings.JDBC_TYLE_PARAMS_ZERO_BASE;
|
||||||
|
@ -229,6 +231,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
|
||||||
private TimeZone jdbcTimeZone;
|
private TimeZone jdbcTimeZone;
|
||||||
private boolean queryParametersValidationEnabled;
|
private boolean queryParametersValidationEnabled;
|
||||||
private LiteralHandlingMode criteriaLiteralHandlingMode;
|
private LiteralHandlingMode criteriaLiteralHandlingMode;
|
||||||
|
private ImmutableEntityUpdateQueryHandlingMode immutableEntityUpdateQueryHandlingMode;
|
||||||
|
|
||||||
private Map<String, SQLFunction> sqlFunctions;
|
private Map<String, SQLFunction> sqlFunctions;
|
||||||
|
|
||||||
|
@ -469,6 +472,10 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
|
||||||
configurationSettings,
|
configurationSettings,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.immutableEntityUpdateQueryHandlingMode = ImmutableEntityUpdateQueryHandlingMode.interpret(
|
||||||
|
configurationSettings.get( IMMUTABLE_ENTITY_UPDATE_QUERY_HANDLING_MODE )
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
|
@ -952,6 +959,11 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
|
||||||
return this.criteriaLiteralHandlingMode;
|
return this.criteriaLiteralHandlingMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ImmutableEntityUpdateQueryHandlingMode getImmutableEntityUpdateQueryHandlingMode() {
|
||||||
|
return immutableEntityUpdateQueryHandlingMode;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean jdbcStyleParamsZeroBased() {
|
public boolean jdbcStyleParamsZeroBased() {
|
||||||
return this.jdbcStyleParamsZeroBased;
|
return this.jdbcStyleParamsZeroBased;
|
||||||
|
|
|
@ -25,6 +25,7 @@ import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||||
import org.hibernate.jpa.JpaCompliance;
|
import org.hibernate.jpa.JpaCompliance;
|
||||||
import org.hibernate.loader.BatchFetchStyle;
|
import org.hibernate.loader.BatchFetchStyle;
|
||||||
import org.hibernate.proxy.EntityNotFoundDelegate;
|
import org.hibernate.proxy.EntityNotFoundDelegate;
|
||||||
|
import org.hibernate.query.ImmutableEntityUpdateQueryHandlingMode;
|
||||||
import org.hibernate.query.criteria.LiteralHandlingMode;
|
import org.hibernate.query.criteria.LiteralHandlingMode;
|
||||||
import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode;
|
import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode;
|
||||||
import org.hibernate.resource.jdbc.spi.StatementInspector;
|
import org.hibernate.resource.jdbc.spi.StatementInspector;
|
||||||
|
@ -411,4 +412,9 @@ public class AbstractDelegatingSessionFactoryOptions implements SessionFactoryOp
|
||||||
public boolean isFailOnPaginationOverCollectionFetchEnabled() {
|
public boolean isFailOnPaginationOverCollectionFetchEnabled() {
|
||||||
return delegate.isFailOnPaginationOverCollectionFetchEnabled();
|
return delegate.isFailOnPaginationOverCollectionFetchEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ImmutableEntityUpdateQueryHandlingMode getImmutableEntityUpdateQueryHandlingMode() {
|
||||||
|
return delegate.getImmutableEntityUpdateQueryHandlingMode();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||||
import org.hibernate.jpa.JpaCompliance;
|
import org.hibernate.jpa.JpaCompliance;
|
||||||
import org.hibernate.loader.BatchFetchStyle;
|
import org.hibernate.loader.BatchFetchStyle;
|
||||||
import org.hibernate.proxy.EntityNotFoundDelegate;
|
import org.hibernate.proxy.EntityNotFoundDelegate;
|
||||||
|
import org.hibernate.query.ImmutableEntityUpdateQueryHandlingMode;
|
||||||
import org.hibernate.query.criteria.LiteralHandlingMode;
|
import org.hibernate.query.criteria.LiteralHandlingMode;
|
||||||
import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode;
|
import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode;
|
||||||
import org.hibernate.resource.jdbc.spi.StatementInspector;
|
import org.hibernate.resource.jdbc.spi.StatementInspector;
|
||||||
|
@ -275,4 +276,8 @@ public interface SessionFactoryOptions {
|
||||||
JpaCompliance getJpaCompliance();
|
JpaCompliance getJpaCompliance();
|
||||||
|
|
||||||
boolean isFailOnPaginationOverCollectionFetchEnabled();
|
boolean isFailOnPaginationOverCollectionFetchEnabled();
|
||||||
|
|
||||||
|
default ImmutableEntityUpdateQueryHandlingMode getImmutableEntityUpdateQueryHandlingMode() {
|
||||||
|
return ImmutableEntityUpdateQueryHandlingMode.WARNING;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,12 +10,14 @@ import java.util.function.Supplier;
|
||||||
|
|
||||||
import javax.persistence.GeneratedValue;
|
import javax.persistence.GeneratedValue;
|
||||||
|
|
||||||
|
import org.hibernate.HibernateException;
|
||||||
import org.hibernate.Transaction;
|
import org.hibernate.Transaction;
|
||||||
import org.hibernate.boot.MetadataBuilder;
|
import org.hibernate.boot.MetadataBuilder;
|
||||||
import org.hibernate.boot.registry.classloading.internal.TcclLookupPrecedence;
|
import org.hibernate.boot.registry.classloading.internal.TcclLookupPrecedence;
|
||||||
import org.hibernate.cache.spi.TimestampsCacheFactory;
|
import org.hibernate.cache.spi.TimestampsCacheFactory;
|
||||||
import org.hibernate.internal.log.DeprecationLogger;
|
import org.hibernate.internal.log.DeprecationLogger;
|
||||||
import org.hibernate.jpa.JpaCompliance;
|
import org.hibernate.jpa.JpaCompliance;
|
||||||
|
import org.hibernate.query.ImmutableEntityUpdateQueryHandlingMode;
|
||||||
import org.hibernate.query.internal.ParameterMetadataImpl;
|
import org.hibernate.query.internal.ParameterMetadataImpl;
|
||||||
import org.hibernate.resource.beans.container.spi.ExtendedBeanManager;
|
import org.hibernate.resource.beans.container.spi.ExtendedBeanManager;
|
||||||
import org.hibernate.resource.transaction.spi.TransactionCoordinator;
|
import org.hibernate.resource.transaction.spi.TransactionCoordinator;
|
||||||
|
@ -1861,4 +1863,24 @@ public interface AvailableSettings extends org.hibernate.jpa.AvailableSettings {
|
||||||
* @since 5.2.13
|
* @since 5.2.13
|
||||||
*/
|
*/
|
||||||
String FAIL_ON_PAGINATION_OVER_COLLECTION_FETCH = "hibernate.query.fail_on_pagination_over_collection_fetch";
|
String FAIL_ON_PAGINATION_OVER_COLLECTION_FETCH = "hibernate.query.fail_on_pagination_over_collection_fetch";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This setting defines how {@link org.hibernate.annotations.Immutable} entities are handled when executing a
|
||||||
|
* bulk update {@link javax.persistence.Query}.
|
||||||
|
*
|
||||||
|
* By default, the ({@link ImmutableEntityUpdateQueryHandlingMode#WARNING}) mode is used, meaning that
|
||||||
|
* a warning log message is issued when an {@link org.hibernate.annotations.Immutable} entity
|
||||||
|
* is to be updated via a bulk update statement.
|
||||||
|
*
|
||||||
|
* If the ({@link ImmutableEntityUpdateQueryHandlingMode#EXCEPTION}) mode is used, then a
|
||||||
|
* {@link HibernateException} is thrown instead.
|
||||||
|
* </p>
|
||||||
|
* Valid options are defined by the {@link ImmutableEntityUpdateQueryHandlingMode} enum.
|
||||||
|
* </p>
|
||||||
|
* The default value is {@link ImmutableEntityUpdateQueryHandlingMode#WARNING}
|
||||||
|
*
|
||||||
|
* @since 5.2.17
|
||||||
|
* @see org.hibernate.query.ImmutableEntityUpdateQueryHandlingMode
|
||||||
|
*/
|
||||||
|
String IMMUTABLE_ENTITY_UPDATE_QUERY_HANDLING_MODE = "hibernate.query.immutable_entity_update_query_handling_mode";
|
||||||
}
|
}
|
||||||
|
|
|
@ -450,4 +450,8 @@ public class HQLQueryPlan implements Serializable {
|
||||||
public boolean isSelect() {
|
public boolean isSelect() {
|
||||||
return !translators[0].isManipulationStatement();
|
return !translators[0].isManipulationStatement();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isUpdate() {
|
||||||
|
return translators[0].isUpdateStatement();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ import org.hibernate.hql.internal.ast.tree.FromElement;
|
||||||
import org.hibernate.hql.internal.ast.tree.InsertStatement;
|
import org.hibernate.hql.internal.ast.tree.InsertStatement;
|
||||||
import org.hibernate.hql.internal.ast.tree.QueryNode;
|
import org.hibernate.hql.internal.ast.tree.QueryNode;
|
||||||
import org.hibernate.hql.internal.ast.tree.Statement;
|
import org.hibernate.hql.internal.ast.tree.Statement;
|
||||||
|
import org.hibernate.hql.internal.ast.tree.UpdateStatement;
|
||||||
import org.hibernate.hql.internal.ast.util.ASTPrinter;
|
import org.hibernate.hql.internal.ast.util.ASTPrinter;
|
||||||
import org.hibernate.hql.internal.ast.util.ASTUtil;
|
import org.hibernate.hql.internal.ast.util.ASTUtil;
|
||||||
import org.hibernate.hql.internal.ast.util.NodeTraverser;
|
import org.hibernate.hql.internal.ast.util.NodeTraverser;
|
||||||
|
@ -503,6 +504,10 @@ public class QueryTranslatorImpl implements FilterTranslator {
|
||||||
return sqlAst.needsExecutor();
|
return sqlAst.needsExecutor();
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
|
public boolean isUpdateStatement() {
|
||||||
|
return SqlTokenTypes.UPDATE == sqlAst.getStatementType();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
public void validateScrollability() throws HibernateException {
|
public void validateScrollability() throws HibernateException {
|
||||||
// Impl Note: allows multiple collection fetches as long as the
|
// Impl Note: allows multiple collection fetches as long as the
|
||||||
// entire fecthed graph still "points back" to a single
|
// entire fecthed graph still "points back" to a single
|
||||||
|
|
|
@ -171,5 +171,9 @@ public interface QueryTranslator {
|
||||||
|
|
||||||
boolean isManipulationStatement();
|
boolean isManipulationStatement();
|
||||||
|
|
||||||
|
default boolean isUpdateStatement() {
|
||||||
|
return getQueryString().toLowerCase().trim().startsWith( "update" );
|
||||||
|
}
|
||||||
|
|
||||||
Class getDynamicInstantiationResultType();
|
Class getDynamicInstantiationResultType();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1800,5 +1800,8 @@ public interface CoreMessageLogger extends BasicLogger {
|
||||||
id = 486)
|
id = 486)
|
||||||
void agroalProviderClassNotFound();
|
void agroalProviderClassNotFound();
|
||||||
|
|
||||||
|
@LogMessage(level = WARN)
|
||||||
|
@Message(value = "The query: [%s] attempts to update an immutable entity: %s",
|
||||||
|
id = 487)
|
||||||
|
void immutableEntityUpdateQuery(String sourceQuery, String querySpaces);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import java.sql.Clob;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.NClob;
|
import java.sql.NClob;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -164,6 +165,7 @@ import org.hibernate.procedure.ProcedureCallMemento;
|
||||||
import org.hibernate.procedure.UnknownSqlResultSetMappingException;
|
import org.hibernate.procedure.UnknownSqlResultSetMappingException;
|
||||||
import org.hibernate.proxy.HibernateProxy;
|
import org.hibernate.proxy.HibernateProxy;
|
||||||
import org.hibernate.proxy.LazyInitializer;
|
import org.hibernate.proxy.LazyInitializer;
|
||||||
|
import org.hibernate.query.ImmutableEntityUpdateQueryHandlingMode;
|
||||||
import org.hibernate.query.Query;
|
import org.hibernate.query.Query;
|
||||||
import org.hibernate.query.criteria.internal.compile.CompilableCriteria;
|
import org.hibernate.query.criteria.internal.compile.CompilableCriteria;
|
||||||
import org.hibernate.query.criteria.internal.compile.CriteriaCompiler;
|
import org.hibernate.query.criteria.internal.compile.CriteriaCompiler;
|
||||||
|
@ -1505,6 +1507,8 @@ public final class SessionImpl
|
||||||
HQLQueryPlan plan = getQueryPlan( query, false );
|
HQLQueryPlan plan = getQueryPlan( query, false );
|
||||||
autoFlushIfRequired( plan.getQuerySpaces() );
|
autoFlushIfRequired( plan.getQuerySpaces() );
|
||||||
|
|
||||||
|
verifyImmutableEntityUpdate( plan );
|
||||||
|
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
int result = 0;
|
int result = 0;
|
||||||
try {
|
try {
|
||||||
|
@ -1518,6 +1522,42 @@ public final class SessionImpl
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void verifyImmutableEntityUpdate(HQLQueryPlan plan) {
|
||||||
|
if ( plan.isUpdate() ) {
|
||||||
|
for ( EntityPersister entityPersister : getSessionFactory().getMetamodel().entityPersisters().values() ) {
|
||||||
|
if ( !entityPersister.isMutable() ) {
|
||||||
|
List<Serializable> entityQuerySpaces = new ArrayList<>(
|
||||||
|
Arrays.asList( entityPersister.getQuerySpaces() )
|
||||||
|
);
|
||||||
|
entityQuerySpaces.retainAll( plan.getQuerySpaces() );
|
||||||
|
|
||||||
|
if ( !entityQuerySpaces.isEmpty() ) {
|
||||||
|
ImmutableEntityUpdateQueryHandlingMode immutableEntityUpdateQueryHandlingMode = getSessionFactory()
|
||||||
|
.getSessionFactoryOptions()
|
||||||
|
.getImmutableEntityUpdateQueryHandlingMode();
|
||||||
|
|
||||||
|
String querySpaces = Arrays.toString( entityQuerySpaces.toArray() );
|
||||||
|
|
||||||
|
switch ( immutableEntityUpdateQueryHandlingMode ) {
|
||||||
|
case WARNING:
|
||||||
|
log.immutableEntityUpdateQuery(plan.getSourceQuery(), querySpaces);
|
||||||
|
break;
|
||||||
|
case EXCEPTION:
|
||||||
|
throw new HibernateException(
|
||||||
|
"The query: [" + plan.getSourceQuery() + "] attempts to update an immutable entity: " + querySpaces
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"The "+ immutableEntityUpdateQueryHandlingMode + " is not supported!"
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int executeNativeUpdate(
|
public int executeNativeUpdate(
|
||||||
NativeSQLQuerySpecification nativeQuerySpecification,
|
NativeSQLQuerySpecification nativeQuerySpecification,
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* 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.query;
|
||||||
|
|
||||||
|
import org.hibernate.HibernateException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This enum defines how {@link org.hibernate.annotations.Immutable} entities are handled when executing a
|
||||||
|
* bulk update statement.
|
||||||
|
*
|
||||||
|
* By default, the ({@link ImmutableEntityUpdateQueryHandlingMode#WARNING}) mode is used, meaning that
|
||||||
|
* a warning log message is issued when an {@link org.hibernate.annotations.Immutable} entity
|
||||||
|
* is to be updated via a bulk update statement.
|
||||||
|
*
|
||||||
|
* If the ({@link ImmutableEntityUpdateQueryHandlingMode#EXCEPTION}) mode is used, then a
|
||||||
|
* {@link HibernateException} is thrown instead.
|
||||||
|
*
|
||||||
|
* @author Vlad Mihalcea
|
||||||
|
*/
|
||||||
|
public enum ImmutableEntityUpdateQueryHandlingMode {
|
||||||
|
|
||||||
|
WARNING,
|
||||||
|
EXCEPTION;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interpret the configured {@link ImmutableEntityUpdateQueryHandlingMode} value.
|
||||||
|
* Valid values are either a {@link ImmutableEntityUpdateQueryHandlingMode} object or its String representation.
|
||||||
|
* For string values, the matching is case insensitive,
|
||||||
|
* so you can use either {@code warning} or {@code exception} (case insensitive).
|
||||||
|
*
|
||||||
|
* @param mode configured {@link ImmutableEntityUpdateQueryHandlingMode} representation
|
||||||
|
* @return associated {@link ImmutableEntityUpdateQueryHandlingMode} object
|
||||||
|
*/
|
||||||
|
public static ImmutableEntityUpdateQueryHandlingMode interpret(Object mode) {
|
||||||
|
if ( mode == null ) {
|
||||||
|
return WARNING;
|
||||||
|
}
|
||||||
|
else if ( mode instanceof ImmutableEntityUpdateQueryHandlingMode ) {
|
||||||
|
return (ImmutableEntityUpdateQueryHandlingMode) mode;
|
||||||
|
}
|
||||||
|
else if ( mode instanceof String ) {
|
||||||
|
for ( ImmutableEntityUpdateQueryHandlingMode value : values() ) {
|
||||||
|
if ( value.name().equalsIgnoreCase( (String) mode ) ) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new HibernateException(
|
||||||
|
"Unrecognized immutable_entity_update_query_handling_mode value : " + mode
|
||||||
|
+ ". Supported values include 'warning' and 'exception''."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* 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.test.annotations.immutable;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.hibernate.HibernateException;
|
||||||
|
import org.hibernate.cfg.AvailableSettings;
|
||||||
|
|
||||||
|
import org.hibernate.testing.TestForIssue;
|
||||||
|
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||||
|
import org.hibernate.testing.logger.Triggerable;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Vlad Mihalcea
|
||||||
|
*/
|
||||||
|
@TestForIssue( jiraKey = "HHH-12387" )
|
||||||
|
public class ImmutableEntityUpdateQueryHandlingModeExceptionTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class[] getAnnotatedClasses() {
|
||||||
|
return new Class[] { Country.class, State.class, Photo.class };
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void addSettings(Map settings) {
|
||||||
|
settings.put( AvailableSettings.IMMUTABLE_ENTITY_UPDATE_QUERY_HANDLING_MODE, "exception" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBulkUpdate(){
|
||||||
|
Country _country = doInHibernate( this::sessionFactory, session -> {
|
||||||
|
Country country = new Country();
|
||||||
|
country.setName("Germany");
|
||||||
|
session.persist(country);
|
||||||
|
|
||||||
|
return country;
|
||||||
|
} );
|
||||||
|
|
||||||
|
try {
|
||||||
|
doInHibernate( this::sessionFactory, session -> {
|
||||||
|
session.createQuery(
|
||||||
|
"update Country " +
|
||||||
|
"set name = :name" )
|
||||||
|
.setParameter( "name", "N/A" )
|
||||||
|
.executeUpdate();
|
||||||
|
} );
|
||||||
|
fail("Should throw HibernateException");
|
||||||
|
}
|
||||||
|
catch (HibernateException e) {
|
||||||
|
assertEquals( "The query: [update Country set name = :name] attempts to update an immutable entity: [Country]", e.getMessage() );
|
||||||
|
}
|
||||||
|
|
||||||
|
doInHibernate( this::sessionFactory, session -> {
|
||||||
|
Country country = session.find(Country.class, _country.getId());
|
||||||
|
assertEquals( "Germany", country.getName() );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* 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.test.annotations.immutable;
|
||||||
|
|
||||||
|
import org.hibernate.HibernateException;
|
||||||
|
import org.hibernate.boot.model.process.internal.ScanningCoordinator;
|
||||||
|
import org.hibernate.internal.CoreMessageLogger;
|
||||||
|
import org.hibernate.internal.SessionImpl;
|
||||||
|
|
||||||
|
import org.hibernate.testing.TestForIssue;
|
||||||
|
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||||
|
import org.hibernate.testing.logger.LoggerInspectionRule;
|
||||||
|
import org.hibernate.testing.logger.Triggerable;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Vlad Mihalcea
|
||||||
|
*/
|
||||||
|
@TestForIssue( jiraKey = "HHH-12387" )
|
||||||
|
public class ImmutableEntityUpdateQueryHandlingModeWarningTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public LoggerInspectionRule logInspection = new LoggerInspectionRule(
|
||||||
|
Logger.getMessageLogger( CoreMessageLogger.class, SessionImpl.class.getName() ) );
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class[] getAnnotatedClasses() {
|
||||||
|
return new Class[] { Country.class, State.class, Photo.class };
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBulkUpdate(){
|
||||||
|
Country _country = doInHibernate( this::sessionFactory, session -> {
|
||||||
|
Country country = new Country();
|
||||||
|
country.setName("Germany");
|
||||||
|
session.persist(country);
|
||||||
|
|
||||||
|
return country;
|
||||||
|
} );
|
||||||
|
|
||||||
|
Triggerable triggerable = logInspection.watchForLogMessages( "HHH000487" );
|
||||||
|
triggerable.reset();
|
||||||
|
|
||||||
|
doInHibernate( this::sessionFactory, session -> {
|
||||||
|
session.createQuery(
|
||||||
|
"update Country " +
|
||||||
|
"set name = :name" )
|
||||||
|
.setParameter( "name", "N/A" )
|
||||||
|
.executeUpdate();
|
||||||
|
} );
|
||||||
|
|
||||||
|
assertEquals( "HHH000487: The query: [update Country set name = :name] attempts to update an immutable entity: [Country]", triggerable.triggerMessage() );
|
||||||
|
|
||||||
|
doInHibernate( this::sessionFactory, session -> {
|
||||||
|
Country country = session.find(Country.class, _country.getId());
|
||||||
|
assertEquals( "N/A", country.getName() );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue