Update Hibernate and various other dependencies (#2986)

This ended up being wayyyy more complicated than expected due to
issues with Hibernate, various dependencies having conflicts with the
proto dependency version, and other breaking changes.

Notes:
- Hibernate 7 switches up the user type / converter system and for us,
  this means we must be / want to be more explicit with how we convert
and store things. For example, we need to add Postgres types to @Column
definitions.
- Hibernate 7.3 has an issue with generic MappedSuperclasses -- we have
  issues with BaseDomainLabelList. I'll investigate that, but for now
let's stick with 7.2.x
- H7 is more strict with annotations and prevents us from storing mapped
  superclasses embedded within other objects. This kinda makes sense but
makes the History objects a bit more difficult. We had to add "concrete"
embeddable DomainBase and HostBase objects that we can store/retrieve
from the DB.
- We convert some of the calls to "Query" to "TypedQuery" -- in
  Hibernate 8 / JPA 4.0 these will be super-deprecated and we'll need to
shift everything over, so this is necessary.
- You aren't supposed to put callback listeners on embedded entities
  (because it can be not obvious what's happening). We don't like that,
so we add our own annotations that are processed recursively for
embedded entities, so we get things like the update / create
timestamps.
- Hibernate doesn't allow for multiple converters to be auto-applied to
  the same "type" and it counts all VKey converters as one type.
Unfortunately, this means we have to explicitly mark each one.
- A bunch of other dependency changes were required to keep from having
  the proto 3/4 conflict
This commit is contained in:
gbrodman
2026-03-25 20:10:50 +00:00
committed by GitHub
parent 8c9b38e6af
commit 59b44b60df
91 changed files with 1663 additions and 3588 deletions
@@ -143,7 +143,7 @@ class BsaDownloadFunctionalTest {
tm().transact(
() ->
tm().getEntityManager()
.createNativeQuery("SELECT label from \"BsaLabel\"")
.createNativeQuery("SELECT label from \"BsaLabel\"", String.class)
.getResultList()));
}
@@ -43,8 +43,7 @@ public class HostHistoryTest extends EntityTestCase {
Host host = newHostWithRoid("ns1.example.com", "host1");
persistResource(host);
Host hostFromDb = loadByEntity(host);
HostHistory hostHistory = createHostHistory(hostFromDb);
persistResource(hostHistory);
HostHistory hostHistory = persistResource(createHostHistory(hostFromDb));
tm().transact(
() -> {
HostHistory fromDatabase = tm().loadByKey(hostHistory.createVKey());
@@ -22,6 +22,13 @@ import static google.registry.testing.DatabaseHelper.persistResource;
import com.google.common.collect.ImmutableSet;
import google.registry.model.ImmutableObject;
import google.registry.persistence.EntityCallbacksListener.RecursivePostLoad;
import google.registry.persistence.EntityCallbacksListener.RecursivePostPersist;
import google.registry.persistence.EntityCallbacksListener.RecursivePostRemove;
import google.registry.persistence.EntityCallbacksListener.RecursivePostUpdate;
import google.registry.persistence.EntityCallbacksListener.RecursivePrePersist;
import google.registry.persistence.EntityCallbacksListener.RecursivePreRemove;
import google.registry.persistence.EntityCallbacksListener.RecursivePreUpdate;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension;
import jakarta.persistence.Embeddable;
@@ -29,13 +36,6 @@ import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.MappedSuperclass;
import jakarta.persistence.PostLoad;
import jakarta.persistence.PostPersist;
import jakarta.persistence.PostRemove;
import jakarta.persistence.PostUpdate;
import jakarta.persistence.PrePersist;
import jakarta.persistence.PreRemove;
import jakarta.persistence.PreUpdate;
import jakarta.persistence.Transient;
import java.lang.reflect.Method;
import org.junit.jupiter.api.Test;
@@ -173,13 +173,13 @@ class EntityCallbacksListenerTest {
@Embedded EntityEmbedded entityEmbedded = new EntityEmbedded();
@PostLoad
@RecursivePostLoad
void entityPostLoad() {
entityPostLoad++;
nonTransientField++;
}
@PreUpdate
@RecursivePreUpdate
void entityPreUpdate() {
entityPreUpdate++;
}
@@ -193,8 +193,8 @@ class EntityCallbacksListenerTest {
String entityEmbedded = "placeholder";
@PostLoad
void entityEmbeddedPrePersist() {
@RecursivePostLoad
void entityEmbeddedPostLoad() {
entityEmbeddedPostLoad++;
}
}
@@ -205,7 +205,7 @@ class EntityCallbacksListenerTest {
String entityEmbeddedParent = "placeholder";
@PostLoad
@RecursivePostLoad
void entityEmbeddedParentPostLoad() {
entityEmbeddedParentPostLoad++;
}
@@ -223,37 +223,37 @@ class EntityCallbacksListenerTest {
String entityEmbeddedNested = "placeholder";
@PrePersist
@RecursivePrePersist
void entityEmbeddedNestedPrePersist() {
entityEmbeddedNestedPrePersist++;
}
@PreRemove
@RecursivePreRemove
void entityEmbeddedNestedPreRemove() {
entityEmbeddedNestedPreRemove++;
}
@PostPersist
@RecursivePostPersist
void entityEmbeddedNestedPostPersist() {
entityEmbeddedNestedPostPersist++;
}
@PostRemove
@RecursivePostRemove
void entityEmbeddedNestedPostRemove() {
entityEmbeddedNestedPostRemove++;
}
@PreUpdate
@RecursivePreUpdate
void entityEmbeddedNestedPreUpdate() {
entityEmbeddedNestedPreUpdate++;
}
@PostUpdate
@RecursivePostUpdate
void entityEmbeddedNestedPostUpdate() {
entityEmbeddedNestedPostUpdate++;
}
@PostLoad
@RecursivePostLoad
void entityEmbeddedNestedPostLoad() {
entityEmbeddedNestedPostLoad++;
}
@@ -267,12 +267,12 @@ class EntityCallbacksListenerTest {
String parentEntity = "placeholder";
@PostLoad
@RecursivePostLoad
void parentPostLoad() {
parentPostLoad++;
}
@PrePersist
@RecursivePrePersist
void parentPrePersist() {
parentPrePersist++;
}
@@ -286,7 +286,7 @@ class EntityCallbacksListenerTest {
@Embedded ParentEmbeddedNested parentEmbeddedNested = new ParentEmbeddedNested();
@PostLoad
@RecursivePostLoad
void parentEmbeddedPostLoad() {
parentEmbeddedPostLoad++;
}
@@ -298,7 +298,7 @@ class EntityCallbacksListenerTest {
String parentEmbeddedNested = "placeholder";
@PostLoad
@RecursivePostLoad
void parentEmbeddedNestedPostLoad() {
parentEmbeddedNestedPostLoad++;
}
@@ -310,7 +310,7 @@ class EntityCallbacksListenerTest {
String parentEmbeddedParent = "placeholder";
@PostLoad
@RecursivePostLoad
void parentEmbeddedParentPostLoad() {
parentEmbeddedParentPostLoad++;
}
@@ -28,6 +28,7 @@ import google.registry.model.common.TimedTransitionProperty;
import google.registry.model.domain.token.AllocationToken.TokenStatus;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import org.hibernate.annotations.Type;
@@ -75,6 +76,7 @@ public class AllocationTokenStatusTransitionUserTypeTest {
@Id String name = "id";
@Type(AllocationTokenStatusTransitionUserType.class)
@Column(columnDefinition = "hstore")
TimedTransitionProperty<TokenStatus> timedTransitionProperty;
private AllocationTokenStatusTransitionConverterTestEntity() {}
@@ -23,9 +23,11 @@ import static google.registry.testing.DatabaseHelper.persistResource;
import com.google.common.collect.ImmutableList;
import google.registry.model.ImmutableObject;
import google.registry.model.domain.token.AllocationToken;
import google.registry.model.domain.token.VKeyConverter_AllocationToken;
import google.registry.persistence.VKey;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension;
import jakarta.persistence.Convert;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import java.util.List;
@@ -62,6 +64,7 @@ public class AllocationTokenVkeyListUserTypeTest {
@Id String id = "id";
@Type(AllocationTokenVkeyListUserType.class)
@Convert(converter = VKeyConverter_AllocationToken.class)
List<VKey<AllocationToken>> tokenList;
TestAllocationTokenVKeyList() {}
@@ -25,6 +25,7 @@ import google.registry.model.ImmutableObject;
import google.registry.model.common.TimedTransitionProperty;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import org.hibernate.annotations.Type;
@@ -65,6 +66,7 @@ public class BillingCostTransitionUserTypeTest {
@Id String name = "id";
@Type(BillingCostTransitionUserType.class)
@Column(columnDefinition = "hstore")
TimedTransitionProperty<Money> timedTransitionProperty;
private TestEntity() {}
@@ -23,6 +23,7 @@ import google.registry.model.ImmutableObject;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension;
import google.registry.util.CidrAddressBlock;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import java.util.List;
@@ -59,6 +60,7 @@ public class CidrBlockListUserTypeTest {
@Id String name = "id";
@Type(CidrBlockListUserType.class)
@Column(columnDefinition = "text[]")
List<CidrAddressBlock> addresses;
private TestEntity() {}
@@ -22,6 +22,7 @@ import com.google.common.collect.ImmutableMap;
import google.registry.model.ImmutableObject;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import java.util.Map;
@@ -56,6 +57,7 @@ public class CurrencyToStringMapUserTypeTest {
@Id String name = "id";
@Type(CurrencyToStringMapUserType.class)
@Column(columnDefinition = "hstore")
Map<CurrencyUnit, String> currencyToBilling;
private TestEntity() {}
@@ -21,8 +21,10 @@ import static google.registry.testing.DatabaseHelper.persistResource;
import google.registry.model.ImmutableObject;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import org.hibernate.annotations.Type;
import org.joda.time.Duration;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
@@ -101,6 +103,8 @@ public class DurationUserTypeTest {
@Id String name = "id";
@Column(columnDefinition = "interval")
@Type(DurationUserType.class)
Duration duration;
public DurationTestEntity() {}
@@ -24,6 +24,7 @@ import google.registry.model.ImmutableObject;
import google.registry.persistence.VKey;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import java.net.InetAddress;
@@ -76,6 +77,7 @@ public class InetAddressSetUserTypeTest {
@Id String name = "id";
@Type(InetAddressSetUserType.class)
@Column(columnDefinition = "text[]")
Set<InetAddress> addresses;
private InetAddressSetTestEntity() {}
@@ -23,6 +23,7 @@ import google.registry.model.console.RegistrarRole;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension;
import google.registry.testing.DatabaseHelper;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import java.util.Map;
@@ -59,6 +60,7 @@ public class RegistrarToRoleMapUserTypeTest {
@Id String name = "id";
@Type(RegistrarToRoleMapUserType.class)
@Column(columnDefinition = "hstore")
Map<String, RegistrarRole> map;
private TestEntity() {}
@@ -24,6 +24,7 @@ import com.google.common.collect.ImmutableList;
import google.registry.model.ImmutableObject;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.NoResultException;
@@ -146,6 +147,7 @@ public class StringCollectionUserTypeTest {
@Id String name = "id";
@Type(TestListUserType.class)
@Column(columnDefinition = "text[]")
List<ListElement> value;
private TestEntity() {}
@@ -23,6 +23,7 @@ import com.google.common.collect.ImmutableMap;
import google.registry.model.ImmutableObject;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.NoResultException;
@@ -127,6 +128,7 @@ public class StringMapUserTypeTest {
@Id String name = "id";
@Type(StringMapUserType.class)
@Column(columnDefinition = "hstore")
Map<String, String> map;
private TestEntity() {}
@@ -54,7 +54,8 @@ public class StringValueEnumeratedTest {
tm().transact(
() ->
tm().getEntityManager()
.createNativeQuery("SELECT state FROM \"TestEntity\" WHERE name = 'id'")
.createNativeQuery(
"SELECT state FROM \"TestEntity\" WHERE name = 'id'", String.class)
.getSingleResult()))
.isEqualTo("DISABLED");
}
@@ -25,6 +25,7 @@ import google.registry.model.ImmutableObject;
import google.registry.model.common.TimedTransitionProperty;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.NoResultException;
@@ -145,6 +146,7 @@ class TimedTransitionBaseUserTypeTest {
@Id String name = "id";
@Type(StringTransitionUserType.class)
@Column(columnDefinition = "hstore")
TimedTransitionProperty<String> property;
private TestEntity() {}
@@ -25,6 +25,7 @@ import google.registry.model.common.TimedTransitionProperty;
import google.registry.model.tld.Tld.TldState;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import org.hibernate.annotations.Type;
@@ -68,6 +69,7 @@ class TldStateTransitionUserTypeTest {
@Id String name = "id";
@Type(TldStateTransitionUserType.class)
@Column(columnDefinition = "hstore")
TimedTransitionProperty<TldState> timedTransitionProperty;
private TestEntity() {}
@@ -41,8 +41,7 @@ import org.junit.jupiter.api.extension.RegisterExtension;
*/
public class HibernateLoggingSuppressionTest {
private static final String LOG_SUPPRESSION_TARGET =
"org.hibernate.engine.jdbc.spi.SqlExceptionHelper";
private static final String LOG_SUPPRESSION_TARGET = "org.hibernate.orm.jdbc.error";
// The line that should be added to the `logging.properties` file.
private static final String LOGGING_PROPERTIES_LINE = LOG_SUPPRESSION_TARGET + ".level=OFF\n";
@@ -115,7 +114,7 @@ public class HibernateLoggingSuppressionTest {
testLogHandler.getStoredLogRecords().stream()
.anyMatch(
logRecord ->
logRecord.getLevel().equals(Level.SEVERE)
logRecord.getLevel().equals(Level.WARNING)
&& logRecord.getMessage().contains("duplicate key")))
.isTrue();
}
@@ -133,7 +132,7 @@ public class HibernateLoggingSuppressionTest {
testLogHandler.getStoredLogRecords().stream()
.anyMatch(
logRecord ->
logRecord.getLevel().equals(Level.SEVERE)
logRecord.getLevel().equals(Level.WARNING)
&& logRecord.getMessage().contains("duplicate key")))
.isFalse();
revertSuppressionOfHibernateLogs();
@@ -51,7 +51,11 @@ class JpaTestExtensionsSqlLoggingTest {
@Test
void sqlLog_displayed() throws UnsupportedEncodingException {
tm().transact(() -> tm().getEntityManager().createNativeQuery("select 1").getSingleResult());
tm().transact(
() ->
tm().getEntityManager()
.createNativeQuery("select 1", long.class)
.getSingleResult());
assertThat(stdoutBuffer.toString(UTF_8.name())).contains("select 1");
}
}
@@ -185,7 +185,7 @@ public abstract class JpaTransactionManagerExtension
* <p>Test that create {@code EntityManagerFactory} instances are responsible for tearing them
* down.
*/
public ImmutableMap<String, String> getJpaProperties() {
public ImmutableMap<String, Object> getJpaProperties() {
Map<String, String> mergedProperties =
Maps.newHashMap(PersistenceModule.provideDefaultDatabaseConfigs());
if (!userProperties.isEmpty()) {
@@ -451,7 +451,7 @@ public abstract class JpaTransactionManagerExtension
}
/** Constructs the {@link EntityManagerFactory} instance. */
private EntityManagerFactory createEntityManagerFactory(ImmutableMap<String, String> properties) {
private EntityManagerFactory createEntityManagerFactory(ImmutableMap<String, Object> properties) {
ParsedPersistenceXmlDescriptor descriptor =
PersistenceXmlUtility.getParsedPersistenceXmlDescriptor();
@@ -68,7 +68,7 @@ public class JpaTransactionManagerExtensionTest {
() -> replicaTm().transact(() -> replicaTm().put(testEntity)))
.getCause())
.hasMessageThat()
.isEqualTo("Error while committing the transaction");
.startsWith("Error while committing the transaction");
}
@Test
@@ -26,7 +26,6 @@ import google.registry.model.ImmutableObject;
import google.registry.persistence.VKey;
import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension;
import google.registry.testing.FakeClock;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.MappedSuperclass;
@@ -376,7 +375,6 @@ public class TransactionManagerTest {
* We put the id field into a base class to test that id fields can be discovered in a base class.
*/
@MappedSuperclass
@Embeddable
private static class TestEntityBase extends ImmutableObject {
@Id protected String name;