List型のフィールドを持つエンティティを永続化する最も賢い方法は何ですか?
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:
> ...
いくつかのJPA 2実装を使用します。Hibernateに似た@ElementCollectionアノテーションを追加します。これは、必要なことを正確に行います。 1つの例があります here 。
編集
以下のコメントで述べたように、正しいJPA 2実装は
javax.persistence.ElementCollection
@ElementCollection
Map<Key, Value> collection;
参照: http://docs.Oracle.com/javaee/6/api/javax/persistence/ElementCollection.html
JPA2を使用している場合、この回答はJPA2以前の実装で作成されました。上記のElementCollectionの回答を参照してください。
モデルオブジェクト内のオブジェクトのリストは、通常、別のオブジェクトとの「OneToMany」関係と見なされます。ただし、文字列は(それ自体では)IDを持たないため、1対多リレーションシップの許容クライアントではありません。
したがって、shouldストリングのリストを、IDとストリングを含むArgument-class JPAオブジェクトのリストに変換します。文字列をIDとして使用する可能性があります。これにより、IDフィールドを削除したり、文字列が等しい行を統合したりすることで、テーブルのスペースを少し節約できますが、引数を元の順序に戻す機能が失われます(注文情報を保存しなかったため)。
または、リストを@Transientに変換し、クラスにVARCHAR()またはCLOBである別のフィールド(argStorage)を追加することもできます。次に、3つの関数を追加する必要があります。そのうち2つは同じであり、文字列のリストを、簡単に分離できる方法で区切られた単一の文字列(argStorage内)に変換する必要があります。これら2つの関数(それぞれ同じことを行う)に@PrePersistと@PreUpdateで注釈を付けます。最後に、argStorageを再度Stringsのリストに分割する3番目の関数を追加し、@ PostLoadアノテーションを付けます。これにより、コマンドを保存するたびにCLOBが文字列で更新され、DBに保存する前にargStorageフィールドが更新されます。
私はまだ最初のケースを行うことをお勧めします。後で実際の関係を築くのは良い習慣です。
古いスレッドを復活させてすみませんが、文字列リストをデータベースの1つのフィールドとして保存する代替ソリューションを探している人がいれば、ここでそれを解決します。次のようなコンバーターを作成します。
import Java.util.Arrays;
import Java.util.List;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
@Converter
public class StringListConverter implements AttributeConverter<List<String>, String> {
private static final String SPLIT_CHAR = ";";
@Override
public String convertToDatabaseColumn(List<String> stringList) {
return String.join(SPLIT_CHAR, stringList);
}
@Override
public List<String> convertToEntityAttribute(String string) {
return Arrays.asList(string.split(SPLIT_CHAR));
}
}
次のようにエンティティで使用します:
@Convert(converter = StringListConverter.class)
private List<String> yourList;
データベースでは、リストはfoo; bar; foobarとして保存され、Javaオブジェクトでは、これらの文字列を含むリストが取得されます。
これが誰かに役立つことを願っています。
HibernateでのJava Persistence による
値タイプのコレクションを注釈[...]でマッピングします。執筆時点では、それはJava Persistence標準の一部ではありません
Hibernateを使用している場合、次のようなことができます。
@org.hibernate.annotations.CollectionOfElements(
targetElement = Java.lang.String.class
)
@JoinTable(
name = "foo",
joinColumns = @JoinColumn(name = "foo_id")
)
@org.hibernate.annotations.IndexColumn(
name = "POSITION", base = 1
)
@Column(name = "baz", nullable = false)
private List<String> arguments = new ArrayList<String>();
更新:これはJPA2で利用可能になりました。
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>();
}
これも使用できます。
@Column(name="arguments")
@ElementCollection(targetClass=String.class)
private List<String> arguments;
私は同じ問題を抱えていたので、可能な解決策を投資しましたが、最後に「;」を実装することにしました文字列の区切りリスト。
ので、私は持っています
// a ; separated list of arguments
String arguments;
public List<String> getArguments() {
return Arrays.asList(arguments.split(";"));
}
このように、リストはデータベーステーブルで簡単に読み取り/編集できます。
わかった、少し遅れた。しかし、時間の経過とともにこれを見る勇敢な魂のために。
ドキュメント :
@Basic:データベース列へのマッピングの最も単純なタイプ。 Basic注釈は、Javaプリミティブ型、[...]、enum、およびJava.io.Serializableを実装するその他の型の永続プロパティまたはインスタンス変数に適用できます。
重要な部分はSerializableを実装する型です
したがって、最もシンプルで使いやすいソリューションは、List(またはシリアル化可能なコンテナ)の代わりにArrayListを使用するだけです。
@Basic
ArrayList<Color> lovedColors;
@Basic
ArrayList<String> catNames;
ただし、これにはシステムのシリアル化が使用されるため、次のような価格が伴うことに注意してください。
シリアル化されたオブジェクトモデルが変更される場合、uはデータを復元できない可能性があります
格納される各要素に小さなオーバーヘッドが追加されます。
要するに
フラグまたはいくつかの要素を保存するのは非常に簡単ですが、大きくなる可能性のあるデータを保存することはお勧めしません。
Thiagoの答えは正しいです。質問にさらに具体的なサンプルを追加すると、@ ElementCollectionはデータベースに新しいテーブルを作成しますが、2つのテーブルをマッピングせずに、コレクションはエンティティのコレクションではなくコレクションであることを意味します単純型(文字列など)または埋め込み可能な要素のコレクション(@ Embeddableアノテーションが付けられたクラス)。
Stringのリストを永続化するサンプルを次に示します
@ElementCollection
private Collection<String> options = new ArrayList<String>();
カスタムオブジェクトのリストを永続化するサンプルを次に示します
@Embedded
@ElementCollection
private Collection<Car> carList = new ArrayList<Car>();
この場合、クラスを作成する必要がありますEmbeddable
@Embeddable
public class Car {
}
この問題に対する私の修正は、主キーと外部キーを分離することでした。 Eclipseを使用して上記の変更を行った場合は、データベースエクスプローラーを更新してください。次に、テーブルからエンティティを再作成します。
@ElementCollection
マッピングの最も重要な設定を調査した回答はないようです。
このアノテーションを使用してリストをマッピングし、JPA/Hibernateがテーブルや列などを自動生成できるようにすると、自動生成された名前も使用されます。
それでは、基本的な例を分析しましょう。
@Entity
@Table(name = "sample")
public class MySample {
@Id
@GeneratedValue
private Long id;
@ElementCollection
@CollectionTable(name = "my_list", joinColumns = @JoinColumn(name = "id"))
@Column(name = "list")
private List<String> list;
}
@ElementCollection
アノテーション(既知のfetch
およびtargetClass
設定を定義できる場所)@CollectionTable
アノテーションは、joinColumns
、foreignKey
、indexes
、uniqueConstraints
などの定義と同様に、生成されるテーブルに名前を付けるときに非常に便利です。@Column
は、リストのvarchar
値を格納する列の名前を定義するために重要です。生成されるDDL作成は次のようになります。
-- table sample
CREATE TABLE sample (
id bigint(20) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (id)
);
-- table my_list
CREATE TABLE IF NOT EXISTS my_list (
id bigint(20) NOT NULL,
list varchar(255) DEFAULT NULL,
FOREIGN KEY (id) REFERENCES sample (id)
);