diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/onetomany/OneToManyWithUnmodifiableSetTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/onetomany/OneToManyWithUnmodifiableSetTest.java new file mode 100644 index 000000000000..48711431e002 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/onetomany/OneToManyWithUnmodifiableSetTest.java @@ -0,0 +1,102 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.annotations.onetomany; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Access; +import jakarta.persistence.AccessType; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.PreUpdate; +import jakarta.persistence.Transient; +import org.assertj.core.api.InstanceOfAssertFactories; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Allows testing a collection accessed via a getter that returns a {@link Collections#unmodifiableSet(Set)} + * + * @author Vincent Bouthinon + */ +@DomainModel( + annotatedClasses = { + OneToManyWithUnmodifiableSetTest.Client.class, + OneToManyWithUnmodifiableSetTest.Command.class + } +) +@SessionFactory +@JiraKey(value = "HHH-19589") +public class OneToManyWithUnmodifiableSetTest { + + @Test + void test(SessionFactoryScope scope) { + scope.inTransaction( session -> { + Client client = new Client(); + Command command = new Command(); + client.addToCommands( command ); + session.persist( client ); + session.persist( command ); + session.flush(); + session.clear(); + Client clientFind = session.find( Client.class, client.id ); + assertThat( clientFind ) + .isNotNull() + .extracting( Client::getCommands ) + .asInstanceOf( InstanceOfAssertFactories.COLLECTION ) + .isNotEmpty(); + } ); + } + + + @Entity(name = "Client") + public static class Client { + @Transient + protected Set commands = new HashSet<>(); + @Id + @GeneratedValue + private Long id; + + @OneToMany + @Access(AccessType.PROPERTY) + public Set getCommands() { + return Collections.unmodifiableSet( commands ); + } + + public void setCommands(Set commands) { + this.commands = commands; + } + + public void addToCommands(Command command) { + this.commands.add( command ); + } + + /** + * It triggers the {@link org.hibernate.event.internal.DefaultFlushEntityEventListener#copyState} method, because otherwise, if there’s no callbackPreUpdate, + * the system assumes the entity isn’t dirty and doesn’t perform copyState to check for dirtiness. + */ + @PreUpdate + public void preUpdate() { + } + } + + @Entity(name = "Command") + public static class Command { + + @Id + @GeneratedValue + private Long id; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/converted/converter/ConverterOverrideTypeRegisttrationTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/converted/converter/ConverterOverrideTypeRegisttrationTest.java new file mode 100644 index 000000000000..4718a5007f9f --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/converted/converter/ConverterOverrideTypeRegisttrationTest.java @@ -0,0 +1,102 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.mapping.converted.converter; + +import java.util.BitSet; + +import org.hibernate.annotations.TypeRegistration; +import org.hibernate.orm.test.mapping.basic.bitset.BitSetHelper; +import org.hibernate.orm.test.mapping.basic.bitset.BitSetUserType; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Convert; +import jakarta.persistence.Converter; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + *
+ * The @Converter should take precedence over @TypeRegistration.
+ * This test shows that this is not the case.
+ *
+ * To ensure that the @Converter is taken into account without @TypeRegistration, you just need to remove the @TypeRegistration.
+ * 
+ * + * @author Vincent Bouthinon + */ +@DomainModel( + annotatedClasses = { + ConverterOverrideTypeRegisttrationTest.SimpleEntity.class + } +) +@SessionFactory +@JiraKey(value = "HHH-19589") +public class ConverterOverrideTypeRegisttrationTest { + + @Test + void test(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final SimpleEntity object = new SimpleEntity( 77L ); + BitSet bitSet = new BitSet(); + bitSet.set( 0, true ); + object.setBitSet( bitSet ); + session.persist( object ); + session.flush(); + session.clear(); + SimpleEntity simpleEntity = session.find( SimpleEntity.class, object.id ); + assertThat( simpleEntity.getBitSet().get( 7 ) ).isTrue(); + } ); + } + + + @Entity(name = "SimpleEntity") + @TypeRegistration(basicClass = BitSet.class, userType = BitSetUserType.class) // Remove this annotation to test the use of @Converter + public static class SimpleEntity { + + @Id + private Long id; + @Convert(converter = BitSetConverter.class) + private BitSet bitSet; + + public SimpleEntity() { + } + + public SimpleEntity(Long id) { + this.id = id; + } + + public BitSet getBitSet() { + return bitSet; + } + + public void setBitSet(final BitSet bitSet) { + this.bitSet = bitSet; + } + } + + @Converter + public static class BitSetConverter implements AttributeConverter { + + @Override + public String convertToDatabaseColumn(final BitSet attribute) { + return BitSetHelper.bitSetToString( attribute ); + } + + @Override + public BitSet convertToEntityAttribute(final String dbData) { + BitSet bitSet = new BitSet(); + bitSet.set( 7, true ); + return bitSet; + } + } +}