完全なソースは こちら。
追記
Wildfly (Hibernate) だと persist()
の後に生成された ID がエンティティにセットされませんでした。
実装依存かもしれません。。。
環境
AP サーバ
Payara Micro 4.1.1.161.1
(JPA 実装:EclipseLink)
DB
Derby (Payara に組み込まれているやつ)
実装
Sample.java
package sample.domain;
import java.util.Objects;
import javax.persistence.Embedded;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
@Entity
@Table(name="SAMPLE_TABLE")
@NamedQuery(name="Sample.findAll", query="SELECT sample FROM Sample sample ORDER BY sample.id ASC")
public class Sample {
@EmbeddedId
private final SampleId id = new SampleId();
@Embedded
private SampleValue value;
public void setValue(SampleValue value) {
this.value = value;
}
@Override
public String toString() {
return "Sample [id=" + id + ", value=" + value + "]";
}
@Override
public int hashCode() {
(省略)
}
@Override
public boolean equals(Object obj) {
(省略)
}
}
SampleId.java
package sample.domain;
import java.util.Objects;
import javax.persistence.Embeddable;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
@Embeddable
public class SampleId {
@GeneratedValue(strategy=GenerationType.IDENTITY)
private final Integer id;
public SampleId(int id) {
this.id = id;
}
SampleId() {
this.id = null;
}
@Override
public String toString() {
return "SampleId [id=" + id + "]";
}
@Override
public boolean equals(Object obj) {
(省略)
}
@Override
public int hashCode() {
(省略)
}
}
HelloResource.java
package sample.web;
import java.util.stream.Collectors;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import sample.domain.Sample;
import sample.domain.SampleValue;
@Stateless
@Path("hello")
public class HelloResource {
@PersistenceContext(unitName="SampleUnit")
private EntityManager em;
@GET
public String hello(@QueryParam("value") String value) {
Sample sample = new Sample();
sample.setValue(new SampleValue(value));
System.out.println("[before persist]" + sample);
this.em.persist(sample);
System.out.println("[after persist]" + sample);
this.em.flush();
System.out.println("[after flush]" + sample);
TypedQuery<Sample> query = this.em.createNamedQuery("Sample.findAll", Sample.class);
return query.getResultList().stream().map(Sample::toString).collect(Collectors.joining("\n"));
}
}
動作確認
curlで検証
$ curl http://localhost:8080/jpa-sample/api/hello?value=foo
Sample [id=SampleId [id=1], value=SampleValue [value=foo]]
サーバー側出力
[before persist]Sample [id=SampleId [id=null], value=SampleValue [value=foo]]
[after persist]Sample [id=SampleId [id=null], value=SampleValue [value=foo]]
[after flush]Sample [id=SampleId [id=1], value=SampleValue [value=foo]]
-
id
が自動採番されてデータベースに保存されている。 -
flush
したら、SampleId
にも採番されたID
がちゃんと設定されている(final
でも構わずに!)。
説明
Sample.java
import javax.persistence.EmbeddedId;
(省略)
public class Sample {
@EmbeddedId
private final SampleId id = new SampleId();
- エンティティのキーとなるフィールドは
@EmbeddedId
でアノテートする。 - キーのフィールド(
id
)には、デフォルトで ID クラス(SampleId
)のインスタンスを設定しておく。-
id
をnull
にしているとpersist()
したときに落ちる。 -
SampleId
オブジェクトが持つ実際の ID 値はnull
になるようにしておく。
-
SampleId.java
import javax.persistence.Embeddable;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
@Embeddable
public class SampleId {
@GeneratedValue(strategy=GenerationType.IDENTITY)
private final Integer id;
public SampleId(int id) {
this.id = id;
}
SampleId() {
this.id = null;
}
- ID となるクラスは
@Embeddable
でアノテートする。 - ID の値が設定されるフィールドに
@GeneratedValue
などのアノテートを設定する。- カラム名が実際と異なるなら、このクラスのフィールドを
@Column
でアノテートする。
- カラム名が実際と異なるなら、このクラスのフィールドを
- デフォルトコンストラクタで
new
した場合はid
がnull
になるようにしておく。
final
なのに値が書き換わっているあたり黒魔術感がハンパないけど、とりあえず動いた。