JPAの@Embeddableと@ElementCollectionでEntityの責務を明確化する。
Entityを定義したものの、Entityの中に繰り返し項目があり、別のEntityに切り出してしまった経験はないでしょうか。
@OneToMany(mappedBy="id") public Set<Order> orders;
Entity自体はまとめて一つで扱いたい単位であり、RDBのテーブル定義の形に引きづられて複数のEntityが生まれてしまうのは良くないです。
Entityを定義してしまうことにより、直接アクセスしてほしくないEntityの属性についてのアクセスパスを提供してしまうことになります。
また、複数のEntityに分断されるため、DB上のライフサイクルも分れてしまうことになりバグの温床となります。*1
そのようなときには@Embeddableと@ElementCollectionを利用することで、Entityを増やさないようにしつつ、RDBへのオブジェクトのマッピングを楽に行うことができるようになります。
@Embeddableとは
@Embeddableはクラスをエンティティに埋め込むために使用するアノテーションです。
複数の属性で構成される物をまとめて扱いたいと思うことはないでしょうか。
例えば、住所をまとめて一つのクラスとし、エンティティに組み込むためには以下のようにします。
@Entity public class Employee { private String employeeName; ・ ・ ・ @Embedded protected Address address; } @Embeddable public class Address { protected String street; protected String city; protected String state; @Embedded protected Zipcode zipcode; } @Embeddable public class Zipcode { protected String zip; protected String plusFour; }
@ElementCollectionとは
@ElementCollectionはCollectionをEntityの属性として使用するアノテーションです。*2
基本型や、@Embeddableなものを埋め込むことが出来ます。
@Entity public class Employee { private String employeeName; 〜〜〜 @Embedded @OrderBy("zipcode.zip, zipcode.plusFour") protected Set<Address> address; }
@ElementCollectionを使用した場合、デフォルトでは組み込み先のEntityのライフサイクルに依存します。
Entityを定義して、CascadeType.Allを追加で付けるよりもずいぶんと直感的ではないでしょうか。
EmbeddableやElementCollectionで定義されているクラスは直接使わないことがEntityの設計者にも明確に伝わるようになるので、開発もやりやすくなるでしょう。
また、デフォルト値が違うだけで、基本的に@Entityで定義した場合と同じ設定を行うことが出来ます。
まとめ
@ElementCollection良いという、Entity設計ヲタの叫びでした。
※サンプルについては各JavaDocにあったものを適時修正して使用しました。
https://docs.oracle.com/javaee/7/api/javax/persistence/Embeddable.html
http://docs.oracle.com/javaee/7/api/javax/persistence/ElementCollection.html
http://docs.oracle.com/javaee/7/api/javax/persistence/OrderBy.html