HHH-18169 disallow refresh/lock for detached instance

Signed-off-by: Gavin King <gavin@hibernate.org>
This commit is contained in:
Gavin King 2024-08-31 12:24:35 +02:00
parent 1d12dc0499
commit 7422c18a94
29 changed files with 149 additions and 309 deletions

View File

@ -933,44 +933,6 @@ And finally, serialization will make the deserialized form be detached (the orig
Detached data can still be manipulated, however, the persistence context will no longer automatically know about these modifications, and the application will need to intervene to make the changes persistent again.
[[pc-detach-reattach]]
==== Reattaching detached data
Reattachment is the process of taking an incoming entity instance that is in the detached state and re-associating it with the current persistence context.
[IMPORTANT]
====
Jakarta Persistence does not support reattaching detached data. This is only available through Hibernate `org.hibernate.Session`.
====
[[pc-detach-reattach-lock-example]]
.Reattaching a detached entity using `lock`
====
[source, java, indent=0]
----
include::{example-dir-pc}/PersistenceContextTest.java[tags=pc-detach-reattach-lock-example]
----
====
[[pc-detach-reattach-saveOrUpdate-example]]
.Reattaching a detached entity using `saveOrUpdate`
====
[source, java, indent=0]
----
include::{example-dir-pc}/PersistenceContextTest.java[tags=pc-detach-reattach-saveOrUpdate-example]
----
====
[NOTE]
====
The method name `update` is a bit misleading here.
It does not mean that an `SQL` `UPDATE` is immediately performed.
It does, however, mean that an `SQL` `UPDATE` will be performed when the persistence context is flushed since Hibernate does not know its previous state against which to compare for changes.
If the entity is mapped with `select-before-update`, Hibernate will pull the current state from the database and see if an update is needed.
====
Provided the entity is detached, `update` and `saveOrUpdate` operate exactly the same.
[[pc-merge]]
==== Merging detached data
@ -1274,17 +1236,6 @@ include::{example-dir-pc}/CascadeDetachTest.java[tags=pc-cascade-detach-example]
Although unintuitively, `CascadeType.LOCK` does not propagate a lock request from a parent entity to its children.
Such a use case requires the use of the `PessimisticLockScope.EXTENDED` value of the `jakarta.persistence.lock.scope` property.
However, `CascadeType.LOCK` allows us to reattach a parent entity along with its children to the currently running Persistence Context.
[[pc-cascade-lock-example]]
.`CascadeType.LOCK` example
====
[source, java, indent=0]
----
include::{example-dir-pc}/CascadeLockTest.java[tags=pc-cascade-lock-example]
----
====
[[pc-cascade-refresh]]
==== `CascadeType.REFRESH`

View File

@ -337,7 +337,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
this.allowRefreshDetachedEntity = configurationService.getSetting(
ALLOW_REFRESH_DETACHED_ENTITY,
BOOLEAN,
true
false
);
this.flushBeforeCompletionEnabled = configurationService.getSetting( FLUSH_BEFORE_COMPLETION, BOOLEAN, true );

View File

@ -659,6 +659,7 @@ public class SessionImpl
private void fireLock(LockEvent event) {
checkOpen();
checkEntityManaged( event.getEntityName(), event.getObject() );
pulseTransactionCoordinator();
fastSessionServices.eventListenerGroup_LOCK
.fireEventOnEachListener( event, LockEventListener::onLock );
@ -1163,18 +1164,7 @@ public class SessionImpl
private void fireRefresh(final RefreshEvent event) {
try {
if ( !getSessionFactory().getSessionFactoryOptions().isAllowRefreshDetachedEntity() ) {
if ( event.getEntityName() != null ) {
if ( !contains( event.getEntityName(), event.getObject() ) ) {
throw new IllegalArgumentException( "Entity not managed" );
}
}
else {
if ( !contains( event.getObject() ) ) {
throw new IllegalArgumentException( "Entity not managed" );
}
}
}
checkEntityManaged( event.getEntityName(), event.getObject() );
pulseTransactionCoordinator();
fastSessionServices.eventListenerGroup_REFRESH
.fireEventOnEachListener( event, RefreshEventListener::onRefresh );
@ -1195,6 +1185,7 @@ public class SessionImpl
private void fireRefresh(final RefreshContext refreshedAlready, final RefreshEvent event) {
try {
checkEntityManaged( event.getEntityName(), event.getObject() );
pulseTransactionCoordinator();
fastSessionServices.eventListenerGroup_REFRESH
.fireEventOnEachListener( event, refreshedAlready, RefreshEventListener::onRefresh );
@ -1207,6 +1198,18 @@ 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 boolean managed(String entityName, Object entity) {
return entityName == null ? contains( entity ) : contains( entityName, entity );
}
// replicate() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -86,7 +86,6 @@ import org.checkerframework.checker.nullness.qual.Nullable;
import static org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer.UNFETCHED_PROPERTY;
import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable;
import static org.hibernate.engine.internal.ManagedTypeHelper.isPersistentAttributeInterceptable;
import static org.hibernate.internal.log.LoggingHelper.toLoggableString;
import static org.hibernate.metamodel.mapping.ForeignKeyDescriptor.Nature.TARGET;
import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer;
@ -163,12 +162,9 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
lockMode = rowProcessingState.determineEffectiveLockMode( initializer.sourceAlias );
if ( initializer.isResultInitializer() ) {
uniqueKeyAttributePath = rowProcessingState.getEntityUniqueKeyAttributePath();
if ( uniqueKeyAttributePath != null ) {
uniqueKeyPropertyTypes = initializer.getParentEntityAttributeTypes( uniqueKeyAttributePath );
}
else {
uniqueKeyPropertyTypes = null;
}
uniqueKeyPropertyTypes = uniqueKeyAttributePath != null
? initializer.getParentEntityAttributeTypes( uniqueKeyAttributePath )
: null;
canUseEmbeddedIdentifierInstanceAsEntity = rowProcessingState.getEntityId() != null
&& initializer.couldUseEmbeddedIdentifierInstanceAsEntity;
}
@ -178,13 +174,10 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
canUseEmbeddedIdentifierInstanceAsEntity = false;
}
hasCallbackActions = rowProcessingState.hasCallbackActions();
if ( initializer.discriminatorAssembler == null
|| rowProcessingState.isQueryCacheHit() && entityDescriptor.useShallowQueryCacheLayout() && !entityDescriptor.storeDiscriminatorInShallowQueryCacheLayout() ) {
defaultConcreteDescriptor = entityDescriptor;
}
else {
defaultConcreteDescriptor = null;
}
defaultConcreteDescriptor =
hasConcreteDescriptor( rowProcessingState, initializer.discriminatorAssembler, entityDescriptor )
? entityDescriptor
: null;
}
/*
@ -206,6 +199,16 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
}
}
private static boolean hasConcreteDescriptor(
RowProcessingState rowProcessingState,
BasicResultAssembler<?> discriminatorAssembler,
EntityPersister entityDescriptor) {
return discriminatorAssembler == null
|| rowProcessingState.isQueryCacheHit()
&& entityDescriptor.useShallowQueryCacheLayout()
&& !entityDescriptor.storeDiscriminatorInShallowQueryCacheLayout();
}
public EntityInitializerImpl(
EntityResultGraphNode resultDescriptor,
String sourceAlias,
@ -229,7 +232,8 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
: entityDescriptor.getRootEntityDescriptor().getEntityPersister();
keyTypeForEqualsHashCode = entityDescriptor.getIdentifierType().getTypeForEqualsHashCode();
// The id can only be the entity instance if this is a non-aggregated id that has no containing class
couldUseEmbeddedIdentifierInstanceAsEntity = entityDescriptor.getIdentifierMapping() instanceof CompositeIdentifierMapping
couldUseEmbeddedIdentifierInstanceAsEntity =
entityDescriptor.getIdentifierMapping() instanceof CompositeIdentifierMapping
&& !( (CompositeIdentifierMapping) entityDescriptor.getIdentifierMapping() ).hasContainingClass();
this.navigablePath = resultDescriptor.getNavigablePath();
@ -258,7 +262,8 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
hasKeyManyToOne = initializer != null && initializer.isLazyCapable();
}
assert entityDescriptor.hasSubclasses() == (discriminatorFetch != null) : "Discriminator should only be fetched if the entity has subclasses";
assert entityDescriptor.hasSubclasses() == (discriminatorFetch != null)
: "Discriminator should only be fetched if the entity has subclasses";
discriminatorAssembler = discriminatorFetch != null
? (BasicResultAssembler<?>) discriminatorFetch.createAssembler( this, creationState )
: null;
@ -286,12 +291,16 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
final BitSet[] lazySets = new BitSet[subMappingTypes.size() + 1];
final BitSet[] maybeLazySets = new BitSet[subMappingTypes.size() + 1];
final MutabilityPlan[][] updatableAttributeMutabilityPlans = new MutabilityPlan[subMappingTypes.size() + 1][];
assemblers[rootEntityDescriptor.getSubclassId()] = new DomainResultAssembler[rootEntityDescriptor.getNumberOfFetchables()];
updatableAttributeMutabilityPlans[rootEntityDescriptor.getSubclassId()] = new MutabilityPlan[rootEntityDescriptor.getNumberOfAttributeMappings()];
assemblers[rootEntityDescriptor.getSubclassId()] =
new DomainResultAssembler[rootEntityDescriptor.getNumberOfFetchables()];
updatableAttributeMutabilityPlans[rootEntityDescriptor.getSubclassId()] =
new MutabilityPlan[rootEntityDescriptor.getNumberOfAttributeMappings()];
for ( EntityMappingType subMappingType : subMappingTypes ) {
assemblers[subMappingType.getSubclassId()] = new DomainResultAssembler[subMappingType.getNumberOfFetchables()];
updatableAttributeMutabilityPlans[subMappingType.getSubclassId()] = new MutabilityPlan[subMappingType.getNumberOfAttributeMappings()];
assemblers[subMappingType.getSubclassId()] =
new DomainResultAssembler[subMappingType.getNumberOfFetchables()];
updatableAttributeMutabilityPlans[subMappingType.getSubclassId()] =
new MutabilityPlan[subMappingType.getNumberOfAttributeMappings()];
}
boolean hasLazySubInitializers = false;
@ -345,7 +354,8 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
}
for ( EntityMappingType subMappingType : declaringType.getSubMappingTypes() ) {
assemblers[subMappingType.getSubclassId()][stateArrayPosition] = stateAssembler;
updatableAttributeMutabilityPlans[subMappingType.getSubclassId()][stateArrayPosition] = updatableAttributeMutabilityPlans[subclassId][stateArrayPosition];
updatableAttributeMutabilityPlans[subMappingType.getSubclassId()][stateArrayPosition] =
updatableAttributeMutabilityPlans[subclassId][stateArrayPosition];
if ( subInitializer != null ) {
if ( subInitializers[subMappingType.getSubclassId()] == null ) {
subInitializers[subMappingType.getSubclassId()] = new Initializer<?>[size];
@ -355,8 +365,10 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
maybeLazySets[subMappingType.getSubclassId()] = new BitSet(size);
}
subInitializers[subMappingType.getSubclassId()][stateArrayPosition] = subInitializer;
eagerSubInitializers[subMappingType.getSubclassId()][stateArrayPosition] = eagerSubInitializers[subclassId][stateArrayPosition];
collectionContainingSubInitializers[subMappingType.getSubclassId()][stateArrayPosition] = collectionContainingSubInitializers[subclassId][stateArrayPosition];
eagerSubInitializers[subMappingType.getSubclassId()][stateArrayPosition] =
eagerSubInitializers[subclassId][stateArrayPosition];
collectionContainingSubInitializers[subMappingType.getSubclassId()][stateArrayPosition] =
collectionContainingSubInitializers[subclassId][stateArrayPosition];
if ( lazySets[subclassId].get( stateArrayPosition ) ) {
lazySets[subMappingType.getSubclassId()].set( stateArrayPosition );
}
@ -411,7 +423,8 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
this.assemblers = assemblers;
this.subInitializers = subInitializers;
this.subInitializersForResolveFromInitialized = rootEntityDescriptor.getBytecodeEnhancementMetadata().isEnhancedForLazyLoading()
this.subInitializersForResolveFromInitialized =
rootEntityDescriptor.getBytecodeEnhancementMetadata().isEnhancedForLazyLoading()
? subInitializers
: eagerSubInitializers;
this.collectionContainingSubInitializers = collectionContainingSubInitializers;
@ -531,7 +544,8 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
}
else {
//noinspection unchecked
final Initializer<InitializerData> initializer = (Initializer<InitializerData>) identifierAssembler.getInitializer();
final Initializer<InitializerData> initializer =
(Initializer<InitializerData>) identifierAssembler.getInitializer();
if ( initializer != null ) {
final InitializerData subData = initializer.getData( rowProcessingState );
initializer.resolveKey( subData );
@ -785,7 +799,8 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
}
private void deepCopy(EntityPersister containerDescriptor, Object[] source, Object[] target) {
final MutabilityPlan<Object>[] updatableAttributeMutabilityPlan = updatableAttributeMutabilityPlans[containerDescriptor.getSubclassId()];
final MutabilityPlan<Object>[] updatableAttributeMutabilityPlan =
updatableAttributeMutabilityPlans[containerDescriptor.getSubclassId()];
for ( int i = 0; i < updatableAttributeMutabilityPlan.length; i++ ) {
final Object sourceValue = source[i];
if ( updatableAttributeMutabilityPlan[i] != null
@ -830,11 +845,7 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
protected Type[] getParentEntityAttributeTypes(String attributeName) {
Type[] types = parentEntityAttributeTypes.get( attributeName );
if ( types == null ) {
types = new Type[
entityDescriptor.getRootEntityDescriptor()
.getSubclassEntityNames()
.size()
];
types = new Type[entityDescriptor.getRootEntityDescriptor().getSubclassEntityNames().size()];
initializeAttributeType( types, entityDescriptor, attributeName );
for ( EntityMappingType subMappingType : entityDescriptor.getSubMappingTypes() ) {
initializeAttributeType( types, subMappingType.getEntityPersister(), attributeName );
@ -855,12 +866,12 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
@Nullable BasicResultAssembler<?> discriminatorAssembler,
EntityPersister entityDescriptor)
throws WrongClassException {
if ( discriminatorAssembler == null
|| rowProcessingState.isQueryCacheHit() && entityDescriptor.useShallowQueryCacheLayout() && !entityDescriptor.storeDiscriminatorInShallowQueryCacheLayout() ) {
if ( hasConcreteDescriptor( rowProcessingState, discriminatorAssembler, entityDescriptor ) ) {
return entityDescriptor;
}
else {
assert entityDescriptor.hasSubclasses() : "Reading a discriminator from a result set should only happen if the entity has subclasses";
assert entityDescriptor.hasSubclasses()
: "Reading a discriminator from a result set should only happen if the entity has subclasses";
final EntityDiscriminatorMapping discriminatorMapping = entityDescriptor.getDiscriminatorMapping();
assert discriminatorMapping != null;
final Object discriminator = discriminatorAssembler.extractRawValue( rowProcessingState );
@ -888,10 +899,17 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
}
protected boolean useEmbeddedIdentifierInstanceAsEntity(EntityInitializerData data) {
return data.canUseEmbeddedIdentifierInstanceAsEntity
&& ( data.concreteDescriptor = determineConcreteEntityDescriptor( data.getRowProcessingState(), discriminatorAssembler, entityDescriptor ) ) != null
if ( data.canUseEmbeddedIdentifierInstanceAsEntity ) {
data.concreteDescriptor =
determineConcreteEntityDescriptor( data.getRowProcessingState(),
discriminatorAssembler, entityDescriptor );
return data.concreteDescriptor != null
&& data.concreteDescriptor.isInstance( data.getRowProcessingState().getEntityId() );
}
else {
return false;
}
}
@Override
public void resolveInstance(Object instance, EntityInitializerData data) {

View File

@ -8,11 +8,14 @@ package org.hibernate.orm.test.bytecode.enhancement.lazy.HHH_10708;
import static org.junit.jupiter.api.Assertions.assertFalse;
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;
@ -33,6 +36,7 @@ import java.util.Set;
)
@SessionFactory
@BytecodeEnhanced
@ServiceRegistry(settings = @Setting(name = AvailableSettings.ALLOW_REFRESH_DETACHED_ENTITY, value = "true"))
public class UnexpectedDeleteTest2 {
private Bar myBar;

View File

@ -8,6 +8,7 @@ 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.internal.util.SerializationHelper;
import org.hibernate.orm.test.bytecode.enhancement.lazy.NoDirtyCheckingContext;
@ -19,8 +20,10 @@ 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;
@ -42,6 +45,7 @@ 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

View File

@ -9,6 +9,7 @@ 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;
@ -18,8 +19,10 @@ 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;
@ -41,6 +44,7 @@ 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

View File

@ -23,10 +23,13 @@ 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;
@ -43,6 +46,7 @@ import static org.junit.Assert.assertEquals;
}
)
@SessionFactory
@ServiceRegistry(settings = @Setting(name = AvailableSettings.ALLOW_REFRESH_DETACHED_ENTITY, value = "true"))
public class RefreshTest {
private JobBatch batch;

View File

@ -8,12 +8,15 @@ 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.internal.util.SerializationHelper;
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;
@ -33,6 +36,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
)
)
@SessionFactory
@ServiceRegistry(settings = @Setting(name = AvailableSettings.ALLOW_REFRESH_DETACHED_ENTITY, value = "true"))
public class BackrefCompositeMapKeyTest {
@Test

View File

@ -301,7 +301,6 @@ public class SQLServerDialectTest extends BaseCoreFunctionalTestCase {
session.persist( new Contact( 1L, "Lukasz", "Antoniak", "owner", folder1 ) );
session.persist( new Contact( 2L, "Kinga", "Mroz", "co-owner", folder2 ) );
session.flush();
session.clear();
session.refresh( folder1 );
session.refresh( folder2 );
session.clear();

View File

@ -65,6 +65,7 @@ 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

View File

@ -11,13 +11,16 @@ 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.FailureExpected;
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;
@ -40,6 +43,7 @@ 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;

View File

@ -14,9 +14,12 @@ 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;
@ -34,6 +37,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
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

View File

@ -15,9 +15,11 @@ 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.Test;
import static org.junit.jupiter.api.Assertions.fail;
@ -25,10 +27,9 @@ import static org.junit.jupiter.api.Assertions.fail;
/**
* @author Steve Ebersole
*/
@Jpa(annotatedClasses = {
MergeWithTransientNonCascadedAssociationTest.Person.class,
MergeWithTransientNonCascadedAssociationTest.Address.class
})
@Jpa(annotatedClasses = {MergeWithTransientNonCascadedAssociationTest.Person.class,
MergeWithTransientNonCascadedAssociationTest.Address.class},
properties = @Setting(name = AvailableSettings.ALLOW_REFRESH_DETACHED_ENTITY, value = "true"))
public class MergeWithTransientNonCascadedAssociationTest {
@Test
public void testMergeWithTransientNonCascadedAssociation(EntityManagerFactoryScope scope) {

View File

@ -6,12 +6,14 @@
*/
package org.hibernate.orm.test.mapping.naturalid.mutable.cached;
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.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;
@ -20,7 +22,8 @@ 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 = GENERATE_STATISTICS, value = "true" ),
@Setting( name = ALLOW_REFRESH_DETACHED_ENTITY, value = "true" )
}
)
@DomainModel( annotatedClasses = {A.class, Another.class, AllCached.class, B.class, SubClass.class} )

View File

@ -18,6 +18,7 @@ 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;
@ -29,7 +30,8 @@ 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 = GENERATE_STATISTICS, value = "true" ),
@Setting( name = ALLOW_REFRESH_DETACHED_ENTITY, value = "true" )
}
)
@DomainModel( annotatedClasses = {A.class, Another.class, AllCached.class, B.class, SubClass.class} )

View File

@ -8,12 +8,15 @@ package org.hibernate.orm.test.orphan;
import org.hibernate.Hibernate;
import org.hibernate.LockMode;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.internal.util.SerializationHelper;
import org.hibernate.testing.TestForIssue;
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;
@ -28,6 +31,7 @@ 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

View File

@ -1,72 +0,0 @@
/*
* 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.pc;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.Session;
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* @author Fábio Takeo Ueno
*/
public class CascadeLockTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Person.class,
Phone.class
};
}
@Test
public void lockTest() {
doInJPA(this::entityManagerFactory, entityManager -> {
Person person = new Person();
person.setId(1L);
person.setName("John Doe");
Phone phone = new Phone();
phone.setId(1L);
phone.setNumber("123-456-7890");
person.addPhone(phone);
entityManager.persist(person);
});
doInJPA(this::entityManagerFactory, entityManager -> {
//tag::pc-cascade-lock-example[]
Person person = entityManager.find(Person.class, 1L);
assertEquals(1, person.getPhones().size());
Phone phone = person.getPhones().get(0);
assertTrue(entityManager.contains(person));
assertTrue(entityManager.contains(phone));
entityManager.detach(person);
assertFalse(entityManager.contains(person));
assertFalse(entityManager.contains(phone));
entityManager.unwrap(Session.class)
.lock(person, new LockOptions(LockMode.NONE));
assertTrue(entityManager.contains(person));
assertTrue(entityManager.contains(phone));
//end::pc-cascade-lock-example[]
});
}
}

View File

@ -252,34 +252,6 @@ public class PersistenceContextTest extends BaseEntityManagerFunctionalTestCase
//end::pc-refresh-native-example[]
});
doInJPA(this::entityManagerFactory, entityManager -> {
Session session = entityManager.unwrap(Session.class);
Long personId = _personId;
//tag::pc-detach-reattach-lock-example[]
Person person = session.byId(Person.class).load(personId);
//Clear the Session so the person entity becomes detached
session.clear();
person.setName("Mr. John Doe");
session.lock(person, LockMode.NONE);
//end::pc-detach-reattach-lock-example[]
});
doInJPA(this::entityManagerFactory, entityManager -> {
Session session = entityManager.unwrap(Session.class);
Long personId = _personId;
//tag::pc-detach-reattach-saveOrUpdate-example[]
Person person = session.byId(Person.class).load(personId);
//Clear the Session so the person entity becomes detached
session.clear();
person.setName("Mr. John Doe");
session.merge(person);
//end::pc-detach-reattach-saveOrUpdate-example[]
});
doInJPA(this::entityManagerFactory, entityManager -> {
Session session = entityManager.unwrap(Session.class);
Long personId = _personId;

View File

@ -14,13 +14,16 @@ import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.TransientObjectException;
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,6 +45,7 @@ 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

View File

@ -15,11 +15,14 @@ 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;
@ -38,6 +41,7 @@ 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

View File

@ -15,8 +15,11 @@ 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;
@ -35,6 +38,7 @@ 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

View File

@ -1,22 +0,0 @@
/*
* 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.refresh;
/**
* @author Andrea Boriero
*/
public class Customer {
private long id;
public long getId() {
return this.id;
}
public void setId(long id) {
this.id = id;
}
}

View File

@ -6,12 +6,15 @@
*/
package org.hibernate.orm.test.refresh;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.orm.test.jpa.refresh.TestEntity;
import org.hibernate.testing.TestForIssue;
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;
@ -24,6 +27,7 @@ import org.junit.jupiter.api.Test;
annotatedClasses = TestEntity.class
)
@SessionFactory
@ServiceRegistry(settings = @Setting(name = AvailableSettings.ALLOW_REFRESH_DETACHED_ENTITY, value = "true"))
public class RefreshDetachedInstanceWhenIsAllowedTest {
private TestEntity testEntity;

View File

@ -80,7 +80,7 @@ public class RefreshTest {
@Test
public void testRefreshWithNullId(SessionFactoryScope scope) {
Assertions.assertThrows(
TransientObjectException.class,
IllegalArgumentException.class,
() -> {
scope.inTransaction(
session -> {

View File

@ -1,52 +0,0 @@
/*
* 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.refresh;
import org.hibernate.testing.TestForIssue;
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.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/**
* @author Andrea Boriero
*/
@TestForIssue(jiraKey = "HHH-11217")
@DomainModel(
xmlMappings = "org/hibernate/orm/test/refresh/Customer.hbm.xml"
)
@SessionFactory
public class RefreshUsingEntityNameTest {
private Customer customer;
@BeforeEach
public void setUp(SessionFactoryScope scope) {
customer = new Customer();
scope.inTransaction(
session ->
session.persist( "CustomName", customer )
);
}
@AfterEach
public void tearDown(SessionFactoryScope scope) {
scope.inTransaction(
session ->
session.createQuery( "delete from CustomName" ).executeUpdate()
);
}
@Test
public void testRefreshUsingEntityName(SessionFactoryScope scope) {
scope.inSession(
session ->
session.refresh( customer )
);
}
}

View File

@ -14,6 +14,7 @@ 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;
@ -23,6 +24,7 @@ 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;
@ -36,10 +38,9 @@ import static org.junit.Assert.fail;
/**
* @author Vlad Mihalcea
*/
@Jpa(annotatedClasses = {
AssociateEntityWithTwoSessionsTest.Location.class,
AssociateEntityWithTwoSessionsTest.Event.class
})
@Jpa(annotatedClasses = {AssociateEntityWithTwoSessionsTest.Location.class,
AssociateEntityWithTwoSessionsTest.Event.class},
properties = @Setting(name = AvailableSettings.ALLOW_REFRESH_DETACHED_ENTITY, value = "true"))
public class AssociateEntityWithTwoSessionsTest {
@Rule
@ -65,8 +66,8 @@ public class AssociateEntityWithTwoSessionsTest {
triggerable.reset();
scope.inTransaction( entityManager -> {
Event event1 = entityManager.find( Event.class, event.id );
Location location1 = event1.getLocation();
Event e = entityManager.find( Event.class, event.id );
Location location1 = e.getLocation();
try {
scope.inTransaction( _entityManager -> {

View File

@ -12,9 +12,10 @@ 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.internal.util.StringHelper;
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;
@ -47,7 +48,7 @@ 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") )
@DomainModel( annotatedClasses = {
EntityOfBasics.class,
ParameterMarkerStrategyTests.EntityWithFilters.class,

View File

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="org.hibernate.orm.test.refresh.Customer"
entity-name="CustomName"
lazy="false">
<id name="id"/>
</class>
</hibernate-mapping>