From c25140025f34c2b0b7ab8009b5a135b93632abd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 17 Sep 2021 15:20:44 +0200 Subject: [PATCH] HHH-14828 Test bytecode enhancement on entities/embeddables with a final field --- .../basic/FinalFieldEnhancementTest.java | 297 ++++++++++++++++++ 1 file changed, 297 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/basic/FinalFieldEnhancementTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/basic/FinalFieldEnhancementTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/basic/FinalFieldEnhancementTest.java new file mode 100644 index 0000000000..2bb38af839 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/basic/FinalFieldEnhancementTest.java @@ -0,0 +1,297 @@ +/* + * 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 . + */ +package org.hibernate.test.bytecode.enhancement.basic; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.Serializable; +import java.util.Objects; +import java.util.UUID; +import javax.persistence.Embeddable; +import javax.persistence.Embedded; +import javax.persistence.EmbeddedId; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +import org.hibernate.annotations.Immutable; + +import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(BytecodeEnhancerRunner.class) +public class FinalFieldEnhancementTest extends BaseCoreFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + EntityWithFinalField.class, + EntityWithEmbeddedIdWithFinalField.class, EntityWithEmbeddedIdWithFinalField.EmbeddableId.class, + EntityWithEmbeddedNonIdWithFinalField.class, EntityWithEmbeddedNonIdWithFinalField.EmbeddableNonId.class + }; + } + + @Test + public void entityWithFinalField_constructor() { + EntityWithFinalField entity = new EntityWithFinalField( "foo" ); + assertThat( entity.immutableProperty ).isEqualTo( "foo" ); + } + + // Just test that the embedded non-ID works correctly over a persist/retrieve cycle + @Test + public void entityWithFinalField_smokeTest() { + EntityWithFinalField persistedEntity = new EntityWithFinalField( "foo" ); + persistedEntity.setName( "Some name" ); + inTransaction( s -> { + s.persist( persistedEntity ); + } ); + + inTransaction( s -> { + EntityWithFinalField entity = s.find( EntityWithFinalField.class, persistedEntity.getId() ); + assertThat( entity ).extracting( EntityWithFinalField::getImmutableProperty ) + .isEqualTo( persistedEntity.getImmutableProperty() ); + } ); + } + + // Just test that the embedded ID works correctly over a persist/retrieve cycle + @Test + public void embeddableIdWithFinalField_smokeTest() { + EntityWithEmbeddedIdWithFinalField persistedEntity = new EntityWithEmbeddedIdWithFinalField(); + persistedEntity.setName( "Some name" ); + inTransaction( s -> { + s.persist( persistedEntity ); + } ); + + // Read with the same ID instance + inTransaction( s -> { + EntityWithEmbeddedIdWithFinalField entity = s.find( EntityWithEmbeddedIdWithFinalField.class, persistedEntity.getId() ); + assertThat( entity ).extracting( EntityWithEmbeddedIdWithFinalField::getId ).extracting( i -> i.id ) + .isEqualTo( persistedEntity.getId().id ); + } ); + + // Read with a new ID instance + inTransaction( s -> { + EntityWithEmbeddedIdWithFinalField entity = s.find( EntityWithEmbeddedIdWithFinalField.class, EntityWithEmbeddedIdWithFinalField.EmbeddableId.of( persistedEntity.getId().id ) ); + assertThat( entity ).extracting( EntityWithEmbeddedIdWithFinalField::getId ).extracting( i -> i.id ) + .isEqualTo( persistedEntity.getId().id ); + } ); + + // Read with a query + // This is special because in this particular test, + // we know Hibernate ORM *has to* instantiate the EmbeddableIdType itself: + // it cannot reuse the ID we passed. + // And since the EmbeddableIdType has a final field, instantiation will not be able to use a no-arg constructor... + inTransaction( s -> { + EntityWithEmbeddedIdWithFinalField entity = + s.createQuery( "from embidwithfinal e where e.name = :name", EntityWithEmbeddedIdWithFinalField.class ) + .setParameter( "name", persistedEntity.getName() ) + .uniqueResult(); + assertThat( entity ).extracting( EntityWithEmbeddedIdWithFinalField::getId ).extracting( i -> i.id ) + .isEqualTo( persistedEntity.getId().id ); + } ); + } + + @Test + public void embeddableNonIdWithFinalField_constructor() { + EntityWithEmbeddedNonIdWithFinalField.EmbeddableNonId embeddable = + new EntityWithEmbeddedNonIdWithFinalField.EmbeddableNonId( "foo" ); + assertThat( embeddable.immutableProperty ).isEqualTo( "foo" ); + } + + // Just test that the embedded non-ID works correctly over a persist/retrieve cycle + @Test + public void embeddableNonIdWithFinalField_smokeTest() { + EntityWithEmbeddedNonIdWithFinalField persistedEntity = new EntityWithEmbeddedNonIdWithFinalField(); + persistedEntity.setName( "Some name" ); + persistedEntity.setEmbedded( new EntityWithEmbeddedNonIdWithFinalField.EmbeddableNonId( "foo" ) ); + inTransaction( s -> { + s.persist( persistedEntity ); + } ); + + inTransaction( s -> { + EntityWithEmbeddedNonIdWithFinalField entity = s.find( EntityWithEmbeddedNonIdWithFinalField.class, persistedEntity.getId() ); + assertThat( entity ).extracting( EntityWithEmbeddedNonIdWithFinalField::getEmbedded ) + .extracting( EntityWithEmbeddedNonIdWithFinalField.EmbeddableNonId::getImmutableProperty ) + .isEqualTo( persistedEntity.getEmbedded().getImmutableProperty() ); + } ); + } + + @Entity(name = "withfinal") + public static class EntityWithFinalField { + + @Id + @GeneratedValue + private Long id; + + private final String immutableProperty; + + private String name; + + // For Hibernate ORM + protected EntityWithFinalField() { + this.immutableProperty = null; + } + + private EntityWithFinalField(String id) { + this.immutableProperty = id; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getImmutableProperty() { + return immutableProperty; + } + } + + @Entity(name = "embidwithfinal") + public static class EntityWithEmbeddedIdWithFinalField { + + @EmbeddedId + private EmbeddableId id; + + private String name; + + public EntityWithEmbeddedIdWithFinalField() { + this.id = EmbeddableId.of( UUID.randomUUID().toString() ); + } + + public EmbeddableId getId() { + return id; + } + + public void setId(EmbeddableId id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Immutable + @Embeddable + public static class EmbeddableId implements Serializable { + private final String id; + + // For Hibernate ORM + protected EmbeddableId() { + this.id = null; + } + + private EmbeddableId(String id) { + this.id = id; + } + + public static EmbeddableId of(String string) { + return new EmbeddableId( string ); + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( !( o instanceof EmbeddableId ) ) { + return false; + } + EmbeddableId embeddableIdType = (EmbeddableId) o; + return Objects.equals( id, embeddableIdType.id ); + } + + @Override + public int hashCode() { + return Objects.hash( id ); + } + } + } + + @Entity(name = "embwithfinal") + public static class EntityWithEmbeddedNonIdWithFinalField { + + @Id + @GeneratedValue + private Long id; + + private String name; + + @Embedded + private EmbeddableNonId embedded; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public EmbeddableNonId getEmbedded() { + return embedded; + } + + public void setEmbedded( + EmbeddableNonId embedded) { + this.embedded = embedded; + } + + @Embeddable + public static class EmbeddableNonId { + private final String immutableProperty; + + private String mutableProperty; + + protected EmbeddableNonId() { + // For Hibernate ORM only - it will change the property value through reflection + this.immutableProperty = null; + } + + private EmbeddableNonId(String immutableProperty) { + this.immutableProperty = immutableProperty; + } + + public String getImmutableProperty() { + return immutableProperty; + } + + public String getMutableProperty() { + return mutableProperty; + } + + public void setMutableProperty(String mutableProperty) { + this.mutableProperty = mutableProperty; + } + } + } + +}