1. 概要

プロジェクトによっては、JSONオブジェクトをリレーショナルデータベースに永続化する必要がある場合があります。

このチュートリアルでは、 JSONオブジェクトを取得し、それをリレーショナルデータベースに永続化する方法を説明します。

この機能を提供する利用可能なフレームワークはいくつかありますが、HibernateJacksonのみを使用したいくつかの単純で一般的なオプションを見ていきます。

2. 依存関係

このチュートリアルでは、基本的なHibernateCore依存関係を使用します。

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>5.4.0.Final</version>
</dependency>

また、JSONライブラリとしてJacksonを使用します。

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.13.0</version>
</dependency>

これらの手法はこれら2つのライブラリに限定されないことに注意してください。お気に入りのJPAプロバイダーとJSONライブラリを置き換えることができます。

3. メソッドのシリアル化と逆シリアル化

リレーショナルデータベースでJSONオブジェクトを永続化する最も基本的な方法は、永続化する前にオブジェクトを文字列に変換することです。 次に、データベースから取得するときに、それをオブジェクトに変換し直します。

これは、いくつかの異なる方法で行うことができます。

最初に確認するのは、カスタムのシリアル化および逆シリアル化メソッドの使用です。

まず、顧客の姓名とその顧客に関するいくつかの属性を格納する単純なCustomerエンティティから始めます。

標準のJSONオブジェクトは、これらの属性を HashMap として表すため、ここでは次のように使用します。

@Entity
@Table(name = "Customers")
public class Customer {

    @Id
    private int id;

    private String firstName;

    private String lastName;

    private String customerAttributeJSON;

    @Convert(converter = HashMapConverter.class)
    private Map<String, Object> customerAttributes;
}

属性を別のテーブルに保存するのではなく、顧客テーブルの列にJSONとして保存します。 これは、スキーマの複雑さを軽減し、クエリのパフォーマンスを向上させるのに役立ちます。

まず、 customerAttributesを取得してJSON文字列に変換するserializeメソッドを作成します

public void serializeCustomerAttributes() throws JsonProcessingException {
    this.customerAttributeJSON = objectMapper.writeValueAsString(customerAttributes);
}

このメソッドは、永続化する前に手動で呼び出すことも、 setCustomerAttributes メソッドから呼び出すこともできます。これにより、属性が更新されるたびに、JSON文字列も更新されます。

次に、データベースから Customer を取得するときに、JSON文字列をHashMapオブジェクトに逆シリアル化するメソッドを作成します。

public void deserializeCustomerAttributes() throws IOException {
    this.customerAttributes = objectMapper.readValue(customerAttributeJSON, HashMap.class);
}

繰り返しになりますが、このメソッドを呼び出すことができる場所はいくつかありますが、この例では、手動で呼び出します。

したがって、 Customer オブジェクトを永続化して取得すると、次のようになります。

@Test
public void whenStoringAJsonColumn_thenDeserializedVersionMatches() {
    Customer customer = new Customer();
    customer.setFirstName("first name");
    customer.setLastName("last name");

    Map<String, Object> attributes = new HashMap<>();
    attributes.put("address", "123 Main Street");
    attributes.put("zipcode", 12345);

    customer.setCustomerAttributes(attributes);
    customer.serializeCustomerAttributes();

    String serialized = customer.getCustomerAttributeJSON();

    customer.setCustomerAttributeJSON(serialized);
    customer.deserializeCustomerAttributes();

    assertEquals(attributes, customer.getCustomerAttributes());
}

4. 属性コンバーター

JPA 2.1以降を使用している場合は、AttributeConvertersを使用してこのプロセスを合理化できます。

まず、AttributeConverterの実装を作成します。 以前のコードを再利用します。

public class HashMapConverter implements AttributeConverter<Map<String, Object>, String> {

    @Override
    public String convertToDatabaseColumn(Map<String, Object> customerInfo) {

        String customerInfoJson = null;
        try {
            customerInfoJson = objectMapper.writeValueAsString(customerInfo);
        } catch (final JsonProcessingException e) {
            logger.error("JSON writing error", e);
        }

        return customerInfoJson;
    }

    @Override
    public Map<String, Object> convertToEntityAttribute(String customerInfoJSON) {

        Map<String, Object> customerInfo = null;
        try {
            customerInfo = objectMapper.readValue(customerInfoJSON, Map.class);
        } catch (final IOException e) {
            logger.error("JSON reading error", e);
        }

        return customerInfo;
    }

}

次に、customerAttributesフィールドに新しいAttributeConverterを使用するようにHibernateに指示します。これで完了です。

@Convert(converter = HashMapConverter.class)
private Map<String, Object> customerAttributes;

このアプローチでは、Hibernateが自動的に処理するため、シリアル化メソッドと逆シリアル化メソッドを手動で呼び出す必要がなくなりました。 通常、Customerオブジェクトを保存して取得するだけです。

5. 結論

この記事では、HibernateとJacksonを使用してJSONオブジェクトを永続化する方法の例をいくつか見てきました。

最初の例では、カスタムのシリアル化および逆シリアル化メソッドを使用した、単純で互換性のあるメソッドを調べました。 次に、コードを簡素化する強力な方法としてAttributeConvertersを導入しました。

いつものように、Githubでこのチュートリアルのソースコードを確認してください。