Help us understand the problem. What is going on with this article?

Spring-Boot でJava8日時を使うためには

More than 3 years have passed since last update.

この記事はリクルートライフスタイル Advent Calendar 2015 - Qiita の23日目です。

こんにちは。現在、JavaでサーバサイドのAPIを作っている関根です。
Javaといえば最近はSpring Bootを使うのがトレンドですね。

始めに

Spring Boot はJava8 に対応しているのですが、
実は、Java8日時API(LocalDateTime, LocalDate, LocalTime)を使用して、
システムから入出力する際にはいくつか設定しなければならないんですよ。

この記事では、Spring BootでJava8 から追加された日時APIを使用する方法についてまとめたいと思います。

対象

今回、使用したフレームワークは以下です。

  • Spring Boot
  • Template Engines:Thymeleaf
  • O/Rマッパー:Mybatis

Hibernate, Velocityとかは今回対象でないですので、ご注意を。

環境

試した環境は以下の通りです。

  • Java 1.8.0_51
  • Spring Boot 1.3.0.RELEASE
  • MyBatis 3.3.0
  • mybatis-spring 1.2.3
  • Maven 3.0.5

Jackson

@RequestBody, @ResponseBody 等のJSON変換に Jackson を使う場合は、以下の設定が必要です。
1. pom.xml にjackson-datatype-jsr310 を追加。

pom.xml
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jsr310</artifactId>
            <version>2.6.2</version>
        </dependency>

2.Configuration にObjectMapper を追加。

JacksonConfiguration
@Configuration
public class JacksonConfiguration {
  @Bean
  @Primary
  public ObjectMapper objectMapper() {
    ObjectMapper objectMapper = new ObjectMapper();

    // JSR310 Date and Time API 変換対応
    objectMapper.registerModule(new JavaTimeModule());
    return objectMapper;
  }
}

書式指定する場合は、@JsonFormat をつければOK。

    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate date;

@RequestParam@PathValiable

リクエストパラメータを受け取る場合には、とくに特別なことはしなくて構いません。
java.util.Dateのときと同じく、@DateTimeFormat をつけることで受け取ることができます。

DemoRestController
  @RequestMapping(value = "/path/{pathDate}", method = RequestMethod.GET)
  public String path(
      @DateTimeFormat(pattern = "yyyyMMdd") @PathVariable LocalDate pathDate,
      @DateTimeFormat(pattern = "yyyyMMdd") @RequestParam LocalDate requestDate) {

    return "pathDate:" + pathDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))
        + " requestDate:" + requestDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
  }

MyBatis

MybatisでJava8 日時APIを使用するために、BaseTypeHandler を継承したクラスを作らなければなりません。
ここでLocalDateを扱うTypeHandlerクラスを見てみましょう。

1.BaseTypeHandler を継承したクラスを作成する。
ジェネリックはjava.time.LocalDate にする。

LocalDateTypeHandler
package com.example.typeHandler;

import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedTypes;

import java.sql.CallableStatement;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDate;

/**
 * MyBatisで{@link LocalDate}を扱うためのハンドラー.
 */
@MappedTypes(LocalDate.class)
public class LocalDateTypeHandler
    extends BaseTypeHandler<java.time.LocalDate> {

  @Override
  public void setNonNullParameter(PreparedStatement ps, int i, java.time.LocalDate parameter,
      JdbcType jdbcType) throws SQLException {
    ps.setDate(i, Date.valueOf(parameter));
  }

  @Override
  public java.time.LocalDate getNullableResult(ResultSet rs, String columnName)
      throws SQLException {
    Date date = rs.getDate(columnName);
    if (date == null) {
      return null;
    } else {
      return date.toLocalDate();
    }
  }

  @Override
  public java.time.LocalDate getNullableResult(ResultSet rs, int columnIndex)
      throws SQLException {
    Date date = rs.getDate(columnIndex);
    if (date == null) {
      return null;
    } else {
      return date.toLocalDate();
    }
  }

  @Override
  public java.time.LocalDate getNullableResult(CallableStatement cs, int columnIndex)
      throws SQLException {
    Date date = cs.getDate(columnIndex);
    if (date == null) {
      return null;
    } else {
      return date.toLocalDate();
    }
  }
}

2.Mybatis のconfig.xml に追加します。

config.xml
<configuration>
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <setting name="useColumnLabel" value="true"/>
    </settings>
    <!-- typeHandler を追加 -->
    <typeHandlers>
        <typeHandler handler="com.example.typeHandler.LocalDateTypeHandler"/>
    </typeHandlers>
</configuration>

同様にLocalDateTimeLocalTimeのTypeHandler を作成し、登録する必要がありますが、ここでは割愛します。

Thymeleaf

Thymeleaf については、thymeleaf-extras-java8time の追加が必要になります。
#dates はJava8 日時APIには対応していないので、#temporals を使用します。

1.pom.xml にthymeleaf-extras-java8time を追加。

pom.xml
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-java8time</artifactId>
            <version>2.1.0.RELEASE</version>
        </dependency>

2.Java8TimeDialect クラスをBean登録する。

ThymeleafConfiguration
  @Bean
  public Java8TimeDialect java8TimeDialect() {
    return new Java8TimeDialect();

テンプレートでは#temporals を使用します。

            <label name="testDate" th:text="${#temporals.format(testDate, 'yyyy-MM-dd')}" />

まとめ

どうでしたか?
いろいろ設定が必要なのが現状です。
新しくJava8 日時API が追加されましたが、まだまだ使い勝手がよろしくないかな、といった印象を私は感じました。

早く特別な設定なしでJava8 日時API が使用できるようになるといいですね!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした