<< Back to previous view

[JPA_SPEC-63] JPA next should support Java 8 Date and Time types Created: 11/Aug/13  Updated: 13/Apr/14

Status: Open
Project: jpa-spec
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Improvement Priority: Major
Reporter: Nick Williams Assignee: Unassigned
Resolution: Unresolved Votes: 12
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Tags: date time date-time jsr-310 java8 temporal
Participants: braghest, mister__m, Nick Williams and reza_rahman

 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.


 Comments   
Comment by Nick Williams [ 11/Aug/13 11:02 PM ]

To be clear, by "JPA.next" I mean JPA 2.2 unless 3.0 is next and there isn't going to be a 2.2. "Whatever is going to be in Java EE 8."

Comment by Nick Williams [ 11/Aug/13 11:50 PM ]

A few additional notes:

  • The reason for specifying the JDBC < 4.2 vs JDBC ≥ 4.2 behavior is that, even today, some JDBC driver vendors have still not fully implemented JDBC 4.0 (Java 6), let alone JDBC 4.1 (Java 7). Unfortunately and terribly, it could be 5 or even 10 years before all driver vendors have JDBC 4.2 drivers. Therefore, JPA vendors should support both mechanisms (since the JDBC 4.2 mechanisms allow saving with timezones, which JDBC 4.1 does not).
  • It is an error if the @TemporalIncludeTimeZone or @TemporalTimeZoneColumn annotations are present on properties of any type other than Calendar, OffsetTime, OffsetDateTime, and ZonedDateTime.
  • A Calendar, OffsetTime, OffsetDateTime, or ZonedDateTime must be converted from its time zone/offset to the database server's time zone/offset on persistence if and only if neither @TemporalIncludeTimeZone nor @TemporalTimeZoneColumn are present on the property. If either of those are present, time zone/offset conversion is not necessary because the time zone/offset will be saved with the time (either using setObject or a second column). Upon retrieval from the database, neither the date/time value nor the time zone/offset value should ever be altered. It should be accepted as it comes back from the database, whether stored together in the same column or separately in two columns.
Comment by Nick Williams [ 12/Aug/13 05:29 AM ]

Another note:

  • In addition to the int, Integer, short, Short, long, Long, and Timestamp types currently supported for @javax.persistence.Version-annotated properties, properties of type Instant may also be @Version-annotated.
Comment by mister__m [ 20/Feb/14 11:22 PM ]

YearMonth and MonthDay columns can also be mapped to INTEGER columns.

Comment by reza_rahman [ 25/Mar/14 07:00 PM ]

I must say this is an excellent initial analysis. For folks interested, the official Java Tutorial now has a nice trail on the Java SE 8 Date/Time API: http://docs.oracle.com/javase/tutorial/datetime/index.html. Details on JDBC 4.2 here: http://docs.oracle.com/javase/8/docs/technotes/guides/jdbc/jdbc_42.html.

Comment by braghest [ 13/Apr/14 08:39 PM ]

Don't do ISO-8601 formatted CHAR/VARCHAR-type fields violate 1st normal form?





Generated at Sat Apr 19 06:13:16 UTC 2014 using JIRA 4.0.2#472.