Sql2oでJava8のDate and Time APIを使う
こちらのスライドでSql2oというライブラリを知りました。
Java が支える 人気ニュースアプリ NewsPicks の裏側 // Speaker Deck
素のJDBCは面倒だけど機能豊富なDB関連のライブラリ使う必要はない、ってときにすごく便利そうなので使ってみました。
Sql2oで、Java8で導入されたZonedDateTimeクラスなどをどうやれば使えるか、というお話です。
試したSql2oのバージョンは、1.5.4です。
Sql2o - Easy database query library - sql2o
まず、こんな感じのUserクラスがあったとします。
import java.util.Date public class User { private int id; private String name; private Date joinedAt; public String toString() { return "User{id=" + id + ", name=" + name + ", joinedAt=" + joinedAt + "}"; } }
SELECTの結果を、Userオブジェクトにマッピングしたい場合は以下のように書きます。シンプルですね!
Sql2o sql2o = new Sql2o(DB_URL, USER, PASSWORD); String sql = "SELECT id, name, joined_at FROM users WHERE id = :id"; try (Connection conn = sql2o.open()) { User user = conn.createQuery(sql) .addParameter("id", userId) .addColumnMapping("joined_at", "joinedAt") .executeAndFetchFirst(User.class); }
ソースコード読んでたらJoda Timeにも対応してそうだったので試してみたら動きました。UserクラスのjoinedAtの型をJoda TimeのDateTimeに変えても動きます。
import org.joda.time.DateTime; public class User { private int id; private String name; private DateTime joinedAt; public String toString() { return "User{id=" + id + ", name=" + name + ", joinedAt=" + joinedAt + "}"; } }
さて、Java8のZonedDataTimeを使うときはどうしたらいいでしょうか。Joda TimeのDateTimeに対応するのにConverterという仕組みを使っていたので、これを利用すればいけそうです。
Converterインタフェースを実装したクラスを用意します。こいつがZonedDateTimeへの変換を担当します。
import org.sql2o.converters.Converter; import org.sql2o.converters.ConverterException; import java.sql.Timestamp; import java.time.ZoneId; import java.time.ZonedDateTime; private static class ZonedDateTimeConverter implements Converter<ZonedDateTime> { private final ZoneId zoneId; public ZonedDateTimeConverter(ZoneId zoneId) { this.zoneId = zoneId; } @Override public ZonedDateTime convert(Object val) throws ConverterException { if (val == null) return null; if (val instanceof Timestamp) { return ZonedDateTime.ofInstant(((Timestamp) val).toInstant(), zoneId); } else { throw new IllegalArgumentException(); } } @Override public Object toDatabaseParam(ZonedDateTime val) { return new Timestamp(val.toInstant().toEpochMilli()); } }
Sql2oオブジェクトを生成するときにConverterを設定しておきます。ハッシュマップのキーは変換先のクラスを指定します。
Map<Class, Converter> converterMap = new HashMap<>(); converterMap.put(ZonedDateTime.class, new ZonedDateTimeConverter(ZoneId.of("Asia/Tokyo"))); Sql2o sql2o = new Sql2o(DB_URL, USER, PASSWORD, new NoQuirks(converterMap));
後はUserクラスで使う日時の型をZonedDateTimeに変更すればOKです!
import java.time.ZonedDateTime; public class User { private int id; private String name; private ZonedDateTime joinedAt; public String toString() { return "User{id=" + id + ", name=" + name + ", joinedAt=" + joinedAt + "}"; } }
同じようにすればInstantクラスやLocalDateクラスに対応することもできそうですね。Optionalにも対応できるかなー。