これはなに? †
- JPA で、カスケード削除をやりたい
- EntityManager?#remove(obj) で、カスケード削除ができることは知っている
- JPQL の DELETE 文って、カスケード削除できないの? の検証をしてみる
- 実行環境 : JDK 7 / Postgre SQL 9.2 / Eclipse Link 2.5
テーブル作成 †
-- Table: mother_tbl
-- Table: child_tbl
ALTER TABLE child_tbl DROP CONSTRAINT child_tbl_motherid_fkey;
DROP TABLE IF EXISTS child_tbl;
DROP TABLE IF EXISTS mother_tbl;
CREATE TABLE mother_tbl
(
id bigserial NOT NULL,
version timestamp,
name character varying(255),
CONSTRAINT id_mother PRIMARY KEY (id)
);
CREATE TABLE child_tbl
(
id bigserial NOT NULL,
version timestamp without time zone,
name character varying(255),
motherid bigint,
CONSTRAINT id_child PRIMARY KEY (id),
CONSTRAINT child_tbl_motherid_fkey FOREIGN KEY (motherid)
REFERENCES mother_tbl (id) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE CASCADE
);
ALTER TABLE mother_tbl
OWNER TO postgres;
ALTER TABLE child_tbl
OWNER TO postgres;
INSERT INTO mother_tbl (version, name) VALUES (current_timestamp, 'm1');
INSERT INTO mother_tbl (version, name) VALUES (current_timestamp, 'm2');
INSERT INTO child_tbl (version, name, motherid)
VALUES (current_timestamp, 'c1', (SELECT id FROM mother_tbl WHERE name = 'm1'));
INSERT INTO child_tbl (version, name, motherid)
VALUES (current_timestamp, 'c2', (SELECT id FROM mother_tbl WHERE name = 'm1'));
INSERT INTO child_tbl (version, name, motherid)
VALUES (current_timestamp, 'c3', (SELECT id FROM mother_tbl WHERE name = 'm1'));
INSERT INTO child_tbl (version, name, motherid)
VALUES (current_timestamp, 'c4', (SELECT id FROM mother_tbl WHERE name = 'm2'));
INSERT INTO child_tbl (version, name, motherid)
VALUES (current_timestamp, 'c5', (SELECT id FROM mother_tbl WHERE name = 'm2'));
INSERT INTO child_tbl (version, name, motherid)
VALUES (current_timestamp, 'c6', (SELECT id FROM mother_tbl WHERE name = 'm2'));
Entity 作成 †
- MotherTbl?.java
@Entity
@Table(name = "mother_tbl")
@XmlRootElement
@NamedQueries({
@NamedQuery(name = "MotherTbl.findAll", query = "SELECT m FROM MotherTbl m"),
@NamedQuery(name = "MotherTbl.findById", query = "SELECT m FROM MotherTbl m WHERE m.id = :id"),
@NamedQuery(name = "MotherTbl.findByVersion", query = "SELECT m FROM MotherTbl m WHERE m.version = :version"),
@NamedQuery(name = "MotherTbl.findByName", query = "SELECT m FROM MotherTbl m WHERE m.name = :name")})
public class MotherTbl implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "id")
private Long id;
@Version
@Column(name = "version")
private Timestamp version;
@Column(name = "name")
private String name;
@OneToMany(mappedBy = "motherid", cascade = CascadeType.ALL)
private Collection<ChildTbl> childTblCollection;
public MotherTbl() {
}
public MotherTbl(Long id) {
this.id = id;
}
以下略
- ChildTbl?.java
@Entity
@Table(name = "child_tbl")
@XmlRootElement
@NamedQueries({
@NamedQuery(name = "ChildTbl.findAll", query = "SELECT c FROM ChildTbl c"),
@NamedQuery(name = "ChildTbl.findById", query = "SELECT c FROM ChildTbl c WHERE c.id = :id"),
@NamedQuery(name = "ChildTbl.findByVersion", query = "SELECT c FROM ChildTbl c WHERE c.version = :version"),
@NamedQuery(name = "ChildTbl.findByName", query = "SELECT c FROM ChildTbl c WHERE c.name = :name")})
public class ChildTbl implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "id")
private Long id;
@Version
@Column(name = "version")
private Timestamp version;
@Column(name = "name")
private String name;
@JoinColumn(name = "motherid", referencedColumnName = "id")
@ManyToOne
private MotherTbl motherid;
public ChildTbl() {
}
public ChildTbl(Long id) {
this.id = id;
}
以下略
EntityManager?#remove(obj) でカスケード削除 †
public class App {
public static void main(String[] args) {
EntityManagerFactory emf
= Persistence.createEntityManagerFactory("com.mycompany_JPAExam_jar_1.0-SNAPSHOTPU");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
TypedQuery<MotherTbl> query
= em.createNamedQuery("MotherTbl.findByName", MotherTbl.class);
query.setParameter("name", "m1");
MotherTbl mother = query.getSingleResult();
em.remove(mother);
tx.commit();
}
}
- 実行ログ
[EL Fine]: sql: Connection(565816949)--SELECT id, name, version FROM mother_tbl WHERE (name = ?)
bind => [m1]
[EL Fine]: sql: Connection(565816949)--SELECT id, name, version, motherid FROM child_tbl WHERE (motherid = ?)
bind => [1]
[EL Fine]: sql: Connection(565816949)--DELETE FROM child_tbl WHERE ((id = ?) AND (version = ?))
bind => [1, 2013-10-27 22:30:48.278485]
[EL Fine]: sql: Connection(565816949)--DELETE FROM child_tbl WHERE ((id = ?) AND (version = ?))
bind => [2, 2013-10-27 22:30:48.28811]
[EL Fine]: sql: Connection(565816949)--DELETE FROM child_tbl WHERE ((id = ?) AND (version = ?))
bind => [3, 2013-10-27 22:30:48.294036]
[EL Fine]: sql: Connection(565816949)--DELETE FROM mother_tbl WHERE ((id = ?) AND (version = ?))
bind => [1, 2013-10-27 22:30:48.263532]
まぁ、順当な SQL 文が発行されてる
JPQL の DELETE 文でカスケード削除 †
public class App {
public static void main(String[] args) {
EntityManagerFactory emf
= Persistence.createEntityManagerFactory("com.mycompany_JPAExam_jar_1.0-SNAPSHOTPU");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Query query = em.createQuery("DELETE FROM MotherTbl m WHERE m.name = :name");
query.setParameter("name", "m1");
int updateRows = query.executeUpdate();
System.out.println(updateRows);
tx.commit();
}
}
- 実行ログ
[EL Fine]: sql: Connection(1541737429)--DELETE FROM mother_tbl WHERE (name = ?)
bind => [m1]
1
- 実行結果
JPQL からは、m1 しか消していないけど、CREATE TABLE child_tbl の DELETE CASCADE 指定に従って、子レコードが消された
CONSTRAINT child_tbl_motherid_fkey FOREIGN KEY (motherid)
REFERENCES mother_tbl (id) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE CASCADE
子から親への FK に ON DELETE CASCADE を指定していない場合、親への DELET 文を発行したらどうなるか? †
子から親への FK に ON DELETE CASCADE をつけてんだから em.remove(m) で、child_tbl への delete 文を発行しないで欲しい †
- org.eclipse.persistence.annotations.CascadeOnDelete? をつけると、child_tbl への delete 文発行が抑止される
public class MotherTbl implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "id")
private Long id;
@Version
@Column(name = "version")
private Timestamp version;
@Column(name = "name")
private String name;
@OneToMany(mappedBy = "motherid", cascade = CascadeType.ALL)
@CascadeOnDelete
private Collection<ChildTbl> childTblCollection;
(略)
Java#Glassfish