From 41866da029df525903e4e49d121d2bda94e773dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 7 Feb 2023 11:29:45 +0100 Subject: [PATCH 1/2] Upgrade to Hibernate ORM 5.6.15.Final Fixes #30234 --- bom/application/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index bebfc838d602c..8583ced254e94 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -91,7 +91,7 @@ 3.12.0 1.15 1.5.1 - 5.6.14.Final + 5.6.15.Final 1.12.18 1.1.9.Final 6.2.5.Final From 735aedb49f3503b8684a9f11f9b967c3faaa1a22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Mon, 16 Jan 2023 13:34:38 +0100 Subject: [PATCH 2/2] Test updates of @Basic attributes with Hibernate ORM Reproducer for https://hibernate.atlassian.net/browse/HHH-16049 --- .../lazyloading/AbstractLazyBasicTest.java | 190 +++++++++++++++--- .../LazyBasicDefaultGroupTest.java | 4 +- .../LazyBasicMultiNonDefaultGroupTest.java | 4 +- .../LazyBasicNonDefaultGroupTest.java | 4 +- 4 files changed, 174 insertions(+), 28 deletions(-) diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/lazyloading/AbstractLazyBasicTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/lazyloading/AbstractLazyBasicTest.java index d19a4dd62178a..833986331b750 100644 --- a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/lazyloading/AbstractLazyBasicTest.java +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/lazyloading/AbstractLazyBasicTest.java @@ -1,11 +1,17 @@ package io.quarkus.hibernate.orm.lazyloading; import static io.quarkus.hibernate.orm.TransactionTestUtils.inTransaction; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; import javax.inject.Inject; import javax.persistence.EntityManager; import javax.transaction.UserTransaction; +import org.hibernate.resource.jdbc.spi.StatementInspector; import org.junit.jupiter.api.Test; public abstract class AbstractLazyBasicTest { @@ -24,11 +30,61 @@ public AbstractLazyBasicTest(AccessDelegate delegate) { } @Test - public void update_all_nullToNonNull() { + public void update_all_nullToNull() { initNull(); + // Updating lazy properties always results in updates, even if the value didn't change, + // because we don't know of their previous value. + StatementSpy.checkAtLeastOneUpdate(() -> inTransaction(transaction, () -> { + delegate.updateAllProperties(em, entityId, null, null, null); + })); inTransaction(transaction, () -> { - delegate.updateAllProperties(em, entityId, "updated1", "updated2", "updated3"); + delegate.testLazyLoadingAndPersistedValues(em, entityId, null, null, null); + }); + } + + @Test + public void update_allLazy_nullToNull() { + initNull(); + // Updating lazy properties always results in updates, even if the value didn't change, + // because we don't know of their previous value. + StatementSpy.checkAtLeastOneUpdate(() -> inTransaction(transaction, () -> { + delegate.updateAllLazyProperties(em, entityId, null, null); + })); + inTransaction(transaction, () -> { + delegate.testLazyLoadingAndPersistedValues(em, entityId, null, null, null); }); + } + + @Test + public void update_oneEager_nullToNull() { + initNull(); + StatementSpy.checkNoUpdate(() -> inTransaction(transaction, () -> { + delegate.updateOneEagerProperty(em, entityId, null); + })); + inTransaction(transaction, () -> { + delegate.testLazyLoadingAndPersistedValues(em, entityId, null, null, null); + }); + } + + @Test + public void update_oneLazy_nullToNull() { + initNull(); + // Updating lazy properties always results in updates, even if the value didn't change, + // because we don't know of their previous value. + StatementSpy.checkAtLeastOneUpdate(() -> inTransaction(transaction, () -> { + delegate.updateOneLazyProperty(em, entityId, null); + })); + inTransaction(transaction, () -> { + delegate.testLazyLoadingAndPersistedValues(em, entityId, null, null, null); + }); + } + + @Test + public void update_all_nullToNonNull() { + initNull(); + StatementSpy.checkAtLeastOneUpdate(() -> inTransaction(transaction, () -> { + delegate.updateAllProperties(em, entityId, "updated1", "updated2", "updated3"); + })); inTransaction(transaction, () -> { delegate.testLazyLoadingAndPersistedValues(em, entityId, "updated1", "updated2", "updated3"); }); @@ -37,9 +93,9 @@ public void update_all_nullToNonNull() { @Test public void update_allLazy_nullToNonNull() { initNull(); - inTransaction(transaction, () -> { + StatementSpy.checkAtLeastOneUpdate(() -> inTransaction(transaction, () -> { delegate.updateAllLazyProperties(em, entityId, "updated1", "updated2"); - }); + })); inTransaction(transaction, () -> { delegate.testLazyLoadingAndPersistedValues(em, entityId, null, "updated1", "updated2"); }); @@ -48,9 +104,9 @@ public void update_allLazy_nullToNonNull() { @Test public void update_oneEager_nullToNonNull() { initNull(); - inTransaction(transaction, () -> { + StatementSpy.checkAtLeastOneUpdate(() -> inTransaction(transaction, () -> { delegate.updateOneEagerProperty(em, entityId, "updated1"); - }); + })); inTransaction(transaction, () -> { delegate.testLazyLoadingAndPersistedValues(em, entityId, "updated1", null, null); }); @@ -59,64 +115,112 @@ public void update_oneEager_nullToNonNull() { @Test public void update_oneLazy_nullToNonNull() { initNull(); - inTransaction(transaction, () -> { + StatementSpy.checkAtLeastOneUpdate(() -> inTransaction(transaction, () -> { delegate.updateOneLazyProperty(em, entityId, "updated2"); - }); + })); inTransaction(transaction, () -> { delegate.testLazyLoadingAndPersistedValues(em, entityId, null, "updated2", null); }); } @Test - public void update_all_nonNullToNonNull() { + public void update_all_nonNullToNonNull_differentValue() { initNonNull(); - inTransaction(transaction, () -> { + StatementSpy.checkAtLeastOneUpdate(() -> inTransaction(transaction, () -> { delegate.updateAllProperties(em, entityId, "updated1", "updated2", "updated3"); - }); + })); inTransaction(transaction, () -> { delegate.testLazyLoadingAndPersistedValues(em, entityId, "updated1", "updated2", "updated3"); }); } @Test - public void update_allLazy_nonNullToNonNull() { + public void update_all_nonNullToNonNull_sameValue() { initNonNull(); + // Updating lazy properties always results in updates, even if the value didn't change, + // because we don't know of their previous value. + StatementSpy.checkAtLeastOneUpdate(() -> inTransaction(transaction, () -> { + delegate.updateAllProperties(em, entityId, "initial1", "initial2", "initial3"); + })); inTransaction(transaction, () -> { - delegate.updateAllLazyProperties(em, entityId, "updated1", "updated2"); + delegate.testLazyLoadingAndPersistedValues(em, entityId, "initial1", "initial2", "initial3"); }); + } + + @Test + public void update_allLazy_nonNullToNonNull_differentValue() { + initNonNull(); + StatementSpy.checkAtLeastOneUpdate(() -> inTransaction(transaction, () -> { + delegate.updateAllLazyProperties(em, entityId, "updated1", "updated2"); + })); inTransaction(transaction, () -> { delegate.testLazyLoadingAndPersistedValues(em, entityId, "initial1", "updated1", "updated2"); }); } @Test - public void update_oneEager_nonNullToNonNull() { + public void update_allLazy_nonNullToNonNull_sameValue() { initNonNull(); + // Updating lazy properties always results in updates, even if the value didn't change, + // because we don't know of their previous value. + StatementSpy.checkAtLeastOneUpdate(() -> inTransaction(transaction, () -> { + delegate.updateAllLazyProperties(em, entityId, "initial2", "initial3"); + })); inTransaction(transaction, () -> { - delegate.updateOneEagerProperty(em, entityId, "updated1"); + delegate.testLazyLoadingAndPersistedValues(em, entityId, "initial1", "initial2", "initial3"); }); + } + + @Test + public void update_oneEager_nonNullToNonNull_differentValue() { + initNonNull(); + StatementSpy.checkAtLeastOneUpdate(() -> inTransaction(transaction, () -> { + delegate.updateOneEagerProperty(em, entityId, "updated1"); + })); inTransaction(transaction, () -> { delegate.testLazyLoadingAndPersistedValues(em, entityId, "updated1", "initial2", "initial3"); }); } @Test - public void update_oneLazy_nonNullToNonNull() { + public void update_oneEager_nonNullToNonNull_sameValue() { initNonNull(); + StatementSpy.checkNoUpdate(() -> inTransaction(transaction, () -> { + delegate.updateOneEagerProperty(em, entityId, "initial1"); + })); inTransaction(transaction, () -> { - delegate.updateOneLazyProperty(em, entityId, "updated2"); + delegate.testLazyLoadingAndPersistedValues(em, entityId, "initial1", "initial2", "initial3"); }); + } + + @Test + public void update_oneLazy_nonNullToNonNull_differentValue() { + initNonNull(); + StatementSpy.checkAtLeastOneUpdate(() -> inTransaction(transaction, () -> { + delegate.updateOneLazyProperty(em, entityId, "updated2"); + })); inTransaction(transaction, () -> { delegate.testLazyLoadingAndPersistedValues(em, entityId, "initial1", "updated2", "initial3"); }); } @Test - public void update_all_nonNullToNull() { + public void update_oneLazy_nonNullToNonNull_sameValue() { initNonNull(); + StatementSpy.checkAtLeastOneUpdate(() -> inTransaction(transaction, () -> { + delegate.updateOneLazyProperty(em, entityId, "initial2"); + })); inTransaction(transaction, () -> { - delegate.updateAllProperties(em, entityId, null, null, null); + delegate.testLazyLoadingAndPersistedValues(em, entityId, "initial1", "initial2", "initial3"); }); + } + + @Test + public void update_all_nonNullToNull() { + initNonNull(); + StatementSpy.checkAtLeastOneUpdate(() -> inTransaction(transaction, () -> { + delegate.updateAllProperties(em, entityId, null, null, null); + })); inTransaction(transaction, () -> { delegate.testLazyLoadingAndPersistedValues(em, entityId, null, null, null); }); @@ -125,9 +229,9 @@ public void update_all_nonNullToNull() { @Test public void update_allLazy_nonNullToNull() { initNonNull(); - inTransaction(transaction, () -> { + StatementSpy.checkAtLeastOneUpdate(() -> inTransaction(transaction, () -> { delegate.updateAllLazyProperties(em, entityId, null, null); - }); + })); inTransaction(transaction, () -> { delegate.testLazyLoadingAndPersistedValues(em, entityId, "initial1", null, null); }); @@ -136,9 +240,9 @@ public void update_allLazy_nonNullToNull() { @Test public void update_oneEager_nonNullToNull() { initNonNull(); - inTransaction(transaction, () -> { + StatementSpy.checkAtLeastOneUpdate(() -> inTransaction(transaction, () -> { delegate.updateOneEagerProperty(em, entityId, null); - }); + })); inTransaction(transaction, () -> { delegate.testLazyLoadingAndPersistedValues(em, entityId, null, "initial2", "initial3"); }); @@ -147,9 +251,9 @@ public void update_oneEager_nonNullToNull() { @Test public void update_oneLazy_nonNullToNull() { initNonNull(); - inTransaction(transaction, () -> { + StatementSpy.checkAtLeastOneUpdate(() -> inTransaction(transaction, () -> { delegate.updateOneLazyProperty(em, entityId, null); - }); + })); inTransaction(transaction, () -> { delegate.testLazyLoadingAndPersistedValues(em, entityId, "initial1", null, "initial3"); }); @@ -197,4 +301,40 @@ void testLazyLoadingAndPersistedValues(EntityManager entityManager, long entityI String expectedLazyProperty1, String expectedLazyProperty2); } + + public static class StatementSpy implements StatementInspector { + private static final ThreadLocal> statements = new ThreadLocal<>(); + + public static void checkAtLeastOneUpdate(Runnable runnable) { + check(runnable, list -> assertThat(list) + .isNotEmpty() // Something is wrong if we didn't even load an entity + .anySatisfy(sql -> assertThat(sql).containsIgnoringCase("update"))); + } + + public static void checkNoUpdate(Runnable runnable) { + check(runnable, list -> assertThat(list) + .isNotEmpty() // Something is wrong if we didn't even load an entity + .allSatisfy(sql -> assertThat(sql).doesNotContainIgnoringCase("update"))); + } + + public static void check(Runnable runnable, Consumer> assertion) { + List list = new ArrayList<>(); + if (statements.get() != null) { + throw new IllegalStateException("Cannot nest checkNoUpdate()"); + } + statements.set(list); + runnable.run(); + statements.remove(); + assertion.accept(list); + } + + @Override + public String inspect(String sql) { + List list = statements.get(); + if (list != null) { + list.add(sql); + } + return sql; + } + } } diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/lazyloading/LazyBasicDefaultGroupTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/lazyloading/LazyBasicDefaultGroupTest.java index 9de6fb25239b1..d75c2aa211169 100644 --- a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/lazyloading/LazyBasicDefaultGroupTest.java +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/lazyloading/LazyBasicDefaultGroupTest.java @@ -25,7 +25,9 @@ public class LazyBasicDefaultGroupTest extends AbstractLazyBasicTest { .addClass(MyEntity.class) .addClass(AccessDelegate.class) .addClass(AccessDelegateImpl.class)) - .withConfigurationResource("application.properties"); + .withConfigurationResource("application.properties") + .overrideConfigKey("quarkus.hibernate-orm.unsupported-properties.\"hibernate.session_factory.statement_inspector\"", + StatementSpy.class.getName()); public LazyBasicDefaultGroupTest() { super(new AccessDelegateImpl()); diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/lazyloading/LazyBasicMultiNonDefaultGroupTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/lazyloading/LazyBasicMultiNonDefaultGroupTest.java index b55f00ab4e8f1..6f32d4c6a2b91 100644 --- a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/lazyloading/LazyBasicMultiNonDefaultGroupTest.java +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/lazyloading/LazyBasicMultiNonDefaultGroupTest.java @@ -26,7 +26,9 @@ public class LazyBasicMultiNonDefaultGroupTest extends AbstractLazyBasicTest { .addClass(MyEntity.class) .addClass(AccessDelegate.class) .addClass(AccessDelegateImpl.class)) - .withConfigurationResource("application.properties"); + .withConfigurationResource("application.properties") + .overrideConfigKey("quarkus.hibernate-orm.unsupported-properties.\"hibernate.session_factory.statement_inspector\"", + StatementSpy.class.getName()); public LazyBasicMultiNonDefaultGroupTest() { super(new AccessDelegateImpl()); diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/lazyloading/LazyBasicNonDefaultGroupTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/lazyloading/LazyBasicNonDefaultGroupTest.java index 066369407e320..0166fc381d4b4 100644 --- a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/lazyloading/LazyBasicNonDefaultGroupTest.java +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/lazyloading/LazyBasicNonDefaultGroupTest.java @@ -26,7 +26,9 @@ public class LazyBasicNonDefaultGroupTest extends AbstractLazyBasicTest { .addClass(MyEntity.class) .addClass(AccessDelegate.class) .addClass(AccessDelegateImpl.class)) - .withConfigurationResource("application.properties"); + .withConfigurationResource("application.properties") + .overrideConfigKey("quarkus.hibernate-orm.unsupported-properties.\"hibernate.session_factory.statement_inspector\"", + StatementSpy.class.getName()); public LazyBasicNonDefaultGroupTest() { super(new AccessDelegateImpl());