2016年3月18日金曜日

H2の1.3系では、auto_incrementなカラムを持つテーブルへのバッチinsertが使いものにならない

H2の安定版の1.3系を使うと、バッチinsert後のgetGeneratedKeysが1レコードしか返さないため、データベースで設定された値を取得できない問題がある。

getGeneratedKeysのJavadocにも下の記述があるので、完全に仕様のようです。
Return a result set that contains the last generated auto-increment key
for this connection, if there was one. If no key was generated by the
last modification statement, then an empty result set is returned.
The returned result set only contains the data for the very last row.

試した結果

試したバージョンは、1.3.176となっています。
compile 'com.h2database:h2:1.3.176'

このコードを実行すると、バッチinsertで10レコード登録しているので、it.getInt(1)が10回標準出力に出力されるはずですが、実際には1回しか出力されません。
出力される値は、Javadocにあるように最後に採番された値の「it.getInt(1) = 10」となります。
jdbcDataSource.connection.use {connection ->
  connection.createStatement().use {statement ->
    statement.execute("create table test(id bigint auto_increment, name varchar(255))")
  }

  connection.prepareStatement("insert into test (name) values (?)").use { ps ->
    for (i in 1..10) {
      ps.setString(1, "name_$i")
      ps.addBatch();
    }
    ps.executeBatch()

    ps.generatedKeys.use {
      while (it.next()) {
        println("it.getInt(1) = ${it.getInt(1)}")
      }
    }
  }
}

ちなみに、Beta版の1.4系(現時点での最新の1.4.191)にするとこの問題は解消しています。

2016年3月8日火曜日

[Jackson]Java8のDate and Time APIを使ってみる

JacksonでJava8のDate and Time APIを扱うためには、依存ライブラリにjackson-datatype-jsr310を追加し、
モジュールをObjectMapperに登録する必要があります。

このモジュールを使わなかった場合、Date and Time APIのクラスのプロパティの値が、
Jsonのプロパティとして出力されるので非常に残念な結果となります。

ライブラリの追加

Gradleの場合には、以下のようになります。
compile 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.7.2'

モジュールの追加

ObjectMapperのregisterModuleメソッドを使って、JavaTimeModuleを登録します。
final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());

実行結果

下のコードを使ってLocalDate型の値をシリアライズします。
final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
System.out.println(objectMapper.writeValueAsString(LocalDate.now()));

実行結果は、以下のようになります。年月日が配列の各要素に分解されてシリアライズされます。
[ 2016, 3, 8 ]

シリアライズ時のフォーマットを変更する

シリアライズ時のフォーマットを変更する場合にはJavaTimeModuleを使わずに
jackson-datatype-jsr310に含まれるシリアライザーを直接使います。

[Jackson]特定の型に対してカスタムなシリアライザを設定する
と同じようにSimpleMoculeを使って、任意のシリアライザーを登録します。

今回は、LocalDateを使うのでjackson-datatype-jsr310に含まれるLocalDateSerializerを登録します。
LocalDateSerializerは、シリアライズ時に行うフォーマットを指定することができるのでDateTimeFormatterを使ってフォーマットを指定ます。
final ObjectMapper objectMapper = new ObjectMapper();

SimpleModule module = new SimpleModule();

// formatを指定してLocalDateSerializerを登録する。
module.addSerializer(new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyyMMdd")));
objectMapper.registerModule(module);

System.out.println(objectMapper.writeValueAsString(LocalDate.now()));

実行すると以下のように指定したフォーマットでシリアライズされます。
"20160308"

2016年3月5日土曜日

[Jackson]特定の型に対してカスタムなシリアライザを設定する

特定の型に対して一括でカスタムなシリアライザを適用する方法。
このパターンを使用すると、シリアライズ対象のプロパティ(getter)にアノテーションでシリアライザを指定しなくてよくなります。
特定型に対して一律シリアライザを適用する場合には、もれなく実行できるメリットがあります。

カスタムなシリアライザの作り方はこちら→[Jackson]末尾のスペースを取り除いんてjson変換する

この例では、String型のプロパティに対して一律スペースをトリムするシリアライザが適用されます。
final Sample sample = new Sample();
sample.setName("aa ");
sample.setAge(100);

// 型とシリアライザのマッピングを定義
final SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(String.class, new TrimSpaceSerializer());

final ObjectMapper objectMapper = new ObjectMapper();
// ObjectMapperにシリアライザの定義を持つモジュールを追加
objectMapper.registerModule(simpleModule);
final String result = objectMapper.writeValueAsString(sample);

// カスタムなシリアライザ
private static class TrimSpaceSerializer extends JsonSerializer {
    @Override
    public void serialize(
            String value,
            JsonGenerator gen,
            SerializerProvider serializers) throws IOException, JsonProcessingException {
        gen.writeString(value.trim());
    }
}

実行すると、この結果のようにString型のプロパティに対してトリムが行われます。
{"name":"aa","age":100}

2016年3月4日金曜日

[Jackson]末尾のスペースを取り除いてjson変換する

Jacksonを使ってオブジェクトをシリアライズするときに、文字列内のスペースをトリムする方法です。
なお、使用しているJacksonのバージョンは2.7.2となります。

デフォルトでは、何も行われないので、下の実行結果のように末尾のスペースがそのままシリアライズされます。

デフォルトの場合の動作

Sample sample = new Sample();
sample.setName("aaaa   ");

StringWriter writer = new StringWriter();
objectMapper.writeValue(writer, sample);

{"name":"aaaa   "}

スペースを除去するためには、カスタムのシリアライザを作成し、アノテーションで設定する必要があります。
このページが参考になります。

http://stackoverflow.com/questions/7161638/how-do-i-use-a-custom-serializer-with-jackson


カスタムのシリアライザの実装

JsonSerializerを継承して、serializeメソッドで値を書き換えます。
シリアライズ対象の値(value)をtrimして書き込みます。
private static class TrimSpaceSerializer extends JsonSerializer<String> {

    @Override
    public void serialize(
            String value,
            JsonGenerator gen,
            SerializerProvider serializers) throws IOException, JsonProcessingException {
        gen.writeString(value.trim());
    }
}

Beanにシリアライザを設定する

カスタムシリアライザをJsonSerializeアノテーションのusing属性に設定します。
JsonSerializeは、値を変換書けたいプロパティのgetterに設定します。
private static class Sample {
    private String name;

    @JsonSerialize(using = TrimSpaceSerializer.class)
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

実行結果

スペースがトリムされました。
{"name":"aaaa"}

2016年3月3日木曜日

[AngularJS1.3]One-Time Data Binding(一回だけ値をバインドして、変更は監視しない)

AngularJS1.3から、One-Time Data Bindingなるモードが増えたらしい(知らなかった)。


これは、初回の一度のみデータをバインドして、その後モデルに変更があってもバインド済みの箇所の値は変更されないらしい。
データの変更監視がなくなる分、パフォーマンスが良くなるらしい。

使い方は、下のコードのように::を付加するだけなので簡単です。

この例だと、入力欄の値が変更されても::app.nameの部分は値が変更されません。
あと、ng-repeatの部分もapp.usersに要素が増えたとしても初期表示のまま変更されません。
<p>{{ app.name }}</p>
  <p>{{ ::app.name }}</p>
  <input type="text" ng-model="app.name">

  <div ng-repeat="user in ::app.users">
    <p>{{ user.name }}</p>
  </div>

2016年3月2日水曜日

git commitで特定ファイルのみコミットする

gitでコミットするときにステージング済みのファイルから一部のファイルだけコミットする場合は、
git commit -- file listのように、コミット対象のfile listを指定する。

■コミット前のステータス
$ git status
On branch master

Initial commit

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

        new file:   fuga.txt
        new file:   hoge.txt

■コミット
$ git commit -m 'add' -- hoge.txt
[master (root-commit) fb9d957] add
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 hoge.txt

■コミット後のステータス
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        new file:   fuga.txt

■複数ファイルの場合はスペースで区切る
$ git commit -m 'mod' -- hoge.txt piyo.txt