web-dev-qa-db-ja.com

HibernateおよびJPAを使用したJSONオブジェクトの永続化

春のブートでMySQLデータベースにJSONオブジェクトを保存しようとしています。私は何か間違ったことをしているのを知っていますが、私は春にかなり新しいのでそれが何であるか理解できません。

(HTTP PUTを介して)次のJSONオブジェクトを取得するレストエンドポイントがあり、ユーザーが後で(HTTP GETを介して)フェッチできるように、データベースに保存する必要があります。

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

上記の場合、オブジェクトのnumberofkeysが異なる場合があることに注意してください。その要件のため、HashMapを使用してオブジェクトをキャッチしていますコントローラで。

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

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

        return "OK";

    }

メソッドで確認できるように、HashMapを繰り返し、各Animalオブジェクトをdbに永続化できます。しかし、HashMap全体を1つのレコードに永続化する方法を探しています。私はいくつか読んだことがあり、@ManyToManyマッピングを使用するよう勧めています。

誰かがHashMapを別の方法で永続化する方向に私を向けることができますか? (または、@ManyToManyを使用してこれを行う唯一の正しい方法は?)

14
Fawzan

これは非常に一般的な要件であるため、JPAとHibernateを使用するときにJSON列タイプをマップする最良の方法について、 非常に詳細な記事 と書くことにしました。

Mavenの依存関係

最初に行う必要があるのは、次のように設定することです Hibernate Types プロジェクトにMaven依存関係pom.xml構成ファイル:

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

ドメインモデル

次のエンティティがあるとします。

@Entity(name = "Book")
@Table(name = "book")
@TypeDef(
    typeClass = JsonBinaryType.class, 
    defaultForType = JsonNode.class
)
public class Book {

    @Id
    @GeneratedValue
    private Long id;

    @NaturalId
    private String isbn;

    @Column(columnDefinition = "jsonb")
    private JsonNode properties;

    //Getters and setters omitted for brevity
}

@TypeDefは、Hibernateタイププロジェクトによって提供されるJsonNodeを使用してJsonBinaryTypeオブジェクトをマップするようにHibernateに指示するために使用されます。

試験時間

エンティティを保存すると、次のようになります。

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/\"" +
        "}"
    )
);

Hibernateが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
26
Vlad Mihalcea

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

マップ全体を1つのレコードに永続化したい場合は、マップをその文字列表現に永続化し、すでに提案されているように、JacksonなどのJSONパーサーを使用してHashMapを再構築できます。

@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";
}
2
kaaas

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

1
Petre Popescu

一匹は一記録です。 1つのレコードではなく、より多くのレコードを保存しています。 1つのトランザクションでより多くのレコードをコミットできます。参照: 多くのエンティティを永続化する方法(JPA)

0
Peter Šály