Fix BasicTypeResolution for collections annotated with @MapKeyTemporal
This commit is contained in:
parent
f14e70cb8f
commit
26e98c0879
|
@ -41,12 +41,10 @@ import org.hibernate.type.spi.TypeConfiguration;
|
|||
* type info was supplied.
|
||||
*/
|
||||
public class InferredBasicValueResolver {
|
||||
/**
|
||||
* Create an inference-based resolution
|
||||
*/
|
||||
|
||||
public static <T> BasicValue.Resolution<T> from(
|
||||
Function<TypeConfiguration, BasicJavaType> explicitJavaTypeAccess,
|
||||
Function<TypeConfiguration, JdbcType> explicitSqlTypeAccess,
|
||||
BasicJavaType<T> explicitJavaType,
|
||||
JdbcType explicitJdbcType,
|
||||
Type resolvedJavaType,
|
||||
Supplier<JavaType<T>> reflectedJtdResolver,
|
||||
JdbcTypeIndicators stdIndicators,
|
||||
|
@ -55,14 +53,6 @@ public class InferredBasicValueResolver {
|
|||
String ownerName,
|
||||
String propertyName,
|
||||
TypeConfiguration typeConfiguration) {
|
||||
|
||||
final BasicJavaType<T> explicitJavaType = explicitJavaTypeAccess != null
|
||||
? explicitJavaTypeAccess.apply( typeConfiguration )
|
||||
: null;
|
||||
final JdbcType explicitJdbcType = explicitSqlTypeAccess
|
||||
!= null ? explicitSqlTypeAccess.apply( typeConfiguration )
|
||||
: null;
|
||||
|
||||
final JavaType<T> reflectedJtd = reflectedJtdResolver.get();
|
||||
|
||||
// NOTE : the distinction that is made below wrt `explicitJavaType` and `reflectedJtd` is
|
||||
|
@ -85,6 +75,16 @@ public class InferredBasicValueResolver {
|
|||
legacyType = jdbcMapping;
|
||||
}
|
||||
else {
|
||||
if ( explicitJavaType instanceof TemporalJavaType ) {
|
||||
return fromTemporal(
|
||||
(TemporalJavaType<T>) explicitJavaType,
|
||||
null,
|
||||
null,
|
||||
resolvedJavaType,
|
||||
stdIndicators,
|
||||
typeConfiguration
|
||||
);
|
||||
}
|
||||
// we need to infer the JdbcType and use that to build the value-mapping
|
||||
final JdbcType inferredJdbcType = explicitJavaType.getRecommendedJdbcType( stdIndicators );
|
||||
if ( inferredJdbcType instanceof ObjectJdbcType ) {
|
||||
|
@ -271,6 +271,41 @@ public class InferredBasicValueResolver {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an inference-based resolution
|
||||
*/
|
||||
public static <T> BasicValue.Resolution<T> from(
|
||||
Function<TypeConfiguration, BasicJavaType> explicitJavaTypeAccess,
|
||||
Function<TypeConfiguration, JdbcType> explicitSqlTypeAccess,
|
||||
Type resolvedJavaType,
|
||||
Supplier<JavaType<T>> reflectedJtdResolver,
|
||||
JdbcTypeIndicators stdIndicators,
|
||||
Table table,
|
||||
Selectable selectable,
|
||||
String ownerName,
|
||||
String propertyName,
|
||||
TypeConfiguration typeConfiguration) {
|
||||
|
||||
final BasicJavaType<T> explicitJavaType = explicitJavaTypeAccess != null
|
||||
? explicitJavaTypeAccess.apply( typeConfiguration )
|
||||
: null;
|
||||
final JdbcType explicitJdbcType = explicitSqlTypeAccess
|
||||
!= null ? explicitSqlTypeAccess.apply( typeConfiguration )
|
||||
: null;
|
||||
return InferredBasicValueResolver.from(
|
||||
explicitJavaType,
|
||||
explicitJdbcType,
|
||||
resolvedJavaType,
|
||||
reflectedJtdResolver,
|
||||
stdIndicators,
|
||||
table,
|
||||
selectable,
|
||||
ownerName,
|
||||
propertyName,
|
||||
typeConfiguration
|
||||
);
|
||||
}
|
||||
|
||||
public static <T> BasicType<T> resolveSqlTypeIndicators(
|
||||
JdbcTypeIndicators stdIndicators,
|
||||
BasicType<T> resolved,
|
||||
|
|
|
@ -522,7 +522,8 @@ public class BasicValueBinder implements JdbcTypeIndicators {
|
|||
|
||||
final MapKeyClass mapKeyClassAnn = mapAttribute.getAnnotation( MapKeyClass.class );
|
||||
if ( mapKeyClassAnn != null ) {
|
||||
return (BasicJavaType<?>) typeConfiguration.getJavaTypeRegistry().getDescriptor( mapKeyClassAnn.value() );
|
||||
return (BasicJavaType<?>) typeConfiguration.getJavaTypeRegistry()
|
||||
.getDescriptor( mapKeyClassAnn.value() );
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
|
@ -411,12 +411,16 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
|
|||
|
||||
// determine JavaType if we can
|
||||
|
||||
final BasicJavaType explicitJtd;
|
||||
if ( explicitJavaTypeAccess != null ) {
|
||||
final BasicJavaType<?> explicitJtd = explicitJavaTypeAccess.apply( typeConfiguration );
|
||||
explicitJtd = explicitJavaTypeAccess.apply( typeConfiguration );
|
||||
if ( explicitJtd != null ) {
|
||||
jtd = explicitJtd;
|
||||
}
|
||||
}
|
||||
else {
|
||||
explicitJtd = null;
|
||||
}
|
||||
|
||||
if ( jtd == null ) {
|
||||
if ( implicitJavaTypeAccess != null ) {
|
||||
|
@ -434,12 +438,17 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
|
|||
}
|
||||
}
|
||||
|
||||
final JdbcType jdbcType;
|
||||
if ( explicitJdbcTypeAccess != null ) {
|
||||
jdbcType = explicitJdbcTypeAccess.apply( typeConfiguration );
|
||||
}
|
||||
else {
|
||||
jdbcType = null;
|
||||
}
|
||||
|
||||
if ( jtd == null ) {
|
||||
if ( explicitJdbcTypeAccess != null ) {
|
||||
final JdbcType jdbcType = explicitJdbcTypeAccess.apply( typeConfiguration );
|
||||
if ( jdbcType != null ) {
|
||||
jtd = jdbcType.getJdbcRecommendedJavaTypeMapping( null, null, typeConfiguration );
|
||||
}
|
||||
if ( jdbcType != null ) {
|
||||
jtd = jdbcType.getJdbcRecommendedJavaTypeMapping( null, null, typeConfiguration );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -460,8 +469,8 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
|
|||
}
|
||||
|
||||
return InferredBasicValueResolver.from(
|
||||
explicitJavaTypeAccess,
|
||||
explicitJdbcTypeAccess,
|
||||
explicitJtd,
|
||||
jdbcType,
|
||||
resolvedJavaType,
|
||||
this::determineReflectedJavaType,
|
||||
this,
|
||||
|
|
|
@ -0,0 +1,197 @@
|
|||
/*
|
||||
* 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>.
|
||||
*/
|
||||
|
||||
//$Id$
|
||||
package org.hibernate.orm.test.jpa;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
|
||||
import org.hibernate.testing.orm.junit.Jpa;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.MapKeyClass;
|
||||
import jakarta.persistence.MapKeyColumn;
|
||||
import jakarta.persistence.MapKeyTemporal;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.persistence.Temporal;
|
||||
import jakarta.persistence.TemporalType;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@Jpa(
|
||||
annotatedClasses = {
|
||||
MapKeyTest.School.class,
|
||||
MapKeyTest.Person.class
|
||||
}
|
||||
)
|
||||
public class MapKeyTest {
|
||||
|
||||
|
||||
@Test
|
||||
public void testMapKeyTemporal(EntityManagerFactoryScope scope) throws Exception {
|
||||
|
||||
SimpleDateFormat formatter = new SimpleDateFormat( "yyyy-MM-dd" );
|
||||
final Date date1 = formatter.parse( "2022-02-02" );
|
||||
final Date date2 = java.sql.Date.valueOf( formatter.format( Calendar.getInstance().getTime() ) );
|
||||
|
||||
Set<Date> expectedDates = new HashSet<>();
|
||||
expectedDates.add( date1 );
|
||||
expectedDates.add( date2 );
|
||||
|
||||
School school1 = new School( 1, "High School" );
|
||||
School school2 = new School( 2, "Primary School" );
|
||||
|
||||
Person person1 = new Person( 1, "Andrea", school2 );
|
||||
Person person2 = new Person( 2, "Luigi", school2 );
|
||||
|
||||
Set<Person> expectedPeople = new HashSet<>();
|
||||
expectedPeople.add( person1 );
|
||||
expectedPeople.add( person2 );
|
||||
|
||||
scope.inTransaction(
|
||||
entityManager -> {
|
||||
Map<Date, Person> lastNames = new HashMap<>();
|
||||
lastNames.put( date1, person1 );
|
||||
lastNames.put( date2, person2 );
|
||||
school2.setStudentsByDate( lastNames );
|
||||
|
||||
entityManager.persist( school1 );
|
||||
entityManager.persist( school2 );
|
||||
|
||||
entityManager.persist( person1 );
|
||||
entityManager.persist( person2 );
|
||||
}
|
||||
);
|
||||
|
||||
scope.inTransaction(
|
||||
entityManager -> {
|
||||
Person person = entityManager.find( Person.class, 2 );
|
||||
School school = person.getSchool();
|
||||
|
||||
Map<Date, Person> studentsByDate = school.getStudentsByDate();
|
||||
|
||||
Set<Date> dates = studentsByDate.keySet();
|
||||
assertEquals( expectedDates.size(), dates.size() );
|
||||
assertTrue( expectedDates.containsAll( dates ) );
|
||||
|
||||
Collection<Person> people = studentsByDate.values();
|
||||
assertEquals( expectedPeople.size(), people.size() );
|
||||
assertTrue( expectedPeople.containsAll( people ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Entity
|
||||
@Table(name = "PERSON_TABLE")
|
||||
public static class Person {
|
||||
|
||||
@Id
|
||||
private int id;
|
||||
|
||||
private String name;
|
||||
|
||||
@ManyToOne
|
||||
private School school;
|
||||
|
||||
public Person() {
|
||||
}
|
||||
|
||||
public Person(int id, String name, School school) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.school = school;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public School getSchool() {
|
||||
return school;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if ( this == o ) {
|
||||
return true;
|
||||
}
|
||||
if ( o == null || getClass() != o.getClass() ) {
|
||||
return false;
|
||||
}
|
||||
Person person = (Person) o;
|
||||
return name.equals( person.name );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash( name );
|
||||
}
|
||||
}
|
||||
|
||||
@Entity
|
||||
@Table(name = "SCHOOL_TABLE")
|
||||
public static class School {
|
||||
|
||||
@Id
|
||||
private int id;
|
||||
|
||||
private String name;
|
||||
|
||||
@OneToMany(mappedBy = "school")
|
||||
@MapKeyClass(Date.class)
|
||||
@MapKeyColumn(name = "THE_DATE")
|
||||
@MapKeyTemporal(TemporalType.DATE)
|
||||
private Map<Date, Person> studentsByDate;
|
||||
|
||||
@Temporal( TemporalType.DATE )
|
||||
private Date aDate;
|
||||
|
||||
public School() {
|
||||
}
|
||||
|
||||
public School(int id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Map getStudentsByDate() {
|
||||
return studentsByDate;
|
||||
}
|
||||
|
||||
public void setStudentsByDate(Map studentsByDate) {
|
||||
this.studentsByDate = studentsByDate;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue