3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

MyBatis(Mapper XML)でオブジェクトをPostgreSQLにJSONとして格納する方法

Posted at

やりたいこと

Spring + Mybatis(Mapper XMLでSQL文を定義)なときに、ObjectをPostgreSQLにJSONB型として格納したい

やり方

今回は2つ取り上げます。

まず思いつく方法

Java側で一旦Stringにして、格納するSQL文の中でJSONBにキャストします。Object→Stringにはcom.fasterxml.jackson.databind.ObjectMapperを利用します。

SQL呼び出す側
Object data = hoge(); //格納したいデータ
ObjectMapper mapper = new ObjectMapper();
String objectJSON = mapper.writeValueAsString(data)
sqlClient.insert(objectJSON)
MapperXML
<insert id="insert">
    insert into hogetable (data) values (#{objectJSON}::jsonb);
</insert>

ちゃんとStringがJSON形式になっていれば、格納されます。

ちょっと気になること

これで動くといえば動くのですが、できればJava内ではオブジェクトのまま持っておきたいところです。わざわざ呼び出す側が、呼び出される側が使いやすいように整形するのは煩雑です。

そこで、あくまでJava側ではオブジェクトのまま保持しておき、Mapper XMLの中でJSON形式に変換します。 そうすれば先程述べたような問題点は解決されます。

ObjectMapperでの処理をMyBatis側にさせる方法

まず、Mapperのインターフェースを以下のように設計します。ポイントは、Mapper XMLの中でcom.fasterxml.jackson.databind.ObjectMapper.writeValueAsStringを実行させるために、ObjectMapperも引き受けるようにしているところです。

Mapperのインターフェース
insert(@Param("objectMapper") ObjectMapper objectMapper, @Param("object") Object object);

呼び出す側はObjectMapperと格納したいObjectを併せて渡すだけです。

SQL呼び出す側
Object data = hoge(); //格納したいデータ
ObjectMapper mapper = new ObjectMapper();
sqlClient.insert(mapper, data)

また、Mapper XMLではbind式を利用します。

MapperXML
<insert id="insert">
    <bind name="objectJSON" value="objectMapper.writeValueAsString(object)" />
    insert into hogetable (data) values ('${objectJSON}'::jsonb)
</insert>

bind式では、valueに書いた値がnameで指定した変数に代入されます。このとき、valueの中には値そのものだけではなく、式を書くこともできます。

その後のsql文では${変数名}で参照できます。ただし、${変数名}では、単なる置換にすぎないので、シングルクォーテーションで囲んで文字列(PostgreSQLではtext型)にしておく必要があります。また、本来は${}を使うべきではありません。単なる置換のせいでSQLインジェクションに弱くなるからです。

本当はそのあたりを解決してくれる#{}の方を使うべきなのですが、うまく参照できませんでした・・・。

ちょっと気になること

結局の所、Object→StringのマッピングをJava側からMybatis側に移すことがこの記事の目的なので達成はしました。

しかしながら、呼び出す側でmapperを準備しているので、完全に処理を移譲したとは言えないと思います。もしまたMybatis側の処理内容に変更が加わった場合、Java側も変更しないといけません。変更に強いアーキテクチャーを実装しようとすると、外部接続するものに依存したくはありません。

その点では、Mybatis側でmapperを準備できればよいでしょう。呼び出される側でmapperを準備できれば、いよいよ呼び出す側はデータそのものを渡すだけすみます。その方法を発見できれば、またアップデートしようと思います。

3
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?