Skip to content

Commit 392122e

Browse files
committed
HHH-19604 fix Session.isDirty() with @immutable entity
1 parent 63b072d commit 392122e

File tree

2 files changed

+62
-6
lines changed

2 files changed

+62
-6
lines changed

hibernate-core/src/main/java/org/hibernate/event/internal/DefaultDirtyCheckEventListener.java

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55
package org.hibernate.event.internal;
66

7+
import org.hibernate.AssertionFailure;
78
import org.hibernate.HibernateException;
89
import org.hibernate.collection.spi.PersistentCollection;
910
import org.hibernate.engine.spi.EntityEntry;
@@ -42,11 +43,7 @@ public void onDirtyCheck(DirtyCheckEvent event) throws HibernateException {
4243
final var holdersByKey = persistenceContext.getEntityHoldersByKey();
4344
if ( holdersByKey != null ) {
4445
for ( var entry : holdersByKey.entrySet() ) {
45-
final EntityHolder holder = entry.getValue();
46-
final EntityEntry entityEntry = holder.getEntityEntry();
47-
final Status status = entityEntry.getStatus();
48-
if ( status != Status.MANAGED && status != Status.GONE
49-
|| isEntityDirty( holder.getManagedObject(), holder.getDescriptor(), entityEntry, session ) ) {
46+
if ( isEntityDirty( entry.getValue(), session ) ) {
5047
event.setDirty( true );
5148
return;
5249
}
@@ -63,7 +60,18 @@ public void onDirtyCheck(DirtyCheckEvent event) throws HibernateException {
6360
}
6461
}
6562

66-
private static boolean isEntityDirty(
63+
private static boolean isEntityDirty(EntityHolder holder, EventSource session) {
64+
final EntityEntry entityEntry = holder.getEntityEntry();
65+
final Status status = entityEntry.getStatus();
66+
return switch ( status ) {
67+
case GONE, READ_ONLY -> false;
68+
case DELETED -> true;
69+
case MANAGED -> isManagedEntityDirty( holder.getManagedObject(), holder.getDescriptor(), entityEntry, session );
70+
case SAVING, LOADING -> throw new AssertionFailure( "Unexpected status: " + status );
71+
};
72+
}
73+
74+
private static boolean isManagedEntityDirty(
6775
Object entity, EntityPersister descriptor, EntityEntry entityEntry, EventSource session) {
6876
if ( entityEntry.requiresDirtyCheck( entity ) ) { // takes into account CustomEntityDirtinessStrategy
6977
final Object[] propertyValues =
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.orm.test;
6+
7+
import jakarta.persistence.Entity;
8+
import jakarta.persistence.Id;
9+
import jakarta.persistence.Table;
10+
import org.hibernate.annotations.Immutable;
11+
import org.hibernate.testing.orm.junit.DomainModel;
12+
import org.hibernate.testing.orm.junit.SessionFactory;
13+
import org.hibernate.testing.orm.junit.SessionFactoryScope;
14+
import org.junit.jupiter.api.Test;
15+
16+
import static org.junit.jupiter.api.Assertions.assertFalse;
17+
18+
@SessionFactory
19+
@DomainModel(annotatedClasses = {IsDirtyImmutableTest.Mutable.class, IsDirtyImmutableTest.NotMutable.class})
20+
class IsDirtyImmutableTest {
21+
@Test
22+
void test(SessionFactoryScope scope) {
23+
scope.inTransaction( s -> s.persist( new Mutable() ) );
24+
scope.inTransaction( s -> {
25+
var entity = s.find( NotMutable.class, 1L );
26+
assertFalse( s.isDirty() );
27+
entity.description = "new description";
28+
assertFalse( s.isDirty() );
29+
} );
30+
}
31+
32+
@Entity
33+
@Table(name = "TheTable")
34+
static class Mutable {
35+
@Id
36+
Long id = 1L;
37+
String description = "old description";
38+
}
39+
40+
@Immutable
41+
@Entity
42+
@Table(name = "TheTable")
43+
static class NotMutable {
44+
@Id
45+
Long id;
46+
String description;
47+
}
48+
}

0 commit comments

Comments
 (0)