`

Using the Java 8 DateTime Classes with JPA!

    博客分类:
  • JPA
 
阅读更多

Using the Java 8 DateTime Classes with JPA!

原文:https://weblogs.java.net/blog/montanajava/archive/2014/06/17/using-java-8-datetime-classes-jpa?utm_source=tuicool

 

With the Java 8 SE release, developers get a splendid new best-in-class Date-Time API. Wouldn't it be nice if you could use it with JPA? Not so fast. JPA and for that matter JDBC know nothing about the new classes, and if you use them in your entities, JPA will map them to BLOBs in your database by default. This happens in DDL or database creation, in queries, and in inserts and updates. Take a simple entity as an example:

@Entity
publicclassTrip{
    @Idlong id;
    LocalDateTime departure;
}

The persistence of the departure attribute will work perfectly, if you like saving DateTimes as BLOBs that is. A BLOB for a datetime is hardly the mapping anyone would want, adversely affecting your indexing and querying options, not to mention being an inefficient choice for storage. If you are taking advantage of either the database or the DDL file generation features of JPA 2.1, the above Entity will also result in a BLOB departure field being created in the Trip table, directly or indirectly. What most people will want is a sensible mapping to the Date, Time, and Timestamp types of SQL. This can be achieved by creating custom converters under JPA 2.1 that use old-school classes from the java.sql package which in turn are supported by JPA and JDBC. Here is an example for LocalDate:

@Converter(autoApply =true)
publicclassLocalDatePersistenceConverterimplements
    AttributeConverter<java.time.localdate, java.sql.date="">{
    @Override
    public java.sql.Date convertToDatabaseColumn(LocalDate entityValue){
        return java.sql.Date.valueOf(entityValue);
    }

    @Override
    publicLocalDate convertToEntityAttribute(java.sql.Date databaseValue){
        return databaseValue.toLocalDate();
    }
}

Easy as pie. JPA 2.1 gives you a portable way of fixing our not-quite-up-to-snuff JDBC/JPA specification dilemma, simply by providing you with a standard way to create a custom converter that implements javax.persistence.AttributeConverter. This interface provides two methods that define the mappings: The first one is from a JDBC supported class when you are reading from the database convertToEntityAttribute; the other one is used to map to a JDBC supported class when you are persisting tothe database convertToDatabaseColumn. The usual suspects for such mappings will be java.sql.Datejava.sql.Time, and java.sql.Timestamp and even the old stalwartjava.lang.String. In the case of LocalDateTime, the converter looks like this:

@Converter(autoApply =true)
publicclassLocalDateTimePersistenceConverterimplements
    AttributeConverter<localdatetime, timestamp="">{
    @Override
    public java.sql.Timestamp convertToDatabaseColumn(LocalDateTime entityValue){
        returnTimestamp.valueOf(entityValue);
    }

    @Override
    publicLocalDateTime convertToEntityAttribute(java.sql.Timestamp databaseValue){
        return databaseValue.toLocalDateTime();
    }
}

By mapping LocalDateTime to java.sql.Timestamp, we get an automatic mapping to the SQL type we wish to have. Notice the @Converter annotation which has anautoapply parameter, here set to true. That means that you can now use LocalDateTime in any of your entities without requiring further annotation. If this is not what you want, set it to false, and then in your entity annotate your attribute like this:

@Convert(converter =LocalTimePersistenceConverter.class)
LocalTime departure;

JDBC has no concept of persisting timezone information with dates and timestamps. For the LocalDate and LocalDateTime classes, that is a good semantic fit. If you do wish to store java.time classes that include timezone information, mapping these to a String will provide for an easy, portable solution. See the following example forOffsetDateTime:

@Converter(autoApply =true)
publicclassOffsetDateTimePersistenceConverterimplements
    AttributeConverter<offsetdatetime, string="">{

    /**
    * @return a value as a String such as 2014-12-03T10:15:30+01:00
    * @see OffsetDateTime#toString()
    */
    @Override
    publicString convertToDatabaseColumn(OffsetDateTime entityValue){
        returnObjects.toString(entityValue,null);
    }

    @Override
    publicOffsetDateTime convertToEntityAttribute(String databaseValue){
        returnOffsetDateTime.parse(databaseValue);
    }
}

It keeps getting better! Java 8 now has extremely handy Period and Duration classes (anyone out there ever have to deal with schedules or timetables?), as well as sundry other classes, for which you may find the need persist instances. These too can be easily persisted as Strings with simple converters. Here is a converter for Period using existing JDBC String mapping to do the dirty work:

@Converter(autoApply =true)
publicclassPeriodPersistenceConverterimplementsAttributeConverter<period, string="">{

    /**
    * @return an ISO-8601 representation of this duration.
    * @see Period#toString()
    */
    @Override
    publicString convertToDatabaseColumn(Period entityValue){
        returnObjects.toString(entityValue,null);
    }

    @Override
    publicPeriod convertToEntityAttribute(String databaseValue){
        returnPeriod.parse(databaseValue);
    }
}

The JPA persistence unit needs to know about the converters. This can be done by adding the converters to the persistence.xml. An excerpt from the persistence.xml for the unit tests of the converters looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<persistenceversion="1.0"
            xmlns="http://java.sun.com/xml/ns/persistence">
<persistence-unitname="java8DateTimeTestPersistenceUnit"
                    transaction-type="RESOURCE_LOCAL">
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    <class>org.parola.domain.Book</class>
    <class>org.parola.domain.Appointment</class>
    <class>org.parola.domain.SpaceTravel</class>
    ...
    <class>org.parola.util.date.LocalDatePersistenceConverter</class>
    <class>org.parola.util.date.LocalTimePersistenceConverter</class>
    <class>org.parola.util.date.LocalDateTimePersistenceConverter</class>
    ... 
    <properties>
        <propertyname="eclipselink.logging.level"value="INFO"/>
        <propertyname="eclipselink.target-database"value="DERBY"/>
        <propertyname="javax.persistence.jdbc.driver"
                value="org.apache.derby.jdbc.EmbeddedDriver"/>
        <propertyname="javax.persistence.jdbc.url"
                value="jdbc:derby:memory:java8DateTimeTest;create=true"/>
        <propertyname="javax.persistence.jdbc.user"value=""/>
        <propertyname="javax.persistence.jdbc.password"value=""/>
        <propertyname="javax.persistence.schema-generation.database.action"
                value="drop-and-create"/>
    </properties>
</persistence-unit>
</persistence>

Some databases have custom Java types for storing timezone data along with Date/Time information, DB2 as of vs. 10 and Oracle being two examples that provide that support for zoned timestamps. If you have such a database, you can still make use of a custom JPA 2.1 converter and stay in the world of Java 8 datetime goodness and not have to use non-standard vendor-provided classes in your code.

For other databases, or in the event you do not wish to use the proprietary feature of your database, an attribute of type ZonedDateTime, using the appropriate converter, will be stored in your database in a human-readable form, and will be sortable in accordance with ISO 8601 norms, albeit not amenable to Date/Time math within SQL, although your databases should have built-in from-String converter functions. You can establish constraints in the database if you want further control over your data integrity, of potential importance for databases additionally serving non-Java 8 clients. Database functions come to mind as a solution if you need native types in the SQL world, while retaining VARCHAR storage. Any deeper mappings, such as to multiple columns separating out the timezone information, exceed what is foreseen in the current specification for converters.

Resources

The project itself is located at JPAAttributeConverters on BitBucket with a commercially-friendly license. You will need Maven to build the project. The project has extensive unit tests and to that end includes several highly minimalistic domain classes. The tests use an in-memory Derby database and a JPA 2.1-compliant driver. Be sure to test against your database, easily done by modifying the persistence.xml. You can check out the code using Mercurial: hg clonehttps://montanajava@bitbucket.org/montanajava/jpaattributeconverters

Have fun with the new Java 8 Date and Time classes! We say good riddance bid a fond farewell to the venerable java.util and java.sql Date/Time classes. As soon as you can use Java 8 in your environment, there is no reason to wait for updates to the JPA and JDBC specifications to be able to beneifit from these classes. If support does become available within a future standard, modification to your code should consist of removing some lines from the persistence.xml and deleting some classes.

分享到:
评论

相关推荐

    将c#的DateTime类转成java

    转换为Java时,我们可以使用`java.util.Calendar`类或Java 8引入的`java.time`包。如果要保持与C#类似的API,可以创建一个自定义类,封装这些功能。以下是一个简单的示例,展示了如何实现类似`DateTime`的功能: ``...

    java datetime

    根据提供的文件信息,本文将详细解释Java中日期处理的相关知识点,特别是如何利用`java.util.Date`类以及`java.util.Calendar`类来进行日期操作。 ### Java日期处理基础 在Java中,日期时间处理主要包括两个基本类...

    Java仿.NET 的DateTime封装

    Java或jdbc操作日期时间存在诸多不便,代码通过对Date、Calendar、SimpleDateFormat的封装,实现类似.NET DateTime的功能

    Swifty日期和时间API受Java 8 DateTime API的启发。-Swift开发

    受Java 8 DateTime API启发的AnyDate Swifty日期和时间API。 背景我认为日期和时间API应该简单而准确。 Swift的以前的日期,时间和时区API给Java 8 DateTime API带来了不便,AnyDate Swifty Date&Time API。 背景我...

    java的Date类型转换成MySQL的DateTime类型.docx

    ### Java的Date类型转换成MySQL的DateTime类型 在软件开发特别是网络游戏开发中,记录关键时间点(例如游戏开始时间和结束时间)并将其保存至数据库是非常常见的需求。由于Java中的`java.util.Date`类与MySQL数据库...

    java的Date类型转换成MySQL的DateTime类型.doc

    Java 中 Date 类型转换为 MySQL 的 DateTime 类型 Java 中的 Date 类型和 MySQL 的 DateTime 类型是两个不同的日期时间类型,都是用于表示日期和时间的信息,但它们之间存在一定的区别。Java 中的 Date 类型是使用...

    mysql时间类型对应的java类型1

    - `java.time.*`(Java 8引入的新日期时间API)的类如`LocalDate`、`LocalTime`和`LocalDateTime`也可以转换为SQL类型,通过`PreparedStatement.setObject()`方法。 - **从MySQL到Java**: - 当从数据库查询结果...

    DateTime类java-Eclipse

    在Java编程语言中,`DateTime`类并不是标准库的一部分,但这里提到的可能是`java.time.DateTime`,这可能指的是第三方库如Joda-Time或者Java 8及更高版本中的`java.time`包。Joda-Time是Java日期和时间处理的一个...

    java-datetime-demo

    首先,Java 8之前的日期时间处理主要依赖于`java.util.Date`和`java.text.SimpleDateFormat`这两个类。`Date`类用于表示时间戳,即从1970年1月1日(格林尼治标准时间)开始的毫秒数。然而,`Date`类并不方便直接格式...

    Using_the_Date.rar_c# datetime

    在C#编程中,`DateTime`对象是处理日期和时间数据的核心组件。它提供了丰富的功能,包括创建、比较、格式化以及对日期和时间进行算术运算。本教程将深入探讨`DateTime`类的各个方面,帮助你更好地理解和利用这个强大...

    Android的SQLite中DateTime类型数据的存取问题

    Android 的 SQLite 中 DateTime 类型数据的存取问题 Android 平台中的 SQLite 数据库在存储 DateTime 类型的数据时会遇到一些问题。本文将详细介绍这些问题和解决方案。 日期类型数据的存储问题 -----------------...

    yyyyMMddHHmmss 字符串 转DateTime

    yyyyMMddHHmmss 字符串 转DateTime

    graphql-java-datetime:GraphQL ISO日期是一组与RFC 3339兼容的datetime标量类型,可与graphql-java一起使用

    graphql-java-datetime· GraphQL ISO Date是与一起使用的一组RFC 3339兼容日期/时间标量类型。 概要 一组ISO 33601和RFC 3339兼容的日期时间标量,用于GraphQL Java实现( )和GraphQL Java Kickstart( )。 序列...

    python中时间转换datetime和pd.to_datetime详析

    本文将深入探讨两种常用的时间转换方法:`datetime` 和 `pd.to_datetime`。这两种方法都是为了将不同格式的时间数据转换成标准的日期时间对象,以便进行进一步的操作。 首先,我们来看`datetime` 模块。`datetime` ...

    DateTime操作

    DateTime是C#编程语言中一个重要的日期和时间处理类,它提供了丰富的功能,可以用于处理各种日期和时间相关的操作。以下是对DateTime类的一些关键知识点的详细说明: 1. 获取当前日期和时间: `DateTime.Now` 是...

    java获取两个时间的相隔时间,包括年、月、日、时、分、秒

    3. **使用Java 8的日期时间API**:如果你使用的是Java 8或更高版本,可以利用 `java.time` API 来进行更直观的操作。例如,创建两个 `LocalDateTime` 对象,然后使用 `ChronoUnit` 来计算差值: ```java ...

    java与mysql日期类型的问题

    通过java向mysql中插入datetime类型的数据: String sql = "INSERT INTO wp_posts ( post_date )VALUES(?)"; PreparedStatement pstmt = connection.prepareStatement(sql); Timestamp time = new Timestamp...

    C# DateTime 日期操作

    在C#编程中,`DateTime`结构用于表示特定的日期和时间。它是.NET框架中处理日期和时间操作的核心类型。本篇文章将详细讲解如何在C#中进行`DateTime`的日期操作,包括加减日期、获取本周、上周、下周及本月的日期。 ...

Global site tag (gtag.js) - Google Analytics