HHH-17971 - Remove ALLOW_REFRESH_DETACHED_ENTITY

Signed-off-by: Jan Schatteman <jschatte@redhat.com>
This commit is contained in:
Jan Schatteman 2024-09-13 19:05:59 +02:00 committed by Jan Schatteman
parent 2be3000e40
commit 3818b6d99f
34 changed files with 122 additions and 872 deletions

View File

@ -886,16 +886,7 @@ See the discussion of non-identifier <<chapters/domain/basic_types.adoc#mapping-
[IMPORTANT]
====
Traditionally, Hibernate allowed detached entities to be refreshed.
Unfortunately, Jakarta Persistence prohibits this practice and specifies that an `IllegalArgumentException` should be thrown instead.
For this reason, when bootstrapping the Hibernate `SessionFactory` using the native API, the legacy detached entity refresh behavior is going to be preserved.
On the other hand, when bootstrapping Hibernate through the Jakarta Persistence `EntityManagerFactory` building process, detached entities are not allowed to be refreshed by default.
However, this default behavior can be overwritten through the `hibernate.allow_refresh_detached_entity` configuration property.
If this property is explicitly set to `true`, then you can refresh detached entities even when using the Jakarta Persistence bootstraps mechanism, therefore bypassing the Jakarta Persistence specification restriction.
For more about the `hibernate.allow_refresh_detached_entity` configuration property,
check out the <<appendices/Configurations.adoc#misc,Configurations>> section as well.
however, Jakarta Persistence prohibits this practice and specifies that an `IllegalArgumentException` should be thrown instead. This is the default behaviour from version 7.0 onwards.
====
[[pc-refresh-gotchas]]

View File

@ -426,11 +426,6 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement
return this;
}
@Override
public void disableRefreshDetachedEntity() {
this.optionsBuilder.disableRefreshDetachedEntity();
}
@Override
public void disableJtaTransactionAccess() {
this.optionsBuilder.disableJtaTransactionAccess();

View File

@ -75,7 +75,6 @@ import org.hibernate.type.format.jaxb.JaxbXmlFormatMapper;
import jakarta.persistence.criteria.Nulls;
import static org.hibernate.cfg.AvailableSettings.ALLOW_JTA_TRANSACTION_ACCESS;
import static org.hibernate.cfg.AvailableSettings.ALLOW_REFRESH_DETACHED_ENTITY;
import static org.hibernate.cfg.AvailableSettings.ALLOW_UPDATE_OUTSIDE_TRANSACTION;
import static org.hibernate.cfg.AvailableSettings.AUTO_CLOSE_SESSION;
import static org.hibernate.cfg.AvailableSettings.AUTO_EVICT_COLLECTION_CACHE;
@ -176,7 +175,6 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
private boolean jtaTransactionAccessEnabled;
private boolean allowOutOfTransactionUpdateOperations;
private boolean releaseResourcesOnCloseEnabled;
private boolean allowRefreshDetachedEntity;
// (JTA) transaction handling
private boolean jtaTrackByThread;
@ -335,12 +333,6 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
true
);
this.allowRefreshDetachedEntity = configurationService.getSetting(
ALLOW_REFRESH_DETACHED_ENTITY,
BOOLEAN,
false
);
this.flushBeforeCompletionEnabled = configurationService.getSetting( FLUSH_BEFORE_COMPLETION, BOOLEAN, true );
this.autoCloseSessionEnabled = configurationService.getSetting( AUTO_CLOSE_SESSION, BOOLEAN, false );
@ -906,11 +898,6 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
return jtaTransactionAccessEnabled;
}
@Override
public boolean isAllowRefreshDetachedEntity() {
return allowRefreshDetachedEntity;
}
@Override
public boolean isAllowOutOfTransactionUpdateOperations() {
return allowOutOfTransactionUpdateOperations;
@ -1624,10 +1611,6 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
this.collectionsInDefaultFetchGroupEnabled = enabled;
}
public void disableRefreshDetachedEntity() {
this.allowRefreshDetachedEntity = false;
}
public void disableJtaTransactionAccess() {
this.jtaTransactionAccessEnabled = false;
}

View File

@ -37,6 +37,8 @@ import org.hibernate.type.format.FormatMapper;
import jakarta.persistence.criteria.Nulls;
import static org.hibernate.internal.log.DeprecationLogger.DEPRECATION_LOGGER;
/**
* Convenience base class for custom implementations of {@link SessionFactoryOptions}
* using delegation.
@ -77,8 +79,13 @@ public class AbstractDelegatingSessionFactoryOptions implements SessionFactoryOp
return delegate.isJtaTransactionAccessEnabled();
}
/**
* @deprecated with no replacement.
*/
@Deprecated(since = "7.0", forRemoval = true)
@Override
public boolean isAllowRefreshDetachedEntity() {
DEPRECATION_LOGGER.deprecatedRefreshLockDetachedEntity();
return delegate.isAllowRefreshDetachedEntity();
}

View File

@ -5,6 +5,7 @@
package org.hibernate.boot.spi;
import org.hibernate.boot.SessionFactoryBuilder;
import static org.hibernate.internal.log.DeprecationLogger.DEPRECATION_LOGGER;
/**
* Additional SPI contract for {@link SessionFactoryBuilder}, mainly intended for
@ -21,10 +22,11 @@ public interface SessionFactoryBuilderImplementor extends SessionFactoryBuilder
void disableJtaTransactionAccess();
/**
* Called if {@link org.hibernate.cfg.AvailableSettings#ALLOW_REFRESH_DETACHED_ENTITY}
* is not enabled.
* @deprecated with no replacement.
*/
@Deprecated(since = "7.0", forRemoval = true)
default void disableRefreshDetachedEntity() {
DEPRECATION_LOGGER.deprecatedRefreshLockDetachedEntity();
}
/**

View File

@ -34,6 +34,8 @@ import org.hibernate.type.format.FormatMapper;
import jakarta.persistence.criteria.Nulls;
import static org.hibernate.internal.log.DeprecationLogger.DEPRECATION_LOGGER;
/**
* Aggregator of special options used to build the {@link org.hibernate.SessionFactory}.
*
@ -76,7 +78,12 @@ public interface SessionFactoryOptions extends QueryEngineOptions {
boolean isJtaTransactionAccessEnabled();
/**
* @deprecated with no replacement.
*/
@Deprecated(since = "7.0", forRemoval = true)
default boolean isAllowRefreshDetachedEntity() {
DEPRECATION_LOGGER.deprecatedRefreshLockDetachedEntity();
return false;
}

View File

@ -120,24 +120,6 @@ public interface AvailableSettings
*/
String DELAY_ENTITY_LOADER_CREATIONS = "hibernate.loader.delay_entity_loader_creations";
/**
* When enabled, allows calls to {@link jakarta.persistence.EntityManager#refresh(Object)}
* and {@link org.hibernate.Session#refresh(Object)} on a detached entity instance.
* <p>
* Values are {@code true}, which allows refreshing a detached instance and {@code false},
* which does not. When refreshing is disallowed, an {@link IllegalArgumentException}
* is thrown.
* <p>
* The default behavior is to allow refreshing a detached instance unless Hibernate
* is bootstrapped via JPA.
*
* @deprecated Will be removed with no replacement from ORM version 7 onwards
*
* @since 5.2
*/
@Deprecated(since="6.6", forRemoval = true)
String ALLOW_REFRESH_DETACHED_ENTITY = "hibernate.allow_refresh_detached_entity";
/**
* Specifies how Hibernate should behave when multiple representations of the same
* persistent entity instance, that is, multiple detached objects with the same

View File

@ -1245,12 +1245,10 @@ public class SessionImpl
}
private void checkEntityManaged(String entityName, Object entity) {
if ( !getSessionFactory().getSessionFactoryOptions().isAllowRefreshDetachedEntity() ) {
if ( !managed( entityName, entity ) ) {
throw new IllegalArgumentException( "Given entity is not associated with the persistence context" );
}
}
}
private void checkEntityManagedIfJpa(String entityName, Object entity) {
if ( getSessionFactory().getSessionFactoryOptions().isJpaBootstrap() ) {

View File

@ -187,4 +187,11 @@ public interface DeprecationLogger extends BasicLogger {
value = "Encountered use of deprecated annotation [%s] at %s."
)
void deprecatedAnnotation(Class<? extends Annotation> annotationType, String locationDescription);
@LogMessage(level = WARN)
@Message(
id = 90000034,
value = "Refreshing/locking detached entities is no longer allowed."
)
void deprecatedRefreshLockDetachedEntity();
}

View File

@ -1479,7 +1479,6 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil
// builder.applyInterceptor( sessionFactoryInterceptor );
// }
handleAllowJtaTransactionAccess( builder );
handleAllowDetachedEntity( builder );
addConfiguredSessionFactoryObserver( builder );
builder.addSessionFactoryObservers( ServiceRegistryCloser.INSTANCE );
builder.applyEntityNotFoundDelegate( JpaEntityNotFoundDelegate.INSTANCE );
@ -1500,16 +1499,6 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil
}
}
// will use user override value or default to false if not supplied to follow JPA spec
private void handleAllowDetachedEntity(SessionFactoryBuilder builder) {
final boolean allowRefreshDetachedEntity =
readBooleanConfigurationValue( AvailableSettings.ALLOW_REFRESH_DETACHED_ENTITY );
if ( !allowRefreshDetachedEntity
&& builder instanceof SessionFactoryBuilderImplementor implementor ) {
implementor.disableRefreshDetachedEntity();
}
}
// will use user override value or default to false if not supplied to follow JPA spec
private void handleAllowJtaTransactionAccess(SessionFactoryBuilder builder) {
final boolean jtaTransactionAccessEnabled =

View File

@ -5,15 +5,13 @@
package org.hibernate.orm.test.bytecode.enhancement.lazy.HHH_10708;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.testing.bytecode.enhancement.extension.BytecodeEnhanced;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.JiraKey;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -34,7 +32,6 @@ import java.util.Set;
)
@SessionFactory
@BytecodeEnhanced
@ServiceRegistry(settings = @Setting(name = AvailableSettings.ALLOW_REFRESH_DETACHED_ENTITY, value = "true"))
public class UnexpectedDeleteTest2 {
private Bar myBar;
@ -59,8 +56,10 @@ public class UnexpectedDeleteTest2 {
@Test
public void test(SessionFactoryScope scope) {
scope.inTransaction( s -> {
s.refresh( myBar );
assertFalse( myBar.foos.isEmpty() );
assertThrows(IllegalArgumentException.class,
() -> s.refresh( myBar ),
"Given entity is not associated with the persistence context"
);
// The issue is that currently, for some unknown reason, foos are deleted on flush
} );

View File

@ -6,7 +6,6 @@ package org.hibernate.orm.test.bytecode.enhancement.lazy.backref;
import org.hibernate.Hibernate;
import org.hibernate.LockMode;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.orm.test.bytecode.enhancement.lazy.NoDirtyCheckingContext;
import org.hibernate.orm.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking.DirtyCheckEnhancementContext;
@ -17,14 +16,13 @@ import org.hibernate.orm.test.collection.backref.map.compkey.Product;
import org.hibernate.testing.bytecode.enhancement.CustomEnhancementContext;
import org.hibernate.testing.bytecode.enhancement.extension.BytecodeEnhanced;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
@ -42,7 +40,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
@SessionFactory
@BytecodeEnhanced
@CustomEnhancementContext({ NoDirtyCheckingContext.class, DirtyCheckEnhancementContext.class })
@ServiceRegistry(settings = @Setting(name = AvailableSettings.ALLOW_REFRESH_DETACHED_ENTITY, value = "true"))
public class BackrefCompositeMapKeyTest {
@Test
@ -123,7 +120,7 @@ public class BackrefCompositeMapKeyTest {
}
@Test
public void testOrphanDeleteAfterLock(SessionFactoryScope scope) {
public void testCannotLockDetachedEntity(SessionFactoryScope scope) {
Product prod = new Product( "Widget" );
MapKey mapKey = new MapKey( "Top" );
scope.inTransaction(
@ -139,14 +136,16 @@ public class BackrefCompositeMapKeyTest {
scope.inTransaction(
session -> {
session.lock( prod, LockMode.READ );
prod.getParts().remove( mapKey );
assertThrows(IllegalArgumentException.class,
() -> session.lock( prod, LockMode.READ ),
"Given entity is not associated with the persistence context"
);
}
);
scope.inTransaction(
session -> {
assertNull( session.get( Part.class, "Widge" ) );
assertNotNull( session.get( Part.class, "Widge" ) );
assertNotNull( session.get( Part.class, "Get" ) );
session.remove( session.get( Product.class, "Widget" ) );
}

View File

@ -7,7 +7,6 @@ package org.hibernate.orm.test.bytecode.enhancement.orphan;
import org.hibernate.Hibernate;
import org.hibernate.LockMode;
import org.hibernate.bytecode.enhance.spi.DefaultEnhancementContext;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.internal.util.SerializationHelper;
import org.hibernate.orm.test.orphan.Part;
import org.hibernate.orm.test.orphan.Product;
@ -17,15 +16,14 @@ import org.hibernate.testing.bytecode.enhancement.EnhancerTestContext;
import org.hibernate.testing.bytecode.enhancement.extension.BytecodeEnhanced;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.JiraKey;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
@ -42,7 +40,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
EnhancerTestContext.class, // supports laziness and dirty-checking
DefaultEnhancementContext.class
})
@ServiceRegistry(settings = @Setting(name = AvailableSettings.ALLOW_REFRESH_DETACHED_ENTITY, value = "true"))
public class OrphanTest {
@AfterEach
@ -152,7 +149,7 @@ public class OrphanTest {
@Test
@SuppressWarnings("unchecked")
public void testOrphanDeleteAfterLock(SessionFactoryScope scope) {
public void testCannotLockDetachedEntity(SessionFactoryScope scope) {
Product prod = new Product();
Part part = new Part();
scope.inTransaction(
@ -172,14 +169,16 @@ public class OrphanTest {
scope.inTransaction(
session -> {
session.lock( prod, LockMode.READ );
prod.getParts().remove( part );
assertThrows(IllegalArgumentException.class,
() -> session.lock( prod, LockMode.READ ),
"Given entity is not associated with the persistence context"
);
}
);
scope.inTransaction(
session -> {
assertNull( session.get( Part.class, "Widge" ) );
assertNotNull( session.get( Part.class, "Widge" ) );
assertNotNull( session.get( Part.class, "Get" ) );
session.remove( session.get( Product.class, "Widget" ) );
}

View File

@ -19,18 +19,17 @@ import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import jakarta.persistence.Temporal;
import jakarta.persistence.TemporalType;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
/**
* Implementation of RefreshTest.
@ -44,7 +43,6 @@ import static org.junit.Assert.assertEquals;
}
)
@SessionFactory
@ServiceRegistry(settings = @Setting(name = AvailableSettings.ALLOW_REFRESH_DETACHED_ENTITY, value = "true"))
public class RefreshTest {
private JobBatch batch;
@ -67,11 +65,14 @@ public class RefreshTest {
}
@Test
void testRefreshCascade(SessionFactoryScope scope) {
void testCannotRefreshCascadeDetachedEntity(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.refresh( batch );
batch.jobs.forEach( job -> assertEquals( "Jobs not refreshed!", 1, job.status ) );
assertThrows(IllegalArgumentException.class,
() -> session.refresh( batch ),
"Given entity is not associated with the persistence context"
);
batch.jobs.forEach( job -> assertEquals( 0, job.status ) );
}
);
}

View File

@ -6,18 +6,16 @@ package org.hibernate.orm.test.collection.backref.map.compkey;
import org.hibernate.Hibernate;
import org.hibernate.LockMode;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
@ -33,7 +31,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
)
)
@SessionFactory
@ServiceRegistry(settings = @Setting(name = AvailableSettings.ALLOW_REFRESH_DETACHED_ENTITY, value = "true"))
public class BackrefCompositeMapKeyTest {
@Test
@ -114,7 +111,7 @@ public class BackrefCompositeMapKeyTest {
}
@Test
public void testOrphanDeleteAfterLock(SessionFactoryScope scope) {
public void testCannotLockDetachedEntity(SessionFactoryScope scope) {
Product prod = new Product( "Widget" );
MapKey mapKey = new MapKey( "Top" );
scope.inTransaction(
@ -130,14 +127,16 @@ public class BackrefCompositeMapKeyTest {
scope.inTransaction(
session -> {
session.lock( prod, LockMode.READ );
prod.getParts().remove( mapKey );
assertThrows(IllegalArgumentException.class,
() -> session.lock( prod, LockMode.READ ),
"Given entity is not associated with the persistence context"
);
}
);
scope.inTransaction(
session -> {
assertNull( session.get( Part.class, "Widge" ) );
assertNotNull( session.get( Part.class, "Widge" ) );
assertNotNull( session.get( Part.class, "Get" ) );
session.remove( session.get( Product.class, "Widget" ) );
}

View File

@ -63,7 +63,6 @@ public class ImmutableTest extends BaseSessionFactoryFunctionalTest {
protected void applySettings(StandardServiceRegistryBuilder builer) {
builer.applySetting( Environment.GENERATE_STATISTICS, "true" );
builer.applySetting( Environment.STATEMENT_BATCH_SIZE, "0" );
builer.applySetting( Environment.ALLOW_REFRESH_DETACHED_ENTITY, "true" );
}
@Override
@ -365,18 +364,9 @@ public class ImmutableTest extends BaseSessionFactoryFunctionalTest {
inTransaction(
s -> {
// refresh detached
s.refresh( contract );
assertTrue( s.isReadOnly( contract ) );
assertEquals( "gavin", contract.getCustomerName() );
assertEquals( 2, contract.getVariations().size() );
Iterator<ContractVariation> it = contract.getVariations().iterator();
ContractVariation cv1 = it.next();
assertEquals( "expensive", cv1.getText() );
ContractVariation cv2 = it.next();
assertEquals( "more expensive", cv2.getText() );
assertTrue( s.isReadOnly( cv1 ) );
assertTrue( s.isReadOnly( cv2 ) );
Contract c = s.get(Contract.class, contract.getId());
c.setCustomerName( "joe" );
s.merge( c );
}
);
@ -384,15 +374,13 @@ public class ImmutableTest extends BaseSessionFactoryFunctionalTest {
assertUpdateCount( 0 );
clearCounts();
contract.setCustomerName( "joe" );
inTransaction(
s -> {
s.refresh( contract );
assertTrue( s.isReadOnly( contract ) );
assertEquals( "gavin", contract.getCustomerName() );
assertEquals( 2, contract.getVariations().size() );
Iterator<ContractVariation> it = contract.getVariations().iterator();
Contract c = s.get(Contract.class, contract.getId());
assertTrue( s.isReadOnly( c ) );
assertEquals( "gavin", c.getCustomerName() );
assertEquals( 2, c.getVariations().size() );
Iterator<ContractVariation> it = c.getVariations().iterator();
ContractVariation cv1 = it.next();
assertEquals( "expensive", cv1.getText() );
ContractVariation cv2 = it.next();
@ -401,11 +389,6 @@ public class ImmutableTest extends BaseSessionFactoryFunctionalTest {
assertTrue( s.isReadOnly( cv2 ) );
}
);
// refresh updated detached
assertInsertCount( 0 );
assertUpdateCount( 0 );
clearCounts();
inTransaction(
s -> {

View File

@ -9,15 +9,12 @@ import java.util.Iterator;
import org.hibernate.LockMode;
import org.hibernate.StaleObjectStateException;
import org.hibernate.StaleStateException;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.metamodel.MappingMetamodel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@ -32,6 +29,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
@ -40,7 +38,6 @@ import static org.junit.jupiter.api.Assertions.fail;
* @author Gail Badner
*/
@SessionFactory(generateStatistics = true)
@ServiceRegistry(settings = @Setting(name = AvailableSettings.ALLOW_REFRESH_DETACHED_ENTITY, value = "true"))
public abstract class AbstractEntityWithManyToManyTest {
private boolean isPlanContractsInverse;
private boolean isPlanContractsBidirectional;
@ -171,9 +168,12 @@ public abstract class AbstractEntityWithManyToManyTest {
scope.inTransaction(
s -> {
s.lock( c, LockMode.NONE );
assertThrows(IllegalArgumentException.class,
() -> s.lock( c, LockMode.NONE ),
"Given entity is not associated with the persistence context"
);
Plan p = new Plan( "plan" );
p.addContract( c );
p.addContract( s.get(Contract.class, c.getId()) );
s.persist( p );
}
);

View File

@ -12,12 +12,9 @@ import jakarta.persistence.criteria.Root;
import org.hibernate.LockMode;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
@ -33,16 +30,15 @@ import static org.junit.jupiter.api.Assertions.assertSame;
xmlMappings = "org/hibernate/orm/test/joinedsubclass/Person.hbm.xml"
)
@SessionFactory
@ServiceRegistry(settings = @Setting(name = AvailableSettings.ALLOW_REFRESH_DETACHED_ENTITY, value = "true"))
public class JoinedSubclassTest {
@AfterEach
public void tearDown(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery( "delete from Employee" ).executeUpdate();
session.createQuery( "delete from Customer" ).executeUpdate();
session.createQuery( "delete from Person" ).executeUpdate();
session.createMutationQuery( "delete from Employee" ).executeUpdate();
session.createMutationQuery( "delete from Customer" ).executeUpdate();
session.createMutationQuery( "delete from Person" ).executeUpdate();
}
);
}
@ -63,15 +59,15 @@ public class JoinedSubclassTest {
Customer c = scope.fromTransaction(
session ->
session.get( Customer.class, new Long( e.getId() ) )
session.get( Customer.class, e.getId() )
);
assertNull( c );
scope.inTransaction(
session -> {
Employee employee = session.get( Employee.class, new Long( e.getId() ) );
Customer customer = session.get( Customer.class, new Long( e.getId() ) );
Employee employee = session.get( Employee.class, e.getId() );
Customer customer = session.get( Customer.class, e.getId() );
assertNotNull( employee );
assertNull( customer );
}
@ -148,8 +144,8 @@ public class JoinedSubclassTest {
scope.inTransaction(
session -> {
session.lock( p, LockMode.PESSIMISTIC_WRITE );
session.lock( q, LockMode.PESSIMISTIC_WRITE );
session.lock( session.get(Person.class, p.getId()), LockMode.PESSIMISTIC_WRITE );
session.lock( session.get( Employee.class, q.getId()), LockMode.PESSIMISTIC_WRITE );
session.remove( p );
session.remove( q );
}

View File

@ -9,15 +9,12 @@ import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import org.hibernate.LockMode;
import org.hibernate.Session;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.testing.orm.junit.Jpa;
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.fail;
@ -26,9 +23,16 @@ import static org.junit.jupiter.api.Assertions.fail;
* @author Steve Ebersole
*/
@Jpa(annotatedClasses = {MergeWithTransientNonCascadedAssociationTest.Person.class,
MergeWithTransientNonCascadedAssociationTest.Address.class},
properties = @Setting(name = AvailableSettings.ALLOW_REFRESH_DETACHED_ENTITY, value = "true"))
MergeWithTransientNonCascadedAssociationTest.Address.class})
public class MergeWithTransientNonCascadedAssociationTest {
@AfterAll
public void tearDown(EntityManagerFactoryScope scope) {
scope.inTransaction(
entityManager -> entityManager.createQuery( "delete from Person" ).executeUpdate()
);
}
@Test
public void testMergeWithTransientNonCascadedAssociation(EntityManagerFactoryScope scope) {
Person person = new Person();
@ -54,14 +58,6 @@ public class MergeWithTransientNonCascadedAssociationTest {
}
}
);
scope.inTransaction(
entityManager -> {
person.address = null;
entityManager.unwrap( Session.class ).lock( person, LockMode.NONE );
entityManager.unwrap( Session.class ).remove( person );
}
);
}
@Entity(name = "Person")

View File

@ -1,69 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.orm.test.jpa.refresh;
import org.hibernate.Session;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.testing.orm.junit.JiraKey;
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.Jpa;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/**
* @author Andrea Boriero
*/
@JiraKey(value = "HHH-11188")
@Jpa(
annotatedClasses = {
TestEntity.class
},
integrationSettings = { @Setting(name = AvailableSettings.ALLOW_REFRESH_DETACHED_ENTITY, value = "true") }
)
public class RefreshDetachedInstanceWhenIsAllowedTest {
private TestEntity testEntity;
@BeforeEach
public void setUp(EntityManagerFactoryScope scope) {
testEntity = new TestEntity();
scope.inTransaction(
entityManager -> {
entityManager.persist( testEntity );
}
);
}
@AfterEach
public void tearDown(EntityManagerFactoryScope scope) {
scope.inTransaction(
entityManager -> {
entityManager.createQuery( "delete from TestEntity" ).executeUpdate();
}
);
}
@Test
public void testUnwrappedSessionRefreshDetachedInstance(EntityManagerFactoryScope scope) {
scope.inEntityManager(
entityManager -> {
final Session session = entityManager.unwrap( Session.class );
session.refresh( testEntity );
}
);
}
@Test
public void testRefreshDetachedInstance(EntityManagerFactoryScope scope) {
scope.inEntityManager(
entityManager -> {
entityManager.refresh( testEntity );
}
);
}
}

View File

@ -1,73 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.orm.test.jpa.refresh;
import org.hibernate.Session;
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.Jpa;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/**
* @author Andrea Boriero
*/
@Jpa(
annotatedClasses = {
TestEntity.class
}
)
public class RefreshDetachedInstanceWhenIsNotAllowedTest {
private TestEntity testEntity;
@BeforeEach
public void setUp(EntityManagerFactoryScope scope) {
testEntity = new TestEntity();
scope.inTransaction(
entityManager -> {
entityManager.persist( testEntity );
}
);
}
@AfterEach
public void tearDown(EntityManagerFactoryScope scope) {
scope.inTransaction(
entityManager -> {
entityManager.createQuery( "delete from TestEntity" ).executeUpdate();
}
);
}
@Test
public void testUnwrappedSessionRefreshDetachedInstance(EntityManagerFactoryScope scope) {
scope.inTransaction(
entityManager -> {
final Session session = entityManager.unwrap( Session.class );
Assertions.assertThrows(
IllegalArgumentException.class,
() -> session.refresh( testEntity ),
"Should have thrown an IllegalArgumentException"
);
}
);
}
@Test
public void testRefreshDetachedInstance(EntityManagerFactoryScope scope) {
scope.inTransaction(
entityManager -> {
Assertions.assertThrows(
IllegalArgumentException.class,
() -> entityManager.refresh( testEntity ),
"Should have thrown an IllegalArgumentException"
);
}
);
}
}

View File

@ -1,19 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.orm.test.jpa.refresh;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
/**
* @author Andrea Boriero
*/
@Entity
public class TestEntity {
@Id
@GeneratedValue
public long id;
}

View File

@ -10,7 +10,6 @@ import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.Setting;
import static org.hibernate.cfg.AvailableSettings.ALLOW_REFRESH_DETACHED_ENTITY;
import static org.hibernate.cfg.AvailableSettings.GENERATE_STATISTICS;
import static org.hibernate.cfg.AvailableSettings.USE_SECOND_LEVEL_CACHE;
import static org.hibernate.testing.cache.CachingRegionFactory.DEFAULT_ACCESSTYPE;
@ -19,8 +18,7 @@ import static org.hibernate.testing.cache.CachingRegionFactory.DEFAULT_ACCESSTYP
settings = {
@Setting( name = USE_SECOND_LEVEL_CACHE, value = "true" ),
@Setting( name = DEFAULT_ACCESSTYPE, value = "nonstrict-read-write" ),
@Setting( name = GENERATE_STATISTICS, value = "true" ),
@Setting( name = ALLOW_REFRESH_DETACHED_ENTITY, value = "true" )
@Setting( name = GENERATE_STATISTICS, value = "true" )
}
)
@DomainModel( annotatedClasses = {A.class, Another.class, AllCached.class, B.class, SubClass.class} )

View File

@ -16,7 +16,6 @@ import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.Test;
import static org.hibernate.cfg.AvailableSettings.ALLOW_REFRESH_DETACHED_ENTITY;
import static org.hibernate.cfg.AvailableSettings.GENERATE_STATISTICS;
import static org.hibernate.cfg.AvailableSettings.USE_SECOND_LEVEL_CACHE;
import static org.hibernate.testing.cache.CachingRegionFactory.DEFAULT_ACCESSTYPE;
@ -28,8 +27,7 @@ import static org.junit.Assert.assertNull;
settings = {
@Setting( name = USE_SECOND_LEVEL_CACHE, value = "true" ),
@Setting( name = DEFAULT_ACCESSTYPE, value = "nonstrict-read-write" ),
@Setting( name = GENERATE_STATISTICS, value = "true" ),
@Setting( name = ALLOW_REFRESH_DETACHED_ENTITY, value = "true" )
@Setting( name = GENERATE_STATISTICS, value = "true" )
}
)
@DomainModel( annotatedClasses = {A.class, Another.class, AllCached.class, B.class, SubClass.class} )

View File

@ -4,7 +4,6 @@
*/
package org.hibernate.orm.test.mapping.naturalid.mutable.cached;
import org.hibernate.LockOptions;
import org.hibernate.cache.spi.CacheImplementor;
import org.hibernate.internal.SessionFactoryImpl;
import org.hibernate.stat.spi.StatisticsImplementor;
@ -274,38 +273,4 @@ public abstract class CachedMutableNaturalIdTest {
});
}
@Test
public void testReattachUnmodifiedInstance(SessionFactoryScope scope) {
final B created = scope.fromTransaction(
(session) -> {
A a = new A();
B b = new B();
b.naturalid = 100;
session.persist( a );
session.persist( b );
b.assA = a;
a.assB.add( b );
return b;
}
);
scope.inTransaction(
(session) -> {
// HHH-7513 failure during reattachment
session.lock( created, LockOptions.NONE );
session.remove( created.assA );
session.remove( created );
}
);
scope.inTransaction(
(session) -> {
// true if the re-attachment worked
assertEquals( session.createQuery( "FROM A" ).list().size(), 0 );
assertEquals( session.createQuery( "FROM B" ).list().size(), 0 );
}
);
}
}

View File

@ -6,19 +6,17 @@ package org.hibernate.orm.test.orphan;
import org.hibernate.Hibernate;
import org.hibernate.LockMode;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.testing.orm.junit.JiraKey;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
@ -28,7 +26,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
xmlMappings = "org/hibernate/orm/test/orphan/Product.hbm.xml"
)
@SessionFactory
@ServiceRegistry(settings = @Setting(name = AvailableSettings.ALLOW_REFRESH_DETACHED_ENTITY, value = "true"))
public class OrphanTest {
@AfterEach
@ -138,7 +135,7 @@ public class OrphanTest {
@Test
@SuppressWarnings("unchecked")
public void testOrphanDeleteAfterLock(SessionFactoryScope scope) {
public void testCannotLockDetachedEntity(SessionFactoryScope scope) {
Product prod = new Product();
Part part = new Part();
scope.inTransaction(
@ -158,14 +155,16 @@ public class OrphanTest {
scope.inTransaction(
session -> {
session.lock( prod, LockMode.READ );
prod.getParts().remove( part );
assertThrows(IllegalArgumentException.class,
() -> session.lock( prod, LockMode.READ ),
"Given entity is not associated with the persistence context"
);
}
);
scope.inTransaction(
session -> {
assertNull( session.get( Part.class, "Widge" ) );
assertNotNull( session.get( Part.class, "Widge" ) );
assertNotNull( session.get( Part.class, "Get" ) );
session.remove( session.get( Product.class, "Widget" ) );
}

View File

@ -10,17 +10,13 @@ import org.hibernate.CacheMode;
import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.UnresolvableObjectException;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.LazyInitializer;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.FailureExpected;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ -42,7 +38,6 @@ import static org.junit.jupiter.api.Assertions.fail;
"org/hibernate/orm/test/readonly/TextHolder.hbm.xml"
}
)
@ServiceRegistry(settings = @Setting(name = AvailableSettings.ALLOW_REFRESH_DETACHED_ENTITY, value = "true"))
public class ReadOnlyProxyTest extends AbstractReadOnlyTest {
@Test
@ -1156,9 +1151,9 @@ public class ReadOnlyProxyTest extends AbstractReadOnlyTest {
s.flush();
try {
s.refresh( dp );
fail( "should have thrown UnresolvableObjectException" );
fail( "should have thrown IllegalArgumentException" );
}
catch (UnresolvableObjectException ex) {
catch (IllegalArgumentException ex) {
// expected
}
finally {
@ -1182,9 +1177,9 @@ public class ReadOnlyProxyTest extends AbstractReadOnlyTest {
assertTrue( Hibernate.isInitialized( dpProxyInit ) );
try {
s.refresh( dpProxyInit );
fail( "should have thrown UnresolvableObjectException" );
fail( "should have thrown IllegalArgumentException" );
}
catch (UnresolvableObjectException ex) {
catch (IllegalArgumentException ex) {
// expected
}
finally {
@ -1199,9 +1194,9 @@ public class ReadOnlyProxyTest extends AbstractReadOnlyTest {
s.refresh( dpProxy );
assertFalse( Hibernate.isInitialized( dpProxy ) );
Hibernate.initialize( dpProxy );
fail( "should have thrown UnresolvableObjectException" );
fail( "should have thrown IllegalArgumentException" );
}
catch (UnresolvableObjectException ex) {
catch (IllegalArgumentException ex) {
// expected
}
finally {
@ -1210,50 +1205,6 @@ public class ReadOnlyProxyTest extends AbstractReadOnlyTest {
}
}
@Test
public void testReadOnlyRefreshDetached(SessionFactoryScope scope) {
Session s = openSession( scope );
s.setCacheMode( CacheMode.IGNORE );
Transaction t = s.beginTransaction();
DataPoint dp = new DataPoint();
dp.setDescription( "original" );
dp.setX( new BigDecimal( 0.1d ).setScale( 19, BigDecimal.ROUND_DOWN ) );
dp.setY( new BigDecimal( Math.cos( dp.getX().doubleValue() ) ).setScale( 19, BigDecimal.ROUND_DOWN ) );
s.persist( dp );
t.commit();
s.close();
s = openSession( scope );
s.setCacheMode( CacheMode.IGNORE );
t = s.beginTransaction();
dp = s.getReference( DataPoint.class, dp.getId() );
assertFalse( Hibernate.isInitialized( dp ) );
assertFalse( s.isReadOnly( dp ) );
s.setReadOnly( dp, true );
assertTrue( s.isReadOnly( dp ) );
s.evict( dp );
s.refresh( dp );
assertTrue( Hibernate.isInitialized( dp ) );
assertFalse( s.isReadOnly( dp ) );
dp.setDescription( "changed" );
assertEquals( "changed", dp.getDescription() );
assertTrue( Hibernate.isInitialized( dp ) );
s.setReadOnly( dp, true );
s.evict( dp );
s.refresh( dp );
assertEquals( "original", dp.getDescription() );
assertFalse( s.isReadOnly( dp ) );
t.commit();
s.clear();
t = s.beginTransaction();
dp = s.get( DataPoint.class, dp.getId() );
assertEquals( "original", dp.getDescription() );
s.remove( dp );
t.commit();
s.close();
}
@Test
public void testReadOnlyProxyMergeDetachedProxyWithChange(SessionFactoryScope scope) {
DataPoint dpOrig = createDataPoint( CacheMode.IGNORE, scope );

View File

@ -13,14 +13,11 @@ import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.query.Query;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
@ -39,7 +36,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
"org/hibernate/orm/test/readonly/TextHolder.hbm.xml"
}
)
@ServiceRegistry(settings = @Setting(name = AvailableSettings.ALLOW_REFRESH_DETACHED_ENTITY, value = "true"))
public class ReadOnlySessionTest extends AbstractReadOnlyTest {
@AfterEach
@ -450,53 +446,6 @@ public class ReadOnlySessionTest extends AbstractReadOnlyTest {
s.close();
}
@Test
public void testReadOnlyRefreshDetached(SessionFactoryScope scope) {
Session s = openSession( scope );
s.setCacheMode( CacheMode.IGNORE );
Transaction t = s.beginTransaction();
DataPoint dp = new DataPoint();
dp.setDescription( "original" );
dp.setX( new BigDecimal( 0.1d ).setScale( 19, BigDecimal.ROUND_DOWN ) );
dp.setY( new BigDecimal( Math.cos( dp.getX().doubleValue() ) ).setScale( 19, BigDecimal.ROUND_DOWN ) );
s.persist( dp );
t.commit();
s.close();
s = openSession( scope );
s.setCacheMode( CacheMode.IGNORE );
t = s.beginTransaction();
s.setDefaultReadOnly( false );
dp.setDescription( "changed" );
assertEquals( "changed", dp.getDescription() );
s.refresh( dp );
assertEquals( "original", dp.getDescription() );
assertFalse( s.isReadOnly( dp ) );
dp.setDescription( "changed" );
assertEquals( "changed", dp.getDescription() );
s.evict( dp );
s.refresh( dp );
assertEquals( "original", dp.getDescription() );
assertFalse( s.isReadOnly( dp ) );
dp.setDescription( "changed" );
assertEquals( "changed", dp.getDescription() );
s.setDefaultReadOnly( true );
s.evict( dp );
s.refresh( dp );
assertEquals( "original", dp.getDescription() );
assertTrue( s.isReadOnly( dp ) );
dp.setDescription( "changed" );
t.commit();
s.clear();
t = s.beginTransaction();
dp = s.get( DataPoint.class, dp.getId() );
assertEquals( "original", dp.getDescription() );
s.remove( dp );
t.commit();
s.close();
}
@Test
public void testReadOnlyProxyRefresh(SessionFactoryScope scope) {
Session s = openSession( scope );
@ -554,65 +503,6 @@ public class ReadOnlySessionTest extends AbstractReadOnlyTest {
}
@Test
public void testReadOnlyProxyRefreshDetached(SessionFactoryScope scope) {
Session s = openSession( scope );
s.setCacheMode( CacheMode.IGNORE );
Transaction t = s.beginTransaction();
DataPoint dp = new DataPoint();
dp.setDescription( "original" );
dp.setX( new BigDecimal( 0.1d ).setScale( 19, BigDecimal.ROUND_DOWN ) );
dp.setY( new BigDecimal( Math.cos( dp.getX().doubleValue() ) ).setScale( 19, BigDecimal.ROUND_DOWN ) );
s.persist( dp );
t.commit();
s.close();
s = openSession( scope );
s.setCacheMode( CacheMode.IGNORE );
t = s.beginTransaction();
s.setDefaultReadOnly( true );
dp = (DataPoint) s.getReference( DataPoint.class, dp.getId() );
assertFalse( Hibernate.isInitialized( dp ) );
assertTrue( s.isReadOnly( dp ) );
s.evict( dp );
s.refresh( dp );
assertTrue( Hibernate.isInitialized( dp ) );
s.setDefaultReadOnly( false );
assertTrue( s.isReadOnly( dp ) );
s.evict( dp );
s.refresh( dp );
assertTrue( Hibernate.isInitialized( dp ) );
assertFalse( s.isReadOnly( dp ) );
assertFalse( s.isReadOnly( ( (HibernateProxy) dp ).getHibernateLazyInitializer().getImplementation() ) );
dp.setDescription( "changed" );
assertEquals( "changed", dp.getDescription() );
assertTrue( Hibernate.isInitialized( dp ) );
s.evict( dp );
s.refresh( dp );
assertEquals( "original", dp.getDescription() );
assertFalse( s.isReadOnly( dp ) );
assertFalse( s.isReadOnly( ( (HibernateProxy) dp ).getHibernateLazyInitializer().getImplementation() ) );
dp.setDescription( "changed" );
assertEquals( "changed", dp.getDescription() );
s.setDefaultReadOnly( true );
s.evict( dp );
s.refresh( dp );
assertEquals( "original", dp.getDescription() );
assertTrue( s.isReadOnly( dp ) );
assertTrue( s.isReadOnly( ( (HibernateProxy) dp ).getHibernateLazyInitializer().getImplementation() ) );
dp.setDescription( "changed" );
assertEquals( "changed", dp.getDescription() );
t.commit();
s.clear();
t = s.beginTransaction();
dp = s.get( DataPoint.class, dp.getId() );
assertEquals( "original", dp.getDescription() );
s.remove( dp );
t.commit();
s.close();
}
@Test
public void testReadOnlyDelete(SessionFactoryScope scope) {
Session s = openSession( scope );

View File

@ -13,11 +13,8 @@ import org.hibernate.ScrollableResults;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ -36,7 +33,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
"org/hibernate/orm/test/readonly/TextHolder.hbm.xml"
}
)
@ServiceRegistry(settings = @Setting(name = AvailableSettings.ALLOW_REFRESH_DETACHED_ENTITY, value = "true"))
public class ReadOnlyTest extends AbstractReadOnlyTest {
@Test
@ -317,55 +313,6 @@ public class ReadOnlyTest extends AbstractReadOnlyTest {
clearCounts( scope );
}
@Test
public void testReadOnlyRefreshDetached(SessionFactoryScope scope) {
clearCounts( scope );
Session s = openSession( scope );
Transaction t = s.beginTransaction();
DataPoint dp = new DataPoint();
dp.setDescription( "original" );
dp.setX( new BigDecimal( 0.1d ).setScale( 19, BigDecimal.ROUND_DOWN ) );
dp.setY( new BigDecimal( Math.cos( dp.getX().doubleValue() ) ).setScale( 19, BigDecimal.ROUND_DOWN ) );
s.persist( dp );
t.commit();
s.close();
assertInsertCount( 1, scope );
assertUpdateCount( 0, scope );
clearCounts( scope );
s = openSession( scope );
t = s.beginTransaction();
dp.setDescription( "changed" );
assertEquals( "changed", dp.getDescription() );
s.refresh( dp );
assertEquals( "original", dp.getDescription() );
assertFalse( s.isReadOnly( dp ) );
s.setReadOnly( dp, true );
dp.setDescription( "changed" );
assertEquals( "changed", dp.getDescription() );
s.evict( dp );
s.refresh( dp );
assertEquals( "original", dp.getDescription() );
assertFalse( s.isReadOnly( dp ) );
t.commit();
assertInsertCount( 0, scope );
assertUpdateCount( 0, scope );
s.clear();
t = s.beginTransaction();
dp = (DataPoint) s.get( DataPoint.class, dp.getId() );
assertEquals( "original", dp.getDescription() );
s.remove( dp );
t.commit();
s.close();
assertUpdateCount( 0, scope );
assertDeleteCount( 1, scope );
}
@Test
public void testReadOnlyDelete(SessionFactoryScope scope) {
clearCounts( scope );

View File

@ -1,56 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.orm.test.refresh;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.orm.test.jpa.refresh.TestEntity;
import org.hibernate.testing.orm.junit.JiraKey;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/**
* @author Andrea Boriero
*/
@JiraKey(value = "HHH-11188")
@DomainModel(
annotatedClasses = TestEntity.class
)
@SessionFactory
@ServiceRegistry(settings = @Setting(name = AvailableSettings.ALLOW_REFRESH_DETACHED_ENTITY, value = "true"))
public class RefreshDetachedInstanceWhenIsAllowedTest {
private TestEntity testEntity;
@BeforeEach
public void setUp(SessionFactoryScope scope) {
testEntity = new TestEntity();
scope.inTransaction(
session ->
session.persist( testEntity )
);
}
@AfterEach
public void tearDown(SessionFactoryScope scope) {
scope.inTransaction(
session ->
session.createQuery( "delete from TestEntity" ).executeUpdate()
);
}
@Test
public void testRefreshDetachedInstance(SessionFactoryScope scope) {
scope.inSession(
session ->
session.refresh( testEntity )
);
}
}

View File

@ -1,60 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.orm.test.refresh;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.orm.test.jpa.refresh.TestEntity;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/**
* @author Andrea Boriero
*/
@DomainModel(
annotatedClasses = TestEntity.class
)
@SessionFactory
@ServiceRegistry(
settings = @Setting(name = AvailableSettings.ALLOW_REFRESH_DETACHED_ENTITY, value = "false")
)
public class RefreshDetachedInstanceWhenIsNotAllowedTest {
private TestEntity testEntity;
@BeforeEach
public void setUp(SessionFactoryScope scope) {
testEntity = new TestEntity();
scope.inTransaction(
session ->
session.persist( testEntity )
);
}
@AfterEach
public void tearDown(SessionFactoryScope scope) {
scope.inTransaction(
session ->
session.createQuery( "delete from TestEntity" ).executeUpdate()
);
}
@Test
public void testRefreshDetachedInstance(SessionFactoryScope scope) {
scope.inSession(
session ->
Assertions.assertThrows(
IllegalArgumentException.class, () ->
session.refresh( testEntity )
)
);
}
}

View File

@ -1,140 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.orm.test.session;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import org.hibernate.LockMode;
import org.hibernate.Session;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.proxy.AbstractLazyInitializer;
import org.hibernate.testing.orm.junit.JiraKey;
import org.hibernate.testing.logger.LoggerInspectionRule;
import org.hibernate.testing.logger.Triggerable;
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.Jpa;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.Rule;
import org.junit.jupiter.api.Test;
import org.jboss.logging.Logger;
import java.lang.invoke.MethodHandles;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
/**
* @author Vlad Mihalcea
*/
@Jpa(annotatedClasses = {AssociateEntityWithTwoSessionsTest.Location.class,
AssociateEntityWithTwoSessionsTest.Event.class},
properties = @Setting(name = AvailableSettings.ALLOW_REFRESH_DETACHED_ENTITY, value = "true"))
public class AssociateEntityWithTwoSessionsTest {
@Rule
public LoggerInspectionRule logInspection = new LoggerInspectionRule(
Logger.getMessageLogger( MethodHandles.lookup(), CoreMessageLogger.class, AbstractLazyInitializer.class.getName() ) );
@Test
@JiraKey( value = "HHH-12216" )
public void test(EntityManagerFactoryScope scope) {
final Location location = new Location();
location.setCity( "Cluj" );
final Event event = new Event();
event.setLocation( location );
scope.inTransaction( entityManager -> {
entityManager.persist( location );
entityManager.persist( event );
} );
final Triggerable triggerable = logInspection.watchForLogMessages( "HHH000485" );
triggerable.reset();
scope.inTransaction( entityManager -> {
Event e = entityManager.find( Event.class, event.id );
Location location1 = e.getLocation();
try {
scope.inTransaction( _entityManager -> {
_entityManager.unwrap( Session.class ).lock( location1, LockMode.NONE );
} );
fail("Should have thrown a HibernateException");
}
catch (Exception expected) {
}
} );
assertEquals(
"HHH000485: Illegally attempted to associate a proxy for entity [org.hibernate.orm.test.session.AssociateEntityWithTwoSessionsTest$Location] with id [1] with two open sessions.",
triggerable.triggerMessage()
);
}
@Entity(name = "Location")
public static class Location {
@Id
@GeneratedValue
public Long id;
public String city;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
}
@Entity(name = "Event")
public static class Event {
@Id
@GeneratedValue
public Long id;
@ManyToOne(fetch = FetchType.LAZY)
private Location location;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Location getLocation() {
return location;
}
public void setLocation(Location location) {
this.location = location;
}
}
}

View File

@ -6,14 +6,11 @@ package org.hibernate.orm.test.sql.ast;
import java.util.List;
import org.hibernate.LockMode;
import org.hibernate.annotations.Filter;
import org.hibernate.annotations.FilterDef;
import org.hibernate.annotations.ParamDef;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.sql.ast.spi.ParameterMarkerStrategy;
import org.hibernate.testing.orm.junit.Setting;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.testing.jdbc.SQLStatementInspector;
@ -45,8 +42,8 @@ import static org.hibernate.internal.util.StringHelper.*;
*/
@ServiceRegistry( services = @ServiceRegistry.Service(
role = ParameterMarkerStrategy.class,
impl = ParameterMarkerStrategyTests.ParameterMarkerStrategyImpl.class
), settings = @Setting(name = AvailableSettings.ALLOW_REFRESH_DETACHED_ENTITY, value = "true") )
impl = ParameterMarkerStrategyTests.ParameterMarkerStrategyImpl.class )
)
@DomainModel( annotatedClasses = {
EntityOfBasics.class,
ParameterMarkerStrategyTests.EntityWithFilters.class,
@ -127,28 +124,6 @@ public class ParameterMarkerStrategyTests {
} );
}
@Test
@Jira( "https://hibernate.atlassian.net/browse/HHH-16229" )
public void testLocking(SessionFactoryScope scope) {
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
final EntityWithVersion created = scope.fromTransaction( (session) -> {
final EntityWithVersion entity = new EntityWithVersion( 1, "Entity Prime" );
session.persist( entity );
return entity;
} );
statementInspector.clear();
scope.inTransaction( (session) -> {
session.lock( created, LockMode.PESSIMISTIC_FORCE_INCREMENT );
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( "?1" );
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( "?2" );
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( "?3" );
assertThat( statementInspector.getSqlQueries().get( 0 ) ).matches( (sql) -> count( sql, "?" ) == 3 );
} );
}
@Test
@FailureExpected
@Jira( "https://hibernate.atlassian.net/browse/HHH-16283" )

View File

@ -245,6 +245,16 @@ parent.addChild( merged );
----
[[refresh-lock-deteached]]
== Refreshing/locking detached entities
Traditionally, Hibernate allowed detached entities to be refreshed. However, Jakarta Persistence prohibits this practice and specifies that an `IllegalArgumentException` should be thrown instead. Hibernate now fully aligns with the JPA specification in this regard.
Along the same line of thought, also acquiring a lock on a detached entity is no longer allowed.
To this effect the `hibernate.allow_refresh_detached_entity`, which allowed Hibernate's legacy refresh behaviour to be invoked, has been removed.
[[auto-cascade-persist]]
== Cascading persistence for `@Id` and `@MapsId` fields
@ -377,6 +387,7 @@ While most users will never see this change, it might impact integrations which
* Settings
** Removed `hibernate.mapping.precedence` and friends
** Removed `hibernate.allow_refresh_detached_entity`