私は JPA を学び、理解しようとしていますが、親とその子を一度に削除することに関していくつか質問があります。私は OpenJPA と EJB3 を使用しています。カテゴリと製品の 2 つのエンティティがあります。カテゴリには多くの製品が含まれており、製品にはその親カテゴリへの参照があります。カテゴリの製品リストはカスケードに設定されています。
//Category
@Entity @NamedQueries({@NamedQuery(name = "Category.getCategoryByName", query = "SELECT c FROM Category c WHERE c.name = :name"),@NamedQuery(name = "Category.getCategoryByCategoryId", query = "SELECT c FROM Category c WHERE c.categoryid = :categoryid"), @NamedQuery(name = "Category.getAllCategories", query = "SELECT c FROM Category c left join fetch c.products")})
public class Category implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=IDENTITY)
private Integer categoryid;
private String name;
//bi-directional many-to-one association to Product
@OneToMany(cascade={CascadeType.ALL}, orphanRemoval = true,
fetch = EAGER, mappedBy="category")
private List<Product> products;
}
//Product
@Entity
@NamedQueries({@NamedQuery(name = "Product.getProductsByCategory",
query = "SELECT p.code, p.description, p.name, p.productid, p.weight FROM Product p WHERE p.category.categoryid = :category_categoryid"),
@NamedQuery(name = "Product.getProductByName", query = "SELECT p FROM Product p WHERE p.name = :name"),
@NamedQuery(name = "Product.getProductByCode", query = "SELECT p FROM Product p WHERE p.code = :code"),
@NamedQuery(name = "Product.getProductByProductId", query = "SELECT p FROM Product p WHERE p.productid = :productid"),
@NamedQuery(name = "Product.getAllProducts", query = "SELECT p FROM Product p")})
public class Product implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=IDENTITY)
private Integer productid;
private String code;
private String description;
private String name;
private Double weight;
//bi-directional many-to-one association to Category
@ManyToOne(optional = false)
@JoinColumn(name="CATEGORYID")
private Category category;
}
}
// The EJB
@Stateless
@LocalBean
public class ShopManagerBean implements Serializable {
@PersistenceContext(unitName = "TestEJBProject2", type = PersistenceContextType.TRANSACTION)
private EntityManager entityManager;
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void deleteCategory(Category category)
throws TestApplicationException {
try {
Category actualCat = entityManager.find(Category.class,
category.getCategoryid());
List<Product> products = actualCat.getProducts();
if (products != null) {
Iterator<Product> it = products.iterator();
while (it.hasNext()) {
Product p = it.next();
it.remove();
entityManager.remove(p);
}
}
entityManager.refresh(actualCat);
entityManager.remove(actualCat);
} catch (Exception e) {
e.printStackTrace();
throw new TestApplicationException("Error creating new Product", e);
}
}
}
EJB の deleteCategory メソッドで次のコードを使用すると、オプティミスティック ロック例外が発生するため、親と子を削除できません (オブジェクト インスタンス「entity.Product-101」をデータ ストアにフラッシュするときに、オプティミスティック ロック違反が検出されました。これは、オブジェクトが別のトランザクションで同時に変更されたことを示します。) - 製品の子をデータ ストアにフラッシュすることについて不平を言っています。
Category actualCat = entityManager.find(Category.class, category.getCategoryid());
if (products != null) {
actualCat.getProducts().clear();
}
entityManager.remove(actualCat);
ただし、deleteCategory メソッドで次のコードを使用すると、親と子を削除できます...ただし、子を削除した後、親を削除する前に entityManager.refresh(actualCat) を呼び出す場合のみ (そうしないと、楽観的ロックが発生します)例外)。なぜこれが当てはまるのか、またOpenJPA V2でカスケード削除を行う正しい/最良の方法は何かを誰かに説明してもらえますか?
Category actualCat = entityManager.find(Category.class, category.getCategoryid());
List<Product> products = actualCat.getProducts();
if (products != null) {
Iterator<Product> it = products.iterator();
while (it.hasNext()) {
Product p = it.next();
it.remove();
entityManager.remove(p);
}
}
entityManager.refresh(actualCat);
entityManager.remove(actualCat);
よろしくお願いいたします。
ファイス
添加
データベース作成スクリプトは次のとおりです。
--
CREATE SCHEMA "DB2ADMIN";
CREATE TABLE "DB2ADMIN"."CATEGORY" ( "CATEGORYID" INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY ( 1 MINVALUE 1 MAXVALUE 2147483647 NO CYCLE CACHE 20 による 1 INCREMENT で開始)、"NAME" VARCHAR(50) NOT NULL ) DATA CAPTURE NONE ;
CREATE TABLE "DB2ADMIN"."PRODUCT" ( "PRODUCTID" INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY ( START WITH 1 INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 NO CYCLE CACHE 20), "CODE" CHAR(15) NOT NULL, "NAME" VARCHAR(50) NOT NULL, "DESCRIPTION" VARCHAR(200) NOT NULL, "WEIGHT" FLOAT(53) NOT NULL, "CATEGORYID" INTEGER NOT NULL ) DATA CAPTURE NONE;
ALTER TABLE "DB2ADMIN"."CATEGORY" ADD CONSTRAINT "CATEGORY_PK" PRIMARY KEY ("CATEGORYID");
ALTER TABLE "DB2ADMIN"."PRODUCT" ADD CONSTRAINT "PRODUCT_PK" PRIMARY KEY ("PRODUCTID");
ALTER TABLE "DB2ADMIN"."PRODUCT" ADD CONSTRAINT "PRODUCT_CATEGORY_FK" 外部キー ("CATEGORYID") REFERENCES "DB2ADMIN"."CATEGORY" ("CATEGORYID") ON DELETE CASCADE;