1. 概要

JPA 2.2バージョンでは、 Java 8 Date and TimeAPIのサポートが正式に導入されました。 その前は、独自のソリューションに依存するか、JPAConverterAPIを使用する必要がありました。

このチュートリアルでは、さまざまなJava8の日付と時刻のタイプをマップする方法を示します。 特に、オフセット情報を考慮したものに焦点を当てます。

2. Mavenの依存関係

開始する前に、JPA2.2APIをプロジェクトのクラスパスに含める必要があります。 Mavenベースのプロジェクトでは、その依存関係をpom.xmlファイルに追加するだけです。

<dependency>
    <groupId>javax.persistence</groupId>
    <artifactId>javax.persistence-api</artifactId>
    <version>2.2</version>
</dependency>

さらに、プロジェクトを実行するには、JPA実装と、使用するデータベースのJDBCドライバーが必要です。 このチュートリアルでは、EclipseLinkとPostgreSQLデータベースを使用します。

<dependency>
    <groupId>org.eclipse.persistence</groupId>
    <artifactId>eclipselink</artifactId>
    <version>2.7.4</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>42.2.5</version>
    <scope>runtime</scope>
    <type>bundle</type>
</dependency>

MavenCentralでJPAAPI EclipseLink 、およびPostgreSQLJDBCドライバーの最新バージョンを確認してください。

もちろん、他のデータベースやHibernateのようなJPA実装を使用することもできます。

3. TimeZoneサポート

任意のデータベースで作業できますが、JDBC 4.2は以下に基づいているため、最初に、これらの標準SQLタイプのサポートを確認する必要があります。

  • タイムスタンプ付きタイムスタンプ(n)
  • タイムスタンプなしのタイムスタンプ(n)
  • タイムゾーン付きのTIME(n)
  • タイムゾーンなしのTIME(n)

ここで、 n は秒の小数部の精度であり、0〜9桁です。  WITHOUT TIME ZONE はオプションであり、省略できます。 WITH TIME ZONE を指定する場合は、タイムゾーン名またはUTCへのオフセットが必要です。

タイムゾーンは、次の2つの形式のいずれかで表すことができます。

  • タイムゾーン名
  • UTCからのオフセットまたはUTCの文字Z

この例では、SQLタイプ TIME WITH TIME ZONE を完全にサポートしているため、PostgreSQLデータベースを選択しました。

他のデータベースはこれらのタイプをサポートしていない可能性があることに注意してください。

4. Java8より前の日付タイプのマッピング

Java 8より前は、通常、汎用SQLタイプ TIME、DATE 、およびTIMESTAMPjava.sql。*クラスのいずれかにマップする必要がありました。 java.sql.Time java.sql.Date、 java.sql.Timestamp、、またはjava.utilタイプ[X272X ]java.util.Dateおよびjava.util.Calendar

まず、 java.sqlタイプの使用方法を見てみましょう。 ここでは、@Entityクラスの一部としてjava.sqlタイプで属性を定義しているだけです。

@Entity
public class JPA22DateTimeEntity {

    private java.sql.Time sqlTime;
    private java.sql.Date sqlDate;
    private java.sql.Timestamp sqlTimestamp;
    
    // ...
}

java.sql タイプは、追加のマッピングなしで他のタイプと同じように機能しますが、 java.util タイプは、対応する時間タイプを指定する必要があります。

これは、 @Temporal アノテーションを介して行われます。このアノテーションでは、 value 属性により、 TemporalType 列挙を使用して、対応するJDBCタイプを指定できます。

@Temporal(TemporalType.TIME)
private java.util.Date utilTime;

@Temporal(TemporalType.DATE)
private java.util.Date utilDate;

@Temporal(TemporalType.TIMESTAMP)
private java.util.Date utilTimestamp;

実装としてHibernateを使用している場合、これはCalendarからTIMEへのマッピングをサポートしていないことに注意してください。

同様に、Calendarクラスを使用できます。

@Temporal(TemporalType.TIME)
private Calendar calendarTime;

@Temporal(TemporalType.DATE)
private Calendar calendarDate;

@Temporal(TemporalType.TIMESTAMP)
private Calendar calendarTimestamp;

これらのタイプはいずれも、タイムゾーンまたはオフセットをサポートしていません。 これらの情報を処理するために、従来はUTC時刻を保存する必要がありました。

5. Java8の日付タイプのマッピング

Java8ではjava.timeパッケージが導入され、JDBC 4.2 APIでは、追加のSQLタイプ TIMESTAMP WITH TIMEZONEおよびTIMEWITH TIMEZONEのサポートが追加されました。

JDBCタイプTIME、DATE、およびTIMESTAMPをjava.timeタイプ LocalTime、 LocalDate 、およびLocalDateTimeにマップできるようになりました。 :

@Column(name = "local_time", columnDefinition = "TIME")
private LocalTime localTime;

@Column(name = "local_date", columnDefinition = "DATE")
private LocalDate localDate;

@Column(name = "local_date_time", columnDefinition = "TIMESTAMP")
private LocalDateTime localDateTime;

さらに、ResetTimeおよびOffsetDateTimeクラスを介してUTCへのオフセットローカルタイムゾーンをサポートしています。

@Column(name = "offset_time", columnDefinition = "TIME WITH TIME ZONE")
private OffsetTime offsetTime;

@Column(name = "offset_date_time", columnDefinition = "TIMESTAMP WITH TIME ZONE")
private OffsetDateTime offsetDateTime;

対応するマップされた列タイプは、 TIME WITH TIMEZONEおよびTIMESTAMPWITH TIMEZONEである必要があります。 残念ながら、すべてのデータベースがこれら2つのタイプをサポートしているわけではありません。

ご覧のとおり、JPAはこれらの5つのクラスを基本タイプとしてサポートしており、日付や時刻の情報を区別するために追加の情報は必要ありません。

エンティティクラスの新しいインスタンスを保存した後、データが正しく挿入されていることを確認できます。

6. 結論

Java8およびJPA2.2より前では、開発者は通常、日付/時刻タイプを永続化する前にUTCに変換する必要がありました。 JPA 2.2は、UTCへのオフセットをサポートし、タイムゾーンのJDBC 4.2サポートを活用することにより、この機能をすぐにサポートするようになりました。

これらのサンプルの完全なソースコードは、Githubにあります。