diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/binding/HibernateTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/metamodel/binding/HibernateTypeDescriptor.java index c4699bc2fc..6629783f50 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/binding/HibernateTypeDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/binding/HibernateTypeDescriptor.java @@ -23,6 +23,7 @@ */ package org.hibernate.metamodel.binding; +import java.util.HashMap; import java.util.Map; import org.hibernate.type.Type; @@ -36,7 +37,7 @@ public class HibernateTypeDescriptor { private String explicitTypeName; private String javaTypeName; private boolean isToOne; - private Map typeParameters; + private Map typeParameters = new HashMap( ); private Type resolvedTypeMapping; diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/attribute/MappedAttribute.java b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/attribute/MappedAttribute.java index c777e2b126..a11aa1568f 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/attribute/MappedAttribute.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/attribute/MappedAttribute.java @@ -23,16 +23,23 @@ */ package org.hibernate.metamodel.source.annotations.attribute; +import java.util.Calendar; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.persistence.TemporalType; import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.AnnotationValue; import org.jboss.jandex.DotName; +import org.hibernate.AnnotationException; +import org.hibernate.AssertionFailure; +import org.hibernate.cfg.NotYetImplementedException; import org.hibernate.metamodel.source.annotations.AnnotationBindingContext; import org.hibernate.metamodel.source.annotations.HibernateDotNames; +import org.hibernate.metamodel.source.annotations.JPADotNames; import org.hibernate.metamodel.source.annotations.JandexHelper; /** @@ -72,6 +79,9 @@ public abstract class MappedAttribute implements Comparable { */ private final Map explicitHibernateTypeParameters; + /** + * The binding context + */ private final AnnotationBindingContext context; MappedAttribute(String name, Class attributeType, String accessType, Map> annotations, AnnotationBindingContext context) { @@ -80,34 +90,8 @@ public abstract class MappedAttribute implements Comparable { this.name = name; this.attributeType = attributeType; this.accessType = accessType; - - final AnnotationInstance typeAnnotation = JandexHelper.getSingleAnnotation( - annotations(), - HibernateDotNames.TYPE - ); - if ( typeAnnotation != null ) { - this.explicitHibernateTypeName = typeAnnotation.value( "type" ).asString(); - this.explicitHibernateTypeParameters = extractTypeParameters( typeAnnotation ); - } - else { - this.explicitHibernateTypeName = null; - this.explicitHibernateTypeParameters = new HashMap(); - } - } - - private Map extractTypeParameters(AnnotationInstance typeAnnotation) { - HashMap typeParameters = new HashMap(); - AnnotationValue parameterAnnotationValue = typeAnnotation.value( "parameters" ); - if ( parameterAnnotationValue != null ) { - AnnotationInstance[] parameterAnnotations = parameterAnnotationValue.asNestedArray(); - for ( AnnotationInstance parameterAnnotationInstance : parameterAnnotations ) { - typeParameters.put( - parameterAnnotationInstance.value( "name" ).asString(), - parameterAnnotationInstance.value( "value" ).asString() - ); - } - } - return typeParameters; + this.explicitHibernateTypeParameters = new HashMap(); + this.explicitHibernateTypeName = determineExplicitHibernateTypeName(); } public String getName() { @@ -151,6 +135,86 @@ public abstract class MappedAttribute implements Comparable { sb.append( '}' ); return sb.toString(); } + + private String getTemporalType() { + final AnnotationInstance temporalAnnotation = JandexHelper.getSingleAnnotation( + annotations(), + JPADotNames.TEMPORAL + ); + if ( isTemporalType( attributeType ) ) { + if ( temporalAnnotation == null ) { + //SPEC 11.1.47 The Temporal annotation must be specified for persistent fields or properties of type java.util.Date and java.util.Calendar. + throw new AnnotationException( "Attribute " + name + " is a Temporal type, but no @Temporal annotation found." ); + } + TemporalType temporalType = JandexHelper.getEnumValue( temporalAnnotation, "value", TemporalType.class ); + boolean isDate = Date.class.isAssignableFrom( attributeType ); + String type = null; + switch ( temporalType ) { + case DATE: + type = isDate ? "date" : "calendar_date"; + break; + case TIME: + type = "time"; + if ( !isDate ) { + throw new NotYetImplementedException( "Calendar cannot persist TIME only" ); + } + break; + case TIMESTAMP: + type = isDate ? "timestamp" : "calendar"; + break; + default: + throw new AssertionFailure( "Unknown temporal type: " + temporalType ); + } + return type; + } + else { + if ( temporalAnnotation != null ) { + throw new AnnotationException( + "@Temporal should only be set on a java.util.Date or java.util.Calendar property: " + name + ); + } + } + return null; + } + + private boolean isTemporalType(Class type) { + return Date.class.isAssignableFrom( type ) || Calendar.class.isAssignableFrom( type ); + //todo (stliu) java.sql.Date is not listed in spec + // || java.sql.Date.class.isAssignableFrom( type ) + } + + private Map extractTypeParameters(AnnotationInstance typeAnnotation) { + HashMap typeParameters = new HashMap(); + AnnotationValue parameterAnnotationValue = typeAnnotation.value( "parameters" ); + if ( parameterAnnotationValue != null ) { + AnnotationInstance[] parameterAnnotations = parameterAnnotationValue.asNestedArray(); + for ( AnnotationInstance parameterAnnotationInstance : parameterAnnotations ) { + typeParameters.put( + parameterAnnotationInstance.value( "name" ).asString(), + parameterAnnotationInstance.value( "value" ).asString() + ); + } + } + return typeParameters; + } + + private String determineExplicitHibernateTypeName() { + String typeName = null; + String temporalType = getTemporalType(); + final AnnotationInstance typeAnnotation = JandexHelper.getSingleAnnotation( + annotations(), + HibernateDotNames.TYPE + ); + if ( typeAnnotation != null ) { + typeName = typeAnnotation.value( "type" ).asString(); + this.explicitHibernateTypeParameters.putAll( extractTypeParameters( typeAnnotation ) ); + } + else if ( temporalType != null ) { + typeName = temporalType; + + } + return typeName; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/source/binder/Binder.java b/hibernate-core/src/main/java/org/hibernate/metamodel/source/binder/Binder.java index c90806e4f8..a5be5ae056 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/source/binder/Binder.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/source/binder/Binder.java @@ -34,6 +34,7 @@ import org.hibernate.EntityMode; import org.hibernate.cfg.NotYetImplementedException; import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.beans.BeanInfoHelper; +import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.metamodel.binding.AbstractPluralAttributeBinding; import org.hibernate.metamodel.binding.AttributeBinding; import org.hibernate.metamodel.binding.CollectionElementNature; @@ -517,7 +518,7 @@ public class Binder { attributeBinding.getHibernateTypeDescriptor().setExplicitTypeName( explicitTypeName ); } final Map parameters = typeSource.getParameters(); - if ( parameters != null ) { + if ( parameters!=null) { attributeBinding.getHibernateTypeDescriptor().getTypeParameters().putAll( parameters ); } } diff --git a/hibernate-core/src/test/java/org/hibernate/metamodel/source/annotations/entity/TemporalBindingTests.java b/hibernate-core/src/test/java/org/hibernate/metamodel/source/annotations/entity/TemporalBindingTests.java new file mode 100644 index 0000000000..f83c553e09 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/metamodel/source/annotations/entity/TemporalBindingTests.java @@ -0,0 +1,81 @@ +package org.hibernate.metamodel.source.annotations.entity; + +import java.util.Date; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +import org.junit.Test; + +import org.hibernate.AnnotationException; +import org.hibernate.metamodel.binding.AttributeBinding; +import org.hibernate.metamodel.binding.EntityBinding; +import org.hibernate.metamodel.binding.HibernateTypeDescriptor; +import org.hibernate.type.TimestampType; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * @author Strong Liu + */ +public class TemporalBindingTests extends BaseAnnotationBindingTestCase { + @Entity + class Item1 { + @Id + long id; + Date date; + } + + @Test(expected = AnnotationException.class) + @Resources(annotatedClasses = TemporalBindingTests.Item1.class) + public void testNoTemporalAnnotationOnTemporalTypeAttribute() { + getEntityBinding( Item1.class ); + + } + + @Entity + class Item2 { + @Id + long id; + @Temporal(TemporalType.TIMESTAMP) + Date date; + } + + @Test + @Resources(annotatedClasses = TemporalBindingTests.Item2.class) + public void testTemporalTypeAttribute() { + EntityBinding binding = getEntityBinding( Item2.class ); + AttributeBinding attributeBinding = binding.getAttributeBinding( "date" ); + HibernateTypeDescriptor descriptor = attributeBinding.getHibernateTypeDescriptor(); + assertEquals( "timestamp", descriptor.getExplicitTypeName() ); + assertEquals( Date.class.getName(), descriptor.getJavaTypeName() ); + assertNotNull( descriptor.getResolvedTypeMapping() ); + assertEquals( TimestampType.class, descriptor.getResolvedTypeMapping().getClass() ); + assertNotNull( descriptor.getTypeParameters() ); + assertTrue( descriptor.getTypeParameters().isEmpty() ); + } + + @Entity + class Item3 { + @Id + @Temporal(TemporalType.TIMESTAMP) + Date date; + } + + @Test + @Resources(annotatedClasses = TemporalBindingTests.Item3.class) + public void testTemporalTypeAsId() { + EntityBinding binding = getEntityBinding( Item3.class ); + AttributeBinding attributeBinding = binding.getAttributeBinding( "date" ); + HibernateTypeDescriptor descriptor = attributeBinding.getHibernateTypeDescriptor(); + assertEquals( "timestamp", descriptor.getExplicitTypeName() ); + assertEquals( Date.class.getName(), descriptor.getJavaTypeName() ); + assertNotNull( descriptor.getResolvedTypeMapping() ); + assertEquals( TimestampType.class, descriptor.getResolvedTypeMapping().getClass() ); + assertNotNull( descriptor.getTypeParameters() ); + assertTrue( descriptor.getTypeParameters().isEmpty() ); + } +}