From 55c4d36409763d04564c408c660991d094085b66 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Wed, 31 May 2023 18:55:36 +0200 Subject: [PATCH] HHH-16250 Test case for allowing to map same column twice, once with a user type --- .../basic/RepeatedMappingUserTypeTests.java | 188 ++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/RepeatedMappingUserTypeTests.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/RepeatedMappingUserTypeTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/RepeatedMappingUserTypeTests.java new file mode 100644 index 0000000000..79f7dd7261 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/RepeatedMappingUserTypeTests.java @@ -0,0 +1,188 @@ +/* + * 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 http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.mapping.basic; + +import java.io.Serializable; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Arrays; +import java.util.Objects; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.stream.Collectors; + +import org.hibernate.annotations.Type; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.type.SqlTypes; +import org.hibernate.usertype.UserType; + +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.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@JiraKey("HHH-16250") +@DomainModel(annotatedClasses = RepeatedMappingUserTypeTests.TheEntity.class) +@SessionFactory +public class RepeatedMappingUserTypeTests { + + @Test + public void testResolution(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.persist( new TheEntity( 1, 123, 456, 789 ) ); + session.flush(); + session.clear(); + final TheEntity theEntity = session.find( TheEntity.class, 1 ); + assertNotNull( theEntity.getSortedIds() ); + assertNotNull( theEntity.getSortedIdsExpression() ); + assertEquals( new TreeSet<>( Arrays.asList( 123, 456, 789 ) ), theEntity.getSortedIds() ); + } + ); + } + + @Entity(name = "TheEntity") + public static class TheEntity { + @Id + private Integer id; + + @Type(CodeJavaType.class) + @Column(name = "SORTED_IDS", nullable = false) + private SortedSet sortedIds = new TreeSet<>(); + + @Column(name = "SORTED_IDS", nullable = false, insertable = false, updatable = false) + private String sortedIdsExpression; + + public TheEntity() { + } + + public TheEntity(Integer id, Integer... ids) { + this.id = id; + this.sortedIds.addAll( Arrays.asList( ids ) ); + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public SortedSet getSortedIds() { + return sortedIds; + } + + public void setSortedIds(SortedSet sortedIds) { + this.sortedIds = sortedIds; + } + + public String getSortedIdsExpression() { + return sortedIdsExpression; + } + + public void setSortedIdsExpression(String sortedIdsExpression) { + this.sortedIdsExpression = sortedIdsExpression; + } + } + + public static class CodeJavaType implements UserType>, + AttributeConverter, String> { + + @Override + public int getSqlType() { + return SqlTypes.VARCHAR; + } + + @Override + public Class> returnedClass() { + //noinspection unchecked + return (Class) Set.class; + } + + @Override + public boolean equals(SortedSet x, SortedSet y) { + return Objects.equals( x, y ); + } + + @Override + public int hashCode(SortedSet x) { + return Objects.hashCode( x ); + } + + @Override + public SortedSet nullSafeGet( + ResultSet rs, + int position, + SharedSessionContractImplementor session, + Object owner) throws SQLException { + return convertToEntityAttribute( rs.getString( position ) ); + } + + @Override + public void nullSafeSet( + PreparedStatement st, + SortedSet values, + int index, + SharedSessionContractImplementor session) throws SQLException { + if ( values == null || values.isEmpty() ) { + st.setNull( index, SqlTypes.VARCHAR ); + return; + } + + String databaseValue = convertToDatabaseColumn( values ); + + st.setString( index, databaseValue ); + } + + @Override + public SortedSet deepCopy(SortedSet value) { + return new TreeSet<>( value ); + } + + @Override + public boolean isMutable() { + return true; + } + + @Override + public Serializable disassemble(SortedSet value) { + return (Serializable) value; + } + + @Override + public SortedSet assemble(Serializable cached, Object owner) { + return (SortedSet) cached; + } + + @Override + public String convertToDatabaseColumn(SortedSet values) { + return values.stream().map( Object::toString ).collect( Collectors.joining( "|", "|", "|" ) ); + } + + @Override + public SortedSet convertToEntityAttribute(String databaseValue) { + return Arrays.stream( databaseValue.split( "\\|" ) ) + .map( value -> value.trim() ) + .filter( value -> !value.isEmpty() ) + .map( value -> Integer.valueOf( value ) ) + .collect( Collectors.toCollection( TreeSet::new ) ); + } + + } +}