jpa-spec
  1. jpa-spec
  2. JPA_SPEC-63

JPA next should support Java 8 Date and Time types

    Details

    • Type: Improvement Improvement
    • Status: Open
    • Priority: Major Major
    • Resolution: Unresolved
    • Labels:
      None

      Description

      Currently, JPA temporal fields are supported for the following data types: java.util.Date, java.util.Calendar, java.sql.Date, java.sql.Time, and java.sql.Timestamp. java.sql.Date properties are always mapped to the JDBC methods getDate and setDate, and it is an error to specify the @javax.persistence.Temporal annotation for these types. The same is true of Time (mapped to getTIme and setTime) and Timestamp (mapped to getTimestamp and setTimestamp). Properties of type java.util.Date and Calendar must be annotated with @Temporal to specify the javax.persistence.TemporalType enum indicating which JDBC methods should be used for those properties.

      Some vendors support other temporal types, such as Joda Time, but this is non-standard and should probably remain so since Joda Time isn't guaranteed to stay around (and, in fact, is likely to start ramping down with the release of Java 8).

      JSR-310 as part of Java 8 specifies a new Date & Time API in the java.time package and sub-packages that supplants java.util.Date, Calendar, java.sql.Date, Time, Timestamp, and Joda Time. It is based off of the Joda Time API, but with enhancements and certain redesigns that the Joda Time founder/creator has said makes it superior to Joda Time.

      JPA's existing rules for the currently-supported temporal types should remain largely unchanged. However, the specification should be added to in order to specify support for JSR-310. These are the proposed new rules I believe should be present in the JPA.next specification:

      • Properties of type java.time.Duration are treated as @javax.persistence.Basic fields. They automatically map to;
        • DURATION fields if the database vendor supports duration types;
        • DECIMAL-type fields storing the seconds before the decimal point and the nanoseconds after the decimal point;
        • INTEGER-type fields storing the seconds; and,
        • CHAR/VARCHAR-type fields storing the value in its ISO-8601 format (Duration#toString() and Duration#parse(CharSequence)).
      • Properties of type java.time.Period are treated as @Basic fields. They automatically map to:
        • PERIOD or DURATION fields if the database vendor supports period or duration types;
        • DECIMAL-type fields storing the seconds before the decimal point and the nanoseconds after the decimal point;
        • INTEGER-type fields storing the seconds; and,
        • CHAR/VARCHAR-type fields storing the value in its ISO-8601 format (Period#toString() and Period#parse(CharSequence)).
      • Properties of type java.time.Year are treated as @Basic fields. They automatically map to:
        • YEAR fields if the database vendor supports year types; and,
        • INTEGER/CHAR/VARCHAR-type fields storing the literal number/string value.
      • Properties of enum type java.time.Month are treated as special-case enum fields.
        • If the database field is a MONTH field (assuming the database vendor supports such types), it maps to this field.
        • If @javax.persistence.Enumerated is not present and the database field is an INTEGER-type field, it maps as the month number (NOT the ordinal) using int Month#getValue() and Month Month#of(int).
        • Otherwise, it falls back to standard enum mapping rules.
        • It is an error to annotate a Month property with @Enumerated if the database field is of type MONTH.
      • Properties of enum type java.time.DayOfWeek are treated as special-case enum fields.
        • If the database field is a DAY_OF_WEEK field (assuming the database vendor supports such types), it maps to this field.
        • If @Enumerated is not present and the database field is an INTEGER-type field, it maps as the day number (NOT the ordinal) using int DayOfWeek#getValue() and DayOfWeek DayOfWeek#of(int).
        • Otherwise, it falls back to standard enum mapping rules.
        • It is an error to annotate a DayOfWeek property with @Enumerated if the database field is of type DAY_OF_WEEK.
      • Properties of type java.time.YearMonth are treated as @Basic fields.
        • By default, they automatically map to:
          • YEARMONTH fields if the database vendor supports year-month types;
          • DATE and DATETIME fields storing the lowest day number that the database vendor supports and zero-time if applicable; and,
          • CHAR/VARCHAR-type fields storing the value in its ISO-8601 format (YearMonth#toString() and YearMonth#parse(CharSequence)).
        • The new @javax.persistence.YearMonthColumns annotation can map a YearMonth property to two database fields. A property annotated with this overrides the default mapping behavior. It is an error to mark properties of any other type with this annotation. The required @javax.persistence.Column-typed year attribute specifies the column that the year is stored in while the required @Column-typed month attribute specifies the column that the month is stored in. The year column follows the same default mapping rules as for Year types and the month column as for the Month enum. It is an error to specify @Column and @YearMonthColumns on the same property.
      • Properties of type java.time.MonthDay are treated as @Basic fields.
        • By default they automatically map to:
          • MONTHDAY fields if the database vendor supports month-day types;
          • DATE and DATETIME fields storing the lowest year number that the database vendor supports and zero-time if applicable; and,
          • CHAR/VARCHAR-type fields storing the value in its ISO-8601 format (MonthDay#toString() and MonthDay#parse(CharSequence).
        • The new @javax.persistence.MonthDayColumns annotation can map a MonthDay property to two database fields. A property annotated with this overrides the default mapping behavior. It is an error to mark properties of any other type with this annotation. The required @Column-typed month attribute specifies the column that the month is stored in while the required @Column-typed day attribute specifies the column that the day is stored in. The month column follows the same default mapping rules as for the Month enum and the day column automatically maps to INTEGER/CHAR/VARCHAR-type fields. It is an error to specify @Column and @MonthDayColumns on the same property.
      • Properties of type java.time.ZoneId are treated as @Basic fields. They automatically map to:
        • TIMEZONE fields if the database vendor supports time zone types (they never map to offset fields); and,
        • CHAR/VARCHAR-type fields storing the value in its ISO-8601 format (ZoneId#toString() and ZoneId#of(String)).
      • Properties of type java.time.ZoneOffset are treated as @Basic fields. They automatically map to:
        • OFFSET fields if the database vendor supports offset types (they never map to time zone fields); and,
        • CHAR/VARCHAR-type fields storing the value in its ISO-8601 format (ZoneOffset#toString() and ZoneOffset#of(String)).
      • Properties of types java.time.Instant, java.time.LocalDate, java.time.LocalTime, java.time.LocalDateTime, java.time.OffsetTime, java.time.OffsetDateTime, and java.time.ZonedDateTime are treated as temporal @Basic types that are mapped using the following rules:
        • LocalDate always maps as a date-only value. It is an error to mark a LocalDate property with the @Temporal annotation.
        • LocalTime and OffsetTime always map as time-only values. It is an error to mark a LocalTime or OffsetTime property with the @Temporal annotation.
        • Instant, LocalDateTime, OffsetDateTime, and ZonedDateTime map as timestamp values by default. You may mark a property of one of these types with @Temporal to specify a different strategy for persisting that property.
        • The new @javax.persistence.TemporalIncludeTimeZone annotation indicates that the offset in the OffsetTime or OffsetDateTime property or the time zone in the ZonedDateTime or Calendar property will be persisted with the value. Otherwise (if this is absent) the value is converted to the database server offset or time zone for persistence.
        • The new @javax.persistence.TemporalTimeZoneColumn(@Column value) annotation indicates a different column in which the time zone value is stored. It implies @TemporalIncludeTimeZone. It is required if @TemporalIncludeTimeZone is present but the database vendor does not support storing the time zone with the field data type. It is also required if @TemporalIncludeTimeZone is present but the JDBC driver in use is less than version 4.2 (a JDBC 4.2 driver is necessary to persist time zones and offsets with time/date-time values). The persistence rules for this column are the same as for ZoneId and ZoneOffset properties.
        • Properties of these types invoke the following special handling for JDBC driver versions before and after 4.2.
          • A JDBC driver is considered version 4.2 or better if java.sql.Driver#getMajorVersion() returns a number greater than 4, or it returns 4 and Driver#getMinorVersion() returns a number greater than 1. In the absence of a testable Driver instance, implementations may assume that the driver version is less than 4.2 if PreparedStatement#setObject(int, Object, SQLType) throws a SQLFeatureNotSupportedException.
          • If the JDBC driver is version 4.2 or newer, these seven types are persisted and retrieved as follows:
            • They are persisted with PreparedStatement#setObject(int, Object, SQLType) and retrieved with ResultSet#getObject(int, Class<?>) or ResultSet#getObject(String, Class<?>).
            • Time-only properties or TemporalType.TIME properties use a java.sql.SQLType of java.sql.JDBCType.TIME in the absence of @TemporalIncludeTimeZone or presence of @TemporalTimeZoneColumn. They use JDBCType.TIME_WITH_TIMEZONE in the presence of @TemporalIncludeTimeZone and absence of @TemporalTimeZoneColumn.
            • Date-only properties or TemporalType.DATE properties use a SQLType of JDBCType.DATE.
            • Date-and-time properties use a SQLType of JDBCType.TIMESTAMP in the absence of @TemporalIncludeTimeZone or presence of @TemporalTimeZoneColumn. They use JDBCType.TIMESTAMP_WITH_TIMEZONE in the presence of @TemporalIncludeTimeZone and absence of @TemporalTimeZoneColumn.
          • If the JDBC driver is version 4.1 or older, these seven types are persisted and retrieved as follows:
            • Time-only properties or TemporalType.TIME properties are automatically converted to and from Time and use the traditional setTime and getTime methods.
            • Date-only properties or TemporalType.DATE properties are automatically converted to and from java.sql.Date and use the traditional setDate and getDate methods.
            • Date-and-time properties are automatically converted to and from Timestamp and use the traditional setTimestamp and getTimestamp methods.
            • @TemporalTimeZoneColumn is required if @TemporalIncludeTimeZone is present.

        Activity

        Hide
        perceptron8 added a comment -

        Properties of type java.time.MonthDay are treated as @Basic fields.
        By default they automatically map to:

        • ...
        • DATE and DATETIME fields storing the lowest year number that the database vendor supports and zero-time if applicable; and,
        • ...

        This year must be also a leap year. Sadly, Date.valueOf(MonthDay.of(1, 1).atYear(0)) becomes "0001-01-01", so it can't be 0.

        Show
        perceptron8 added a comment - Properties of type java.time.MonthDay are treated as @Basic fields. By default they automatically map to: ... DATE and DATETIME fields storing the lowest year number that the database vendor supports and zero-time if applicable; and, ... This year must be also a leap year. Sadly, Date.valueOf(MonthDay.of(1, 1).atYear(0)) becomes "0001-01-01", so it can't be 0.
        Hide
        mkarg added a comment -

        While I indeed support this feature request due to its common purpose, there actually is no real need for it anymore, thanks to the acceptance of the adapter API proposal filed by me in https://java.net/jira/browse/JPA_SPEC-35: You can just write a simple adapter that does the type conversion at runtime. Or are you aiming on schema creation instead of just type conversion?

        Show
        mkarg added a comment - While I indeed support this feature request due to its common purpose, there actually is no real need for it anymore, thanks to the acceptance of the adapter API proposal filed by me in https://java.net/jira/browse/JPA_SPEC-35: You can just write a simple adapter that does the type conversion at runtime. Or are you aiming on schema creation instead of just type conversion?
        Hide
        ymajoros added a comment -

        Yeah, but I think it would be a good idea to mention out-of-the box support in the spec, that providers can implement this way if they want. Otherwise, we'll end up basically having to package boiler-plate code every time we use java.time.* classes for JPA support.

        Show
        ymajoros added a comment - Yeah, but I think it would be a good idea to mention out-of-the box support in the spec, that providers can implement this way if they want. Otherwise, we'll end up basically having to package boiler-plate code every time we use java.time.* classes for JPA support.
        Hide
        mkarg added a comment -

        While that is absolutely correct, the technical answer is a bit more complex: What is the final predicate that makes a data type eligible for inclusion in the set of mandatory type mappings?

        One could say, that predicate is "being essential" or "being of common use", but who defines what "essential" or "common use" is? See, for some applications, support for java.awt.Image and java.net.URL might be much more essential than support for LocalDate or ZonedDateTime. On the other hand, other applications might be full of LocalDate but never uses Instant. So where exactly to make the cut? This becomes particularly complex when looking at the sheer amount of types found in the JRE, and it is obvious there has to be a cut somewhere. Even JavaFX, which is bundled with the JRE, does not support Instant still in v8, so why should JPA? And looking at the current progress of Project Jigsaw, possibly the qualifying predicate might simply be answered by "all types in a particular jigsaw module"?

        Anyways, it is not up to me to decide. I do support your request, and would love to see support for rather all Java Time API times, particularly for Instant and Duration, and your request has prominent supporters like for example Java Champion Arun Gupa as I learned recently. But I doubt the final answer will be as simple an satisfying as we would love to have it.

        Maybe it would be better to simply set up another JSR, like "Common Data Type Conversions for the Java Platform", which provides much more mappings than just date and time, but also would not be bound to JPA but also could be used by JAXB, JAX-RS, and possibly more API that deal which the problem of transforming "<A> to <B>"? Having such a vehicle would really reduce boilerplate a lot.

        Show
        mkarg added a comment - While that is absolutely correct, the technical answer is a bit more complex: What is the final predicate that makes a data type eligible for inclusion in the set of mandatory type mappings? One could say, that predicate is "being essential" or "being of common use", but who defines what "essential" or "common use" is? See, for some applications, support for java.awt.Image and java.net.URL might be much more essential than support for LocalDate or ZonedDateTime. On the other hand, other applications might be full of LocalDate but never uses Instant. So where exactly to make the cut? This becomes particularly complex when looking at the sheer amount of types found in the JRE, and it is obvious there has to be a cut somewhere. Even JavaFX, which is bundled with the JRE, does not support Instant still in v8, so why should JPA? And looking at the current progress of Project Jigsaw, possibly the qualifying predicate might simply be answered by "all types in a particular jigsaw module"? Anyways, it is not up to me to decide. I do support your request, and would love to see support for rather all Java Time API times, particularly for Instant and Duration, and your request has prominent supporters like for example Java Champion Arun Gupa as I learned recently. But I doubt the final answer will be as simple an satisfying as we would love to have it. Maybe it would be better to simply set up another JSR, like "Common Data Type Conversions for the Java Platform", which provides much more mappings than just date and time, but also would not be bound to JPA but also could be used by JAXB, JAX-RS, and possibly more API that deal which the problem of transforming "<A> to <B>"? Having such a vehicle would really reduce boilerplate a lot.
        Hide
        braghest added a comment -

        there actually is no real need for it anymore, thanks to the acceptance of the adapter API proposal

        I'm not sure. Firstly currently the RI explodes with a ClassCastException when trying to write an attribute converter mapping a java.util.Calendar database value. Secondly the spec would have to say that when mapping a java.util.Calendar the time zone of the value returned from the database is the time zone of the value on the database instead of the time zone of the Java virtual machine (like java.sql.Date). JDBC only allows to access the time zone of a value using the Java 8 Date and Time API.
        If you currently want to access the timezone of a database value you need to use vendor specific extensions.

        Show
        braghest added a comment - there actually is no real need for it anymore, thanks to the acceptance of the adapter API proposal I'm not sure. Firstly currently the RI explodes with a ClassCastException when trying to write an attribute converter mapping a java.util.Calendar database value. Secondly the spec would have to say that when mapping a java.util.Calendar the time zone of the value returned from the database is the time zone of the value on the database instead of the time zone of the Java virtual machine (like java.sql.Date ). JDBC only allows to access the time zone of a value using the Java 8 Date and Time API. If you currently want to access the timezone of a database value you need to use vendor specific extensions.

          People

          • Assignee:
            Unassigned
            Reporter:
            Nick Williams
          • Votes:
            37 Vote for this issue
            Watchers:
            28 Start watching this issue

            Dates

            • Created:
              Updated: