2016-11-25 16 views
3

私は春のブート時にJSONオブジェクトをMySQLデータベースに格納しようとしています。私は間違って何かをしていることを知っているが、私は春にはかなり新しいので、何かを理解できない。HibernateとJPAを使用してJSONオブジェクトを永続化する

私は次のJSONオブジェクト(HTTP PUT経由)を取得し、後で(HTTP GET経由で)ユーザーがフェッチできるようにデータベースに格納する必要があります。上記の場合、オブジェクトにのキーは、私は、コントローラ内のオブジェクトをキャッチするHashMapを使用していますその要件に起因して変化し得ること

{ 
    "A": { 
    "Name": "Cat", 
    "Age": "1" 
    }, 
    "B": { 
    "Name": "Dog", 
    "Age": "2" 
    }, 
    "C": { 
    "Name": "Horse", 
    "Age": "1" 
    } 
} 

注意。

@RequestMapping(method = RequestMethod.POST) 
    public String addPostCollection(@RequestBody HashMap<String, Animal> hp) { 

     hp.forEach((x, y) -> { 
      postRepository.save(hp.get(x)); 
     }); 

     return "OK"; 

    } 

あなたが方法で見ることができるように、私はHashMapを反復し、DB内の各Animalオブジェクトを永続化することができます。しかし、私はHashMap全体を単一のレコードに保持する方法を探しています。私はいくつかの読書をして、彼らは@ManyToManyマッピングを使用するように私に示唆しています。

誰もが別の方法でHashMapを維持する方向で私を指摘できますか? (またはこれを行うには@ManyToManyを使用していますか?)

+0

"1つのレコードに全体のHashMap" ??または、単一のdbトランザクション内のハッシュマップ全体? –

+0

私はシングルレコードを意味しました。 – Fawzan

答えて

8

this articleで説明したように、Hibernateを使用してJSONオブジェクトを永続化するのは非常に簡単です。 、詳細情報については

<dependency> 
    <groupId>com.vladmihalcea</groupId> 
    <artifactId>hibernate-types-52</artifactId> 
    <version>${hibernate-types.version}</version> 
</dependency> 

hibernate-types open-source projectをチェックアウト:

あなたは、単に次の依存関係を使用してMavenの中央を経由して にそれらを得ることができ、手動ですべてのこれらのタイプを作成する必要はありません。

ここで、そのすべての仕組みを説明します。

次のエンティティを持っていると仮定すると:

@Entity(name = "Book") 
@Table(name = "book") 
@TypeDef(
    name = "jsonb-node", 
    typeClass = JsonNodeBinaryType.class 
) 
public class Book { 

    @Id 
    @GeneratedValue 
    private Long id; 

    @NaturalId 
    private String isbn; 

    @Type(type = "jsonb-node") 
    @Column(columnDefinition = "jsonb") 
    private JsonNode properties; 

    //Getters and setters omitted for brevity 
} 

お知らせ上記のコードでは二つのこと:

  • @TypeDefがによって処理される新しいカスタム休止タイプ、jsonb-nodeを定義するために使用されますJsonNodeBinaryType
  • properties属性はjsonbの列タイプを持ち、それはジャクソンJsonNode

JsonNodeBinaryTypeこのように実装されている次のよう

public class JsonNodeBinaryType 
    extends AbstractSingleColumnStandardBasicType<JsonNode> { 

    public JsonNodeBinaryType() { 
     super( 
      JsonBinarySqlTypeDescriptor.INSTANCE, 
      JsonNodeTypeDescriptor.INSTANCE 
     ); 
    } 

    public String getName() { 
     return "jsonb-node"; 
    } 
} 
JsonBinarySqlTypeDescriptor

に見える:

public class JsonBinarySqlTypeDescriptor 
    extends AbstractJsonSqlTypeDescriptor { 

    public static final JsonBinarySqlTypeDescriptor INSTANCE = 
     new JsonBinarySqlTypeDescriptor(); 

    @Override 
    public <X> ValueBinder<X> getBinder(
      final JavaTypeDescriptor<X> javaTypeDescriptor 
     ) { 
     return new BasicBinder<X>(javaTypeDescriptor, this) { 
      @Override 
      protected void doBind(
        PreparedStatement st, 
        X value, 
        int index, 
        WrapperOptions options 
       ) throws SQLException { 
       st.setObject(
        index, 
        javaTypeDescriptor.unwrap(
         value, 
         JsonNode.class, 
         options 
        ), 
        getSqlType() 
       ); 
      } 

      @Override 
      protected void doBind(
        CallableStatement st, 
        X value, 
        String name, 
        WrapperOptions options 
       ) throws SQLException { 
       st.setObject(
        name, 
        javaTypeDescriptor.unwrap(
         value, 
         JsonNode.class, 
         options 
        ), 
        getSqlType() 
       ); 
      } 
     }; 
    } 
} 

AbstractJsonSqlTypeDescriptorソースコードはthis articleで可視化することができます。

次に、JsonNodeTypeDescriptorパラメータを結合または根底ResultSetからJSONオブジェクトからフェッチ中に、基礎となるJDBCドライバによって使用されるかもしれない種々の表現にJsonNodeを変換する責任があります。

public class JsonNodeTypeDescriptor 
     extends AbstractTypeDescriptor<JsonNode> { 

    public static final JsonNodeTypeDescriptor INSTANCE = 
     new JsonNodeTypeDescriptor(); 

    public JsonNodeTypeDescriptor() { 
     super( 
      JsonNode.class, 
      new MutableMutabilityPlan<JsonNode>() { 
       @Override 
       protected JsonNode deepCopyNotNull(
         JsonNode value 
        ) { 
        return JacksonUtil.clone(value); 
       } 
      } 
     ); 
    } 

    @Override 
    public boolean areEqual(JsonNode one, JsonNode another) { 
     if (one == another) { 
      return true; 
     } 
     if (one == null || another == null) { 
      return false; 
     } 
     return 
      JacksonUtil.toJsonNode(
       JacksonUtil.toString(one) 
      ).equals(
       JacksonUtil.toJsonNode(
        JacksonUtil.toString(another) 
       ) 
      ); 
    } 

    @Override 
    public String toString(JsonNode value) { 
     return JacksonUtil.toString(value); 
    } 

    @Override 
    public JsonNode fromString(String string) { 
     return JacksonUtil.toJsonNode(string); 
    } 

    @SuppressWarnings({ "unchecked" }) 
    @Override 
    public <X> X unwrap(
      JsonNode value, 
      Class<X> type, 
      WrapperOptions options 
     ) { 
     if (value == null) { 
      return null; 
     } 
     if (String.class.isAssignableFrom(type)) { 
      return (X) toString(value); 
     } 
     if (JsonNode.class.isAssignableFrom(type)) { 
      return (X) JacksonUtil.toJsonNode(toString(value)); 
     } 
     throw unknownUnwrap(type); 
    } 

    @Override 
    public <X> JsonNode wrap(X value, WrapperOptions options) { 
     if (value == null) { 
      return null; 
     } 
     return fromString(value.toString()); 
    } 

} 

これだけです!

さて、あなたはエンティティを保存する場合:

Book book = new Book(); 
book.setIsbn("978-9730228236"); 
book.setProperties(
    JacksonUtil.toJsonNode(
     "{" + 
     " \"title\": \"High-Performance Java Persistence\"," + 
     " \"author\": \"Vlad Mihalcea\"," + 
     " \"publisher\": \"Amazon\"," + 
     " \"price\": 44.99" + 
     "}" 
    ) 
); 

entityManager.persist(book); 
Hibernateは、次のSQL文を生成するために起こっている

INSERT INTO 
    book 
(
    isbn, 
    properties, 
    id 
) 
VALUES 
(
    '978-9730228236', 
    '{"title":"High-Performance Java Persistence","author":"Vlad Mihalcea","publisher":"Amazon","price":44.99}', 
    1 
) 

をそして、あなたはまた戻ってそれをロードし、それを変更することができます

Session session = entityManager.unwrap(Session.class); 

Book book = session 
    .bySimpleNaturalId(Book.class) 
    .load("978-9730228236"); 

LOGGER.info("Book details: {}", book.getProperties()); 

book.setProperties(
    JacksonUtil.toJsonNode(
     "{" + 
     " \"title\": \"High-Performance Java Persistence\"," + 
     " \"author\": \"Vlad Mihalcea\"," + 
     " \"publisher\": \"Amazon\"," + 
     " \"price\": 44.99," + 
     " \"url\": \"https://www.amazon.com/High-Performance-Java-Persistence-Vlad-Mihalcea/dp/973022823X/\"" + 
     "}" 
    ) 
); 

あなたのためのUPDATEの声明を読んで休止状態:

SELECT b.id AS id1_0_ 
FROM book b 
WHERE b.isbn = '978-9730228236' 

SELECT b.id AS id1_0_0_ , 
     b.isbn AS isbn2_0_0_ , 
     b.properties AS properti3_0_0_ 
FROM book b 
WHERE b.id = 1 

-- Book details: {"price":44.99,"title":"High-Performance Java Persistence","author":"Vlad Mihalcea","publisher":"Amazon"} 

UPDATE 
    book 
SET 
    properties = '{"title":"High-Performance Java Persistence","author":"Vlad Mihalcea","publisher":"Amazon","price":44.99,"url":"https://www.amazon.com/High-Performance-Java-Persistence-Vlad-Mihalcea/dp/973022823X/"}' 
WHERE 
    id = 1 

GitHubで利用可能なすべてのコード。

+0

チップをありがとう!しかし、あなたはJSONタイプを知らないH2データベースでこの作業を行うことができますか?私はテストのためにSpring BootとH2を使います。恐らくJSONの型をテキストとして扱うためにH2の方言をオーバーライドするのでしょうか?しかし、どのように考えていませんでした。ありがとう – vernjan

+0

実稼働環境でH2を実行せず、実稼働システムがPostgreSQLまたはMySQLを使用している間にテストに使用すると、統合テストにはまったく価値がありません。テストのために本番環境で使用するのと同じDBを使用する必要があります。 [この記事](https://vladmihalcea.com/2017/02/09/how-to-run-integration-tests-at-warp-speed-with-docker-and-tmpfs/)をご覧ください。 cnaはPostgreSQLとMySQLの統合テストをメモリ内のDBほど高速に実行します。 –

1

FasterXMLなどを使用して実際のオブジェクト(クラスを定義する必要があります)に解析し、Json.toJson(yourObj).toString()を使用してJson String。また、データクラスにも機能があるため、オブジェクトの操作が簡単になります。

0

1つの動物が1つのレコードです。 1レコードではなく、さらに多くのレコードを保存しています。 1つのトランザクションでより多くのレコードをコミットできます。 参照:How to persist a lot of entities (JPA)

1

JSONは構造化されているため、通常はマップ全体を1つのレコードに保持する必要はありません。 Hibernate/JPAクエリ関数などを使用することはできません。

あなたは本当に1つのレコードにマップ全体を保持したい場合は、その文字列表現にマップを持続可能性と、既に提案されているように、あなたのHashMapを再構築するためにジャクソンのようなJSONパーサーを使用

@Entity 
public class Animals { 

    private String animalsString; 

    public void setAnimalsString(String val) { 
    this.animalsString = val; 
    } 

    public String getAnimalsString() { 
    return this.animalsMap; 
    } 

    public HashMap<String, Animal> getAnimalsMap() { 
    ObjectMapper mapper = new ObjectMapper(); 
    TypeReference<HashMap<String,Animal>> typeRef = new TypeReference<HashMap<String,Animal>>() {}; 
    return mapper.readValue(animalsString, typeRef); 
    } 

} 

あなたの動物クラス:

public class Animal { 

    private String name; 
    private int age; 

    /* getter and setter */ 
    /* ... */ 
} 

そして、あなたは

@RequestMapping(method = RequestMethod.POST) 
public String addPostCollection(@RequestBody String hp) { 
    Animals animals = new Animals(); 
    animals.setAnimalsString(hp); 
    animalsRepository.save(hp); 
    return "OK"; 
} 
にあなたのコントローラメソッドを変更することができ
+0

これは私にとって最高の選択だと思います。オブジェクトを文字列として保存し、マップにマップします。 – Fawzan

関連する問題