MyBatisでjava8のjava.time.LocalDateTimeを扱う


#<i class="fa fa-comment-o" style="font-size:1em;"></i> 概要
業務でMyBatisを使っているのですがjavaのバージョンを8に上げるにあたり、折角Date and Time API (JSR 310) で日付関連が便利になったと言われているので、DTOのフィールドをLocalDateTimeにしようとして調べた結果です。

[MyBatisの公式ドキュメント](http://mybatis.github.io/mybatis-3/configuration.html#typeHandlers)に書いてあったので簡単にできました。

## <i class="fa fa-cube" style="font-size:1em;"></i> table (mysql)
```sql
CREATE TABLE `sample` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `created` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
```

## <i class="fa fa-cube" style="font-size:1em;"></i> 結果を格納するDTO
```java:sample.domain.SampleDto.java
import java.time.LocalDateTime;
import lombok.Data;

@Data
public class SampleDto {
    private int id;
    private LocalDateTime created;
}
```

この`created`をSQLの結果とマッピングしたい。

##<i class="fa fa-cube" style="font-size:1em;"></i> TypeHandler
どうやら`org.apache.ibatis.type.TypeHandler`か`org.apache.ibatis.type.BaseTypeHandler`を実装してjdbcのクラスとマッピングすると良いよって事らしい。
今回はBaseTypeHandlerを実装してみました。

```java:sample.mybatis.handlers.LocalDateTimeTypeHandler.java
@MappedTypes(LocalDateTime.class)
public class LocalDateTimeTypeHandler
        extends BaseTypeHandler<LocalDateTime> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, LocalDateTime parameter, JdbcType jdbcType) throws SQLException {
        ps.setTimestamp(i, Timestamp.valueOf(parameter));
    }

    @Override
    public LocalDateTime getNullableResult(ResultSet rs, String columnName) throws SQLException {
        Timestamp timestamp = rs.getTimestamp(columnName);
        if (timestamp == null) {
            return null;
        } else {
            return timestamp.toLocalDateTime();
        }
    }

    @Override
    public LocalDateTime getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        Timestamp timestamp = rs.getTimestamp(columnIndex);
        if (timestamp == null) {
            return null;
        } else {
            return timestamp.toLocalDateTime();
        }
    }

    @Override
    public LocalDateTime getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        Timestamp timestamp = cs.getTimestamp(columnIndex);
        if (timestamp == null) {
            return null;
        } else {
            return timestamp.toLocalDateTime();
        }
    }
}
```

##<i class="fa fa-cube" style="font-size:1em;"></i> mybatis-config.xml

mybatis-config.xmlに作成したTypeHandlerを探すように設定を追加します。

```xml:mybatis-config.xml
<configuration>
  <typeHandlers>
    <typeHandler handler="sample.mybatis.handlers.LocalDateTimeTypeHandler"/>
  </typeHandlers>
</configuration>
```

または

```xml:mybatis-config.xml
<configuration>
  <typeHandlers>
    <package name="sample.mybatis.handlers"/>
  </typeHandlers>
</configuration>
```

同一パッケージにまとめて実装してpackage指定する方が楽かなぁ。

## <i class="fa fa-cube" style="font-size:1em;"></i> Mapper
```java
public interface SampleMapper {

    @Select("SELECT * FROM sample)
    List<SampleDto> selectAll();

    @Insert("insert into sample (created) values ( #{created} )")
    @SelectKey(before = false, keyProperty = "id", resultType = int.class, statement = { "select last_insert_id()" })
    int insert(SampleDto sampleDto);
}
```

あとは普通にMapper使うだけで検索・登録ともにDBとマッピングされます。