2

人々の電話番号と住所をデータベース テーブルに保存しようとしています。複数の電話番号と住所をサポートしたいのですが、国によって形式が異なることを期待しています。その柔軟性を可能にし、特定のフィールドによる効率的なクエリを可能にするために、hstore を使用することにしました。現状では、データベースから値を受け取ることができますが、Java から値を挿入する方法が見つかりませんでした。表(簡略化)は次のようになります。

CREATE TABLE IF NOT EXISTS contacts ( "
        + "id uuid NOT NULL, "
        + "title character varying NOT NULL DEFAULT '', "
        + "first_name character varying NOT NULL, "
        + "last_name character varying NOT NULL, "
        + "phones hstore[] NOT NULL DEFAULT '{}', "
        + "addresses hstore[]  NOT NULL DEFAULT '{}')"

値をバインドするカスタム JDBI Binder を作成しましたが、実行しようとしてもステートメントを取得できません。現在、Binder コード スニペットは次のようになっています。

@Override
public void bind(SQLStatement<?> q, BindContactBean bind, ContactBean bean) {
        q.bind("phones",
                getHstoreArray(q, PhoneDetailMapper.toMapArray(bean.phones.get())));
        q.bind("addresses",
                getHstoreArray(q, AddressDetailMapper.toMapArray(bean.addresses.get())));

getHstoreArray 関数は、Java 配列を SQL 配列に変換するヘルパーで、次のようになります。

private Array getHstoreArray(SQLStatement<?> q, Map<String, String>[] map) {
    try {
        return q.getContext().getConnection().createArrayOf("hstore", map);
    } catch (SQLException e) {
        throw new IllegalArgumentException(e);
    }
}

問題はデータのエンコードにあると思います。たとえば、データの場合 (簡単にするために JSON 表記で)

{
    "firstName": "Maximum",
    "lastName": "Details",
    "status": "active",        
    "phones": [{
        "type": "mobile",
        "number": "0777 66 55 44"
    }]
}

クエリは次のように展開されます。

INSERT INTO contacts ( 
id, first_name, last_name, status, phones ) 
VALUES ( '9be1a040-b408-11e3-bb43-00231832fa86',  'Maximum', 'Details', 4, 
'{"{type=mobile, extracted_number=extracted, number=07777 66 55 44}"}'
)

PGAdmin の SQL エディターから実行しようとすると、返されるエラーは次のとおりです。

ERROR:  Syntax error near 'm' at position 6
LINE 5: '{"{type=mobile, extracted_number=extracted, number=07777 66...
        ^
********** Error **********

ERROR: Syntax error near 'm' at position 6
SQL state: XX000
Character: 179

hstore[] の代わりに JSON を使用することを検討しましたが、特定のフィールドによるクエリが遅くなり、精度が低下するため (基本的にテキスト検索)、避けたいと思います。hstore の前に試した別のオプションは UDT の配列ですが、単純なタスクのようには見えない PGobject のパーサーを作成しないと、データベースから読み取ることさえできませんでした。


編集

データベース内のデータを見て、次の方法でエスケープしたとき:

'{"\"type\"=>\"mobile\", \"number\"=>\"07777 66 55 44\", \"extracted_number\"=>\"777665544\""}'

SQL エディターから手動でクエリを実行できますが、Java ではまだうまくいきません。

4

1 に答える 1

1

私は解決策を見つけました.Mapを文字列リテラルに変換できるHStoreConverterと呼ばれるPostgresドライバーで利用可能なクラスがあります。これが最善のアプローチかどうかはわかりませんが、うまくいくようです。以下のヘルパー関数を変更しました。

        private Array getHstoreArray(SQLStatement<?> q, Map<String, String>[] maps) {
            try {
                String[] hstores = new String[maps.length];
                for (int i = 0; i < maps.length; i++)
                    hstores[i] = HStoreConverter.toString(maps[i]);
                return q.getContext().getConnection().createArrayOf("hstore", hstores);
            } catch (SQLException e) {
                throw new IllegalArgumentException(e);
            }
        }
于 2014-08-28T08:23:28.077 に答える