diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultFlushEntityEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultFlushEntityEventListener.java index 50b4a23aaccb..205df311feba 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultFlushEntityEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultFlushEntityEventListener.java @@ -120,6 +120,12 @@ public void onFlushEntity(FlushEntityEvent event) throws HibernateException { final var entry = event.getEntityEntry(); final var session = event.getSession(); + // short-circuit for immutable entities.... + if ( !entry.getPersister().isMutable() && !entry.getPersister().hasCollections() ) { + // nothing to do + return; + } + final boolean mightBeDirty = entry.requiresDirtyCheck( entity ); final Object[] values = getValues( entity, entry, mightBeDirty, session ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/immutable/ImmutableDirtinessCheckingTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/immutable/ImmutableDirtinessCheckingTests.java new file mode 100644 index 000000000000..16448777cf41 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/immutable/ImmutableDirtinessCheckingTests.java @@ -0,0 +1,88 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.immutable; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import org.hibernate.annotations.Immutable; +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.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Steve Ebersole + */ +@DomainModel(annotatedClasses = ImmutableDirtinessCheckingTests.ReferenceData.class) +@SessionFactory +public class ImmutableDirtinessCheckingTests { + @Test + void testDirtyCheckingBehavior(SessionFactoryScope factoryScope) { + factoryScope.inTransaction( (session) -> { + var data = session.find( ReferenceData.class, 1 ); + data.setName( "another value" ); + nameAccessCount = 0; + session.flush(); + assertThat( nameAccessCount ).isEqualTo( 0 ); + } ); + + factoryScope.inTransaction( (session) -> { + var data = session.find( ReferenceData.class, 1 ); + assertThat( data.getName() ).isEqualTo( "initial value" ); + } ); + } + + @BeforeEach + void prepareTestData(SessionFactoryScope factoryScope) { + factoryScope.inTransaction( (session) -> { + session.persist( new ReferenceData( 1, "initial value" ) ); + } ); + } + + @Test + void dropTestData(SessionFactoryScope factoryScope) { + factoryScope.dropData(); + } + + private static int nameAccessCount = 0; + + @Entity(name="ReferenceData") + @Table(name="ReferenceData") + @Immutable + public static class ReferenceData { + private Integer id; + private String name; + + public ReferenceData() { + } + + public ReferenceData(Integer id, String name) { + this.id = id; + this.name = name; + } + + @Id + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + nameAccessCount++; + return name; + } + + public void setName(String name) { + this.name = name; + } + } +}