この記事はリクルートライフスタイル 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 を使う場合は、以下の設定が必要です。
-
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 を追加。
@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
をつけることで受け取ることができます。
@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
にする。
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 に追加します。
<configuration>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="useColumnLabel" value="true"/>
</settings>
<!-- typeHandler を追加 -->
<typeHandlers>
<typeHandler handler="com.example.typeHandler.LocalDateTypeHandler"/>
</typeHandlers>
</configuration>
同様にLocalDateTime
、LocalTime
のTypeHandler を作成し、登録する必要がありますが、ここでは割愛します。
Thymeleaf
Thymeleaf については、thymeleaf-extras-java8time の追加が必要になります。
#dates
はJava8 日時APIには対応していないので、#temporals
を使用します。
1.pom.xml にthymeleaf-extras-java8time を追加。
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
2.Java8TimeDialect
クラスをBean登録する。
@Bean
public Java8TimeDialect java8TimeDialect() {
return new Java8TimeDialect();
テンプレートでは#temporals
を使用します。
<label name="testDate" th:text="${#temporals.format(testDate, 'yyyy-MM-dd')}" />
まとめ
どうでしたか?
いろいろ設定が必要なのが現状です。
新しくJava8 日時API が追加されましたが、まだまだ使い勝手がよろしくないかな、といった印象を私は感じました。
早く特別な設定なしでJava8 日時API が使用できるようになるといいですね!