HHH-10558 - Add support for java.time.Year;

HHH-13393 - Add support for ZoneId

ZoneOffsetJavaTypeDescriptor was already added
This commit is contained in:
Steve Ebersole 2021-12-08 17:31:02 -06:00
parent 61cba87096
commit 37bc00e567
6 changed files with 488 additions and 2 deletions

View File

@ -0,0 +1,85 @@
/*
* 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.type.descriptor.java;
import java.sql.Types;
import java.time.Year;
import java.time.format.DateTimeFormatter;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcTypeDescriptorIndicators;
/**
* Describes the {@link java.time.Year} Java type
*/
public class YearJavaTypeDescriptor extends AbstractClassJavaTypeDescriptor<Year> {
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern( "yyyy" );
public static final YearJavaTypeDescriptor INSTANCE = new YearJavaTypeDescriptor();
public YearJavaTypeDescriptor() {
super( Year.class );
}
@Override
public JdbcType getRecommendedJdbcType(JdbcTypeDescriptorIndicators context) {
return context.getTypeConfiguration().getJdbcTypeDescriptorRegistry().getDescriptor( Types.INTEGER );
}
@Override
public String toString(Year value) {
return value == null ? null : value.format( FORMATTER );
}
@Override
public Year fromString(CharSequence string) {
return string == null ? null : Year.parse( string, FORMATTER );
}
@SuppressWarnings("unchecked")
@Override
public <X> X unwrap(Year value, Class<X> type, WrapperOptions options) {
if ( value == null ) {
return null;
}
if ( type.isInstance( value ) ) {
return (X) value;
}
if ( Integer.class.isAssignableFrom( type ) ) {
return (X) Integer.valueOf( value.getValue() );
}
if ( Long.class.isAssignableFrom( type ) ) {
return (X) Long.valueOf( value.getValue() );
}
if ( String.class.isAssignableFrom( type ) ) {
return (X) toString( value );
}
throw unknownUnwrap( type );
}
@Override
public <X> Year wrap(X value, WrapperOptions options) {
if ( value == null ) {
return null;
}
if ( value instanceof Number ) {
return Year.of( ( (Number) value ).intValue() );
}
if ( value instanceof String ) {
return fromString( (String) value );
}
throw unknownWrap( value.getClass() );
}
}

View File

@ -0,0 +1,70 @@
/*
* 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.type.descriptor.java;
import java.sql.Types;
import java.time.ZoneId;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcTypeDescriptorIndicators;
/**
* Describes the {@link ZoneId} Java type
*/
public class ZoneIdJavaTypeDescriptor extends AbstractClassJavaTypeDescriptor<ZoneId> {
/**
* Singleton access
*/
public static final ZoneIdJavaTypeDescriptor INSTANCE = new ZoneIdJavaTypeDescriptor();
public ZoneIdJavaTypeDescriptor() {
super( ZoneId.class );
}
@Override
public JdbcType getRecommendedJdbcType(JdbcTypeDescriptorIndicators indicators) {
return indicators.getTypeConfiguration().getJdbcTypeDescriptorRegistry().getDescriptor( Types.VARCHAR );
}
@Override
public String toString(ZoneId value) {
return value == null ? null : value.getId();
}
@Override
public ZoneId fromString(CharSequence string) {
return string == null ? null : ZoneId.of( string.toString() );
}
@SuppressWarnings("unchecked")
@Override
public <X> X unwrap(ZoneId value, Class<X> type, WrapperOptions options) {
if ( value == null ) {
return null;
}
if ( String.class.isAssignableFrom( type ) ) {
return (X) toString( value );
}
throw unknownUnwrap( type );
}
@Override
public <X> ZoneId wrap(X value, WrapperOptions options) {
if ( value == null ) {
return null;
}
if ( value instanceof String ) {
return fromString( (String) value );
}
throw unknownWrap( value.getClass() );
}
}

View File

@ -56,6 +56,9 @@ public class ZoneOffsetJavaTypeDescriptor extends AbstractClassJavaTypeDescripto
if ( String.class.isAssignableFrom( type ) ) { if ( String.class.isAssignableFrom( type ) ) {
return (X) toString( value ); return (X) toString( value );
} }
if ( Integer.class.isAssignableFrom( type ) ) {
return (X) Integer.valueOf( value.getTotalSeconds() );
}
throw unknownUnwrap( type ); throw unknownUnwrap( type );
} }
@ -64,9 +67,12 @@ public class ZoneOffsetJavaTypeDescriptor extends AbstractClassJavaTypeDescripto
if ( value == null ) { if ( value == null ) {
return null; return null;
} }
if ( CharSequence.class.isInstance( value ) ) { if ( value instanceof CharSequence ) {
return fromString( (CharSequence) value ); return fromString( (CharSequence) value );
} }
if ( value instanceof Integer ) {
return ZoneOffset.ofTotalSeconds( (Integer) value );
}
throw unknownWrap( value.getClass() ); throw unknownWrap( value.getClass() );
} }

View File

@ -69,6 +69,8 @@ import org.hibernate.type.descriptor.java.StringJavaTypeDescriptor;
import org.hibernate.type.descriptor.java.TimeZoneJavaTypeDescriptor; import org.hibernate.type.descriptor.java.TimeZoneJavaTypeDescriptor;
import org.hibernate.type.descriptor.java.UUIDJavaTypeDescriptor; import org.hibernate.type.descriptor.java.UUIDJavaTypeDescriptor;
import org.hibernate.type.descriptor.java.UrlJavaTypeDescriptor; import org.hibernate.type.descriptor.java.UrlJavaTypeDescriptor;
import org.hibernate.type.descriptor.java.YearJavaTypeDescriptor;
import org.hibernate.type.descriptor.java.ZoneIdJavaTypeDescriptor;
import org.hibernate.type.descriptor.java.ZoneOffsetJavaTypeDescriptor; import org.hibernate.type.descriptor.java.ZoneOffsetJavaTypeDescriptor;
import org.hibernate.type.descriptor.java.ZonedDateTimeJavaTypeDescriptor; import org.hibernate.type.descriptor.java.ZonedDateTimeJavaTypeDescriptor;
@ -129,6 +131,9 @@ public class JavaTypeDescriptorBaseline {
target.addBaselineDescriptor( OffsetDateTimeJavaTypeDescriptor.INSTANCE ); target.addBaselineDescriptor( OffsetDateTimeJavaTypeDescriptor.INSTANCE );
target.addBaselineDescriptor( OffsetTimeJavaTypeDescriptor.INSTANCE ); target.addBaselineDescriptor( OffsetTimeJavaTypeDescriptor.INSTANCE );
target.addBaselineDescriptor( ZonedDateTimeJavaTypeDescriptor.INSTANCE ); target.addBaselineDescriptor( ZonedDateTimeJavaTypeDescriptor.INSTANCE );
target.addBaselineDescriptor( YearJavaTypeDescriptor.INSTANCE );
target.addBaselineDescriptor( ZoneIdJavaTypeDescriptor.INSTANCE );
target.addBaselineDescriptor( ZoneOffsetJavaTypeDescriptor.INSTANCE );
target.addBaselineDescriptor( CalendarJavaTypeDescriptor.INSTANCE ); target.addBaselineDescriptor( CalendarJavaTypeDescriptor.INSTANCE );
target.addBaselineDescriptor( DateJavaTypeDescriptor.INSTANCE ); target.addBaselineDescriptor( DateJavaTypeDescriptor.INSTANCE );
@ -136,7 +141,6 @@ public class JavaTypeDescriptorBaseline {
target.addBaselineDescriptor( java.sql.Time.class, JdbcTimeJavaTypeDescriptor.INSTANCE ); target.addBaselineDescriptor( java.sql.Time.class, JdbcTimeJavaTypeDescriptor.INSTANCE );
target.addBaselineDescriptor( java.sql.Timestamp.class, JdbcTimestampJavaTypeDescriptor.INSTANCE ); target.addBaselineDescriptor( java.sql.Timestamp.class, JdbcTimestampJavaTypeDescriptor.INSTANCE );
target.addBaselineDescriptor( TimeZoneJavaTypeDescriptor.INSTANCE ); target.addBaselineDescriptor( TimeZoneJavaTypeDescriptor.INSTANCE );
target.addBaselineDescriptor( ZoneOffsetJavaTypeDescriptor.INSTANCE );
target.addBaselineDescriptor( ClassJavaTypeDescriptor.INSTANCE ); target.addBaselineDescriptor( ClassJavaTypeDescriptor.INSTANCE );

View File

@ -0,0 +1,158 @@
/*
* 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.type.java;
import java.sql.Types;
import java.time.Year;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping;
import org.hibernate.metamodel.mapping.internal.BasicValuedCollectionPart;
import org.hibernate.persister.entity.EntityPersister;
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.CollectionTable;
import jakarta.persistence.Column;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.MapKeyColumn;
import jakarta.persistence.Table;
import static org.assertj.core.api.Assertions.assertThat;
@DomainModel( annotatedClasses = YearMappingTests.YearMappingTestEntity.class )
@SessionFactory
@JiraKey( "HHH-10558" )
public class YearMappingTests {
@Test
public void basicAssertions(SessionFactoryScope scope) {
final SessionFactoryImplementor sessionFactory = scope.getSessionFactory();
final EntityPersister entityDescriptor = sessionFactory.getMetamodel().entityPersister( YearMappingTestEntity.class );
{
final BasicAttributeMapping yearAttribute = (BasicAttributeMapping) entityDescriptor.findAttributeMapping( "year" );
assertThat( yearAttribute.getJdbcMapping().getJdbcTypeDescriptor().getJdbcTypeCode() ).isEqualTo( Types.INTEGER );
assertThat( yearAttribute.getJdbcMapping().getJavaTypeDescriptor().getJavaTypeClass() ).isEqualTo( Year.class );
}
{
final PluralAttributeMapping yearsAttribute = (PluralAttributeMapping) entityDescriptor.findAttributeMapping( "years" );
final BasicValuedCollectionPart elementDescriptor = (BasicValuedCollectionPart) yearsAttribute.getElementDescriptor();
assertThat( elementDescriptor.getJdbcMapping().getJdbcTypeDescriptor().getJdbcTypeCode() ).isEqualTo( Types.INTEGER );
assertThat( elementDescriptor.getJdbcMapping().getJavaTypeDescriptor().getJavaTypeClass() ).isEqualTo( Year.class );
}
{
final PluralAttributeMapping countByYearAttribute = (PluralAttributeMapping) entityDescriptor.findAttributeMapping( "countByYear" );
final BasicValuedCollectionPart keyDescriptor = (BasicValuedCollectionPart) countByYearAttribute.getIndexDescriptor();
assertThat( keyDescriptor.getJdbcMapping().getJdbcTypeDescriptor().getJdbcTypeCode() ).isEqualTo( Types.INTEGER );
assertThat( keyDescriptor.getJdbcMapping().getJavaTypeDescriptor().getJavaTypeClass() ).isEqualTo( Year.class );
}
}
@Test
public void testUsage(SessionFactoryScope scope) {
final YearMappingTestEntity entity = new YearMappingTestEntity( 1, "one", Year.now() );
final YearMappingTestEntity entity2 = new YearMappingTestEntity( 2, "two", Year.parse( "+10000" ) );
scope.inTransaction( (session) -> {
session.save( entity );
session.save( entity2 );
} );
try {
scope.inTransaction( (session) -> session.createQuery( "from YearMappingTestEntity" ).list() );
}
finally {
scope.inTransaction( session -> session.delete( entity ) );
scope.inTransaction( session -> session.delete( entity2 ) );
}
}
@Entity( name = "YearMappingTestEntity" )
@Table( name = "year_map_test_entity" )
public static class YearMappingTestEntity {
private Integer id;
private String name;
private Year year;
private Set<Year> years = new HashSet<>();
private Map<Year,Integer> countByYear = new HashMap<>();
public YearMappingTestEntity() {
}
public YearMappingTestEntity(Integer id, String name, Year year) {
this.id = id;
this.name = name;
this.year = year;
}
@Id
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Year getYear() {
return year;
}
public void setYear(Year year) {
this.year = year;
}
@ElementCollection
@CollectionTable(
name = "entity_year",
joinColumns = @JoinColumn( name = "entity_id" )
)
@Column( name = "year" )
public Set<Year> getYears() {
return years;
}
public void setYears(Set<Year> years) {
this.years = years;
}
@ElementCollection
@CollectionTable( name = "count_by_year", joinColumns = @JoinColumn( name = "entity_id" ) )
@MapKeyColumn( name = "year" )
@Column( name = "cnt" )
public Map<Year, Integer> getCountByYear() {
return countByYear;
}
public void setCountByYear(Map<Year, Integer> countByYear) {
this.countByYear = countByYear;
}
}
}

View File

@ -0,0 +1,163 @@
/*
* 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.type.java;
import java.sql.Types;
import java.time.Year;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping;
import org.hibernate.persister.entity.EntityPersister;
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.CollectionTable;
import jakarta.persistence.Column;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.Table;
import static org.assertj.core.api.Assertions.assertThat;
@JiraKey( "HHH-13393" )
@DomainModel( annotatedClasses = ZoneMappingTests.ZoneMappingTestEntity.class )
@SessionFactory
public class ZoneMappingTests {
@Test
public void basicAssertions(SessionFactoryScope scope) {
final SessionFactoryImplementor sessionFactory = scope.getSessionFactory();
final EntityPersister entityDescriptor = sessionFactory.getMetamodel().entityPersister( ZoneMappingTestEntity.class );
{
final BasicAttributeMapping zoneIdAttribute = (BasicAttributeMapping) entityDescriptor.findAttributeMapping( "zoneId" );
assertThat( zoneIdAttribute.getJdbcMapping().getJdbcTypeDescriptor().getJdbcTypeCode() ).isEqualTo( Types.VARCHAR );
assertThat( zoneIdAttribute.getJdbcMapping().getJavaTypeDescriptor().getJavaTypeClass() ).isEqualTo( ZoneId.class );
}
{
final BasicAttributeMapping zoneOffsetAttribute = (BasicAttributeMapping) entityDescriptor.findAttributeMapping( "zoneOffset" );
assertThat( zoneOffsetAttribute.getJdbcMapping().getJdbcTypeDescriptor().getJdbcTypeCode() ).isEqualTo( Types.VARCHAR );
assertThat( zoneOffsetAttribute.getJdbcMapping().getJavaTypeDescriptor().getJavaTypeClass() ).isEqualTo( ZoneOffset.class );
}
}
@Test
public void testUsage(SessionFactoryScope scope) {
final ZoneMappingTestEntity entity = new ZoneMappingTestEntity( 1, "one", ZoneId.systemDefault(), ZoneOffset.UTC );
final ZoneMappingTestEntity entity2 = new ZoneMappingTestEntity( 2, "two", ZoneId.systemDefault(), ZoneOffset.ofHours( 0 ) );
final ZoneMappingTestEntity entity3 = new ZoneMappingTestEntity( 3, "three", ZoneId.systemDefault(), ZoneOffset.ofHours( -10 ) );
scope.inTransaction( (session) -> {
session.persist( entity );
session.persist( entity2 );
session.persist( entity3 );
} );
try {
scope.inTransaction( (session) -> {
session.createQuery( "from ZoneMappingTestEntity" ).list();
});
}
finally {
scope.inTransaction( (session) -> {
session.createQuery( "delete ZoneMappingTestEntity" ).executeUpdate();
});
}
}
@Entity( name = "ZoneMappingTestEntity" )
@Table( name = "zone_map_test_entity" )
public static class ZoneMappingTestEntity {
private Integer id;
private String name;
private ZoneId zoneId;
private Set<ZoneId> zoneIds = new HashSet<>();
private ZoneOffset zoneOffset;
private Set<ZoneOffset> zoneOffsets = new HashSet<>();
public ZoneMappingTestEntity() {
}
public ZoneMappingTestEntity(Integer id, String name, ZoneId zoneId, ZoneOffset zoneOffset) {
this.id = id;
this.name = name;
this.zoneId = zoneId;
this.zoneOffset = zoneOffset;
}
@Id
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ZoneId getZoneId() {
return zoneId;
}
public void setZoneId(ZoneId zoneId) {
this.zoneId = zoneId;
}
@ElementCollection
@CollectionTable( name = "zone_ids", joinColumns = @JoinColumn( name = "entity_id" ) )
@Column( name = "zone_id" )
public Set<ZoneId> getZoneIds() {
return zoneIds;
}
public void setZoneIds(Set<ZoneId> zoneIds) {
this.zoneIds = zoneIds;
}
public ZoneOffset getZoneOffset() {
return zoneOffset;
}
public void setZoneOffset(ZoneOffset zoneOffset) {
this.zoneOffset = zoneOffset;
}
@ElementCollection
@CollectionTable( name = "zone_offsets", joinColumns = @JoinColumn( name = "entity_id" ) )
@Column( name = "zone_offset" )
public Set<ZoneOffset> getZoneOffsets() {
return zoneOffsets;
}
public void setZoneOffsets(Set<ZoneOffset> zoneOffsets) {
this.zoneOffsets = zoneOffsets;
}
}
}