196

リストタイプのフィールドを持つエンティティを永続化する最も賢い方法は何ですか?

Command.java

package persistlistofstring;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Persistence;

@Entity
public class Command implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    Long id;
    @Basic
    List<String> arguments = new ArrayList<String>();

    public static void main(String[] args) {
        Command command = new Command();

        EntityManager em = Persistence
                .createEntityManagerFactory("pu")
                .createEntityManager();
        em.getTransaction().begin();
        em.persist(command);
        em.getTransaction().commit();
        em.close();

        System.out.println("Persisted with id=" + command.id);
    }
}

このコードは以下を生成します:

> Exception in thread "main" javax.persistence.PersistenceException: No Persistence provider for EntityManager named pu: Provider named oracle.toplink.essentials.PersistenceProvider threw unexpected exception at create EntityManagerFactory: 
> oracle.toplink.essentials.exceptions.PersistenceUnitLoadingException
> Local Exception Stack: 
> Exception [TOPLINK-30005] (Oracle TopLink Essentials - 2.0.1 (Build b09d-fcs (12/06/2007))): oracle.toplink.essentials.exceptions.PersistenceUnitLoadingException
> Exception Description: An exception was thrown while searching for persistence archives with ClassLoader: sun.misc.Launcher$AppClassLoader@11b86e7
> Internal Exception: javax.persistence.PersistenceException: Exception [TOPLINK-28018] (Oracle TopLink Essentials - 2.0.1 (Build b09d-fcs (12/06/2007))): oracle.toplink.essentials.exceptions.EntityManagerSetupException
> Exception Description: predeploy for PersistenceUnit [pu] failed.
> Internal Exception: Exception [TOPLINK-7155] (Oracle TopLink Essentials - 2.0.1 (Build b09d-fcs (12/06/2007))): oracle.toplink.essentials.exceptions.ValidationException
> Exception Description: The type [interface java.util.List] for the attribute [arguments] on the entity class [class persistlistofstring.Command] is not a valid type for a serialized mapping. The attribute type must implement the Serializable interface.
>         at oracle.toplink.essentials.exceptions.PersistenceUnitLoadingException.exceptionSearchingForPersistenceResources(PersistenceUnitLoadingException.java:143)
>         at oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider.createEntityManagerFactory(EntityManagerFactoryProvider.java:169)
>         at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:110)
>         at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:83)
>         at persistlistofstring.Command.main(Command.java:30)
> Caused by: 
> ...
4

14 に答える 14

223

いくつかの JPA 2 実装を使用します。Hibernate のものと同様に、必要なことを正確に行う @ElementCollection アノテーションを追加します。ここに一例があります。

編集

以下のコメントで述べたように、正しい JPA 2 実装は

javax.persistence.ElementCollection

@ElementCollection
Map<Key, Value> collection;

参照: http://docs.oracle.com/javaee/6/api/javax/persistence/ElementCollection.html

于 2009-09-15T17:14:43.723 に答える
32

この回答は JPA2 より前の実装で作成されました。JPA2 を使用している場合は、上記の ElementCollection の回答を参照してください。

モデル オブジェクト内のオブジェクトのリストは、通常、別のオブジェクトとの "OneToMany" 関係と見なされます。ただし、文字列には ID がないため、(それ自体では) 1 対多の関係の許容されるクライアントではありません。

したがって、文字列のリストを、ID と文字列を含む Argument クラスの JPA オブジェクトのリストに変換する必要があります。ID として文字列を使用できる可能性があります。これにより、ID フィールドを削除することと、文字列が等しい行を統合することの両方で、テーブルのスペースを少し節約できますが、引数を元の順序に戻すことができなくなります。 (注文情報を保存していないため)。

または、リストを @Transient に変換し、別のフィールド (argStorage) を VARCHAR() または CLOB のいずれかであるクラスに追加することもできます。次に、3 つの関数を追加する必要があります。そのうちの 2 つは同じで、文字列のリストを、簡単に分離できるように区切られた単一の文字列 (argStorage 内) に変換する必要があります。@PrePersist と @PreUpdate を使用して、これら 2 つの関数 (それぞれが同じことを行う) に注釈を付けます。最後に、argStorage を再び文字列のリストに分割する 3 番目の関数を追加し、@PostLoad アノテーションを付けます。これにより、コマンドを保存するたびに CLOB が文字列で更新され、DB に保存する前に argStorage フィールドが更新されます。

私はまだ最初のケースを行うことをお勧めします。後で実際の関係を築くための良い習慣です。

于 2008-11-13T15:34:46.943 に答える
16

Java Persistence with Hibernateによると

注釈付きの値型のコレクションのマッピング [...]。執筆時点では、Java Persistence 標準の一部ではありません。

Hibernate を使用している場合は、次のようなことができます。

@CollectionOfElements(targetElement = String.class)
@JoinTable(name = "foo", joinColumns = @JoinColumn(name = "foo_id"))
@IndexColumn(name = "POSITION", base = 1)
@Column(name = "baz", nullable = false)
private List<String> arguments = new ArrayList<String>();

更新: これは JPA2 で利用できるようになったことに注意してください。

于 2008-11-13T15:26:32.717 に答える
11

JPA の Hibernate 実装を使用する場合、型を List ではなく ArrayList として宣言するだけで、hibernate がデータのリストを格納できることがわかりました。

Entity オブジェクトのリストを作成する場合と比べて、明らかにこれには多くの欠点があります。遅延読み込みがなく、他のオブジェクトからリスト内のエンティティを参照する機能がなく、おそらくデータベース クエリの作成がより困難になります。ただし、エンティティと一緒に常に熱心にフェッチしたいかなりプリミティブなタイプのリストを扱っている場合、このアプローチは私にはうまくいくようです。

@Entity
public class Command implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    Long id;

    ArrayList<String> arguments = new ArrayList<String>();


}
于 2008-12-24T11:38:39.863 に答える
10

私は同じ問題を抱えていたので、与えられた可能な解決策を投資しましたが、最後に「;」を実装することにしました 文字列の分離リスト。

ので、私は持っています

// a ; separated list of arguments
String arguments;

public List<String> getArguments() {
    return Arrays.asList(arguments.split(";"));
}

このようにして、リストはデータベーステーブルで簡単に読み取り/編集できます。

于 2009-12-21T16:07:59.993 に答える
1

この問題に対する私の修正は、主キーを外部キーと分離することでした。Eclipse を使用していて、上記の変更を行った場合は、データベース エクスプローラーを更新することを忘れないでください。次に、テーブルからエンティティを再作成します。

于 2012-04-25T12:34:47.837 に答える