0

データベースに2つのテーブルがあります。

  • 広告-ユーザー定義の広告を表します
  • ad_categories-広告のカテゴリを表します

すべての広告は正確に1つのカテゴリに属している必要があるため、adsテーブルで、on UPDATE NO ACTION ON DELETENOACTIONを使用してad_categoriesを指す外部キーを定義しました。

私のアプリケーションでは、ユーザーは任意のカテゴリを削除できる必要がありますが、そのカテゴリに広告が含まれている場合は、カテゴリを削除する前に別のカテゴリに移動する必要があります。

em.getTransaction().begin();
// get currentNode
AdCategories currentNode = em.find(AdCategories.class, currentNodeId);
// get ads
List<Ads> resultList = em.createQuery("SELECT a from Ads a WHERE a.adCategoryId = :categoryId").setParameter("categoryId", currentNode).getResultList();
// get their new location
AdCategories newLocation = em.find(AdCategories.class, newLocationId);
// set their new location
for(Ads a: resultList)
    a.setAdCategoryId(newLocation);
em.remove(currentNode);
em.getTransaction().commit();

影響を受ける広告ではad_category_idが変更され、空のカテゴリが削除されると予想していました。ただし、影響を受ける広告も削除されます!!

EclipseLinkでFINESTレベルへのログインを有効にしたところ、トランザクションがコミットされると、最初にUPDATEクエリがデータベースに送信され、影響を受ける広告のad_category_idが変更され、次にカテゴリが削除されますが、削除は広告にカスケードされます。広告はoccoursを削除する前にad_category_idsを更新する必要があるため、理由がわかりません。

簡単な回避策の1つはem.flush()、カテゴリを削除する前に電話をかけることですが、それが最適な解決策だとは思いません。私はこの振る舞いを理解する必要があると思います。

NetBeansとPostgreSQLでEclipseLinkを使用しています。

テーブル定義:

AdCategories

@Entity
@Table(name = "ad_categories")
@XmlRootElement
@NamedQueries({
@NamedQuery(name = "AdCategories.findAll", query = "SELECT a FROM AdCategories a"),
@NamedQuery(name = "AdCategories.findById", query = "SELECT a FROM AdCategories a WHERE a.id = :id"),
@NamedQuery(name = "AdCategories.findByParentId", query = "SELECT a FROM AdCategories a WHERE a.parentId = :parentId"),
@NamedQuery(name = "AdCategories.findByCategoryOrder", query = "SELECT a FROM AdCategories a WHERE a.categoryOrder = :categoryOrder"),
@NamedQuery(name = "AdCategories.findByCategoryDepth", query = "SELECT a FROM AdCategories a WHERE a.categoryDepth = :categoryDepth"),
@NamedQuery(name = "AdCategories.findByName", query = "SELECT a FROM AdCategories a WHERE a.name = :name"),
@NamedQuery(name = "AdCategories.findByGrandParentId", query = "SELECT a FROM AdCategories a WHERE a.grandParentId = :grandParentId"),
@NamedQuery(name = "AdCategories.findByParentName", query = "SELECT a FROM AdCategories a WHERE a.parentName = :parentName"),
@NamedQuery(name = "AdCategories.findByGrandParentName", query = "SELECT a FROM AdCategories a WHERE a.grandParentName = :grandParentName")})
public class AdCategories implements Serializable {
@OneToMany(cascade = CascadeType.ALL, mappedBy = "adCategoryId")
private Collection<Ads> adsCollection;
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "id")
private Integer id;
@Basic(optional = false)
@Column(name = "parent_id")
private int parentId;
@Basic(optional = false)
@Column(name = "category_order")
private short categoryOrder;
@Basic(optional = false)
@Column(name = "category_depth")
private short categoryDepth;
@Basic(optional = false)
@Column(name = "name")
private String name;
@Column(name = "grand_parent_id")
private Integer grandParentId;
@Column(name = "parent_name")
private String parentName;
@Column(name = "grand_parent_name")
private String grandParentName;
...

広告

@Entity
@Table(name = "ads")
@XmlRootElement
@NamedQueries({
@NamedQuery(name = "Ads.findAll", query = "SELECT a FROM Ads a"),
@NamedQuery(name = "Ads.findByAdId", query = "SELECT a FROM Ads a WHERE a.adId = :adId"),
@NamedQuery(name = "Ads.findByName", query = "SELECT a FROM Ads a WHERE a.name = :name"),
@NamedQuery(name = "Ads.findByDescriptionShort", query = "SELECT a FROM Ads a WHERE a.descriptionShort = :descriptionShort"),
@NamedQuery(name = "Ads.findByDescriptionLong", query = "SELECT a FROM Ads a WHERE a.descriptionLong = :descriptionLong")})
public class Ads implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "ad_id")
private Integer adId;
@Basic(optional = false)
@Column(name = "name")
private String name;
@Basic(optional = false)
@Column(name = "description_short")
private String descriptionShort;
@Basic(optional = false)
@Column(name = "description_long")
private String descriptionLong;
@JoinColumn(name = "ad_category_id", referencedColumnName = "id")
@ManyToOne(optional = false)
private AdCategories adCategoryId;
...
4

2 に答える 2

0

の広告のコレクションでタイプ REMOVE (または ALL) のカスケードを宣言すると、AdCategoryJPA に次のように伝えられremove()ます。それがJPAの機能です。AdCategoryremove()

双方向の関連付けがあります。関連付けの両側が一貫した状態であることを確認するのはあなたの責任です。したがって、広告のカテゴリを変更する場合は、そのカテゴリの一連の広告からこの広告を削除し、新しいカテゴリに広告を追加する必要もあります。すべての場合で絶対に必須というわけではありませんが、あなたの場合は必須です。

また、あなたのネーミングは本当に悪いです。のインスタンスAdCategories単一のカテゴリです。したがって、エンティティには名前を付ける必要がありますAdCategory。についても同じでAds、名前を付ける必要がありますAd。フィールドadCategoryIdにはカテゴリ ID は含まれていませんが、カテゴリが含まれています。ではなくadCategoryofという名前にする必要があります。フィールドに名前を付ける理由 これは Ad クラスの ID であるため、明らかに Ad の ID です。したがって、名前を付ける必要があります。という名前にする必要があります。これは細かいことのように思えるかもしれませんが、コードの見栄えを良くして読みやすくするための細かいことです。categoryadCategoryIdadIdiddescriptionLonglongDescription

于 2013-03-03T13:00:21.533 に答える
0

ここでの問題は、手動で管理する必要がある双方向の関係を定義したことです (JPA プロバイダーはそれを行いません)。呼び出しコードでは、広告の観点から、広告とそのカテゴリ間のリンクを解除します。

for(Ads a: resultList)
    a.setAdCategoryId(newLocation);

しかし、あなたのカテゴリは、関連していると信じている広告のコレクションを保持し続けており、それを削除すると、それらの広告も削除されます (CascadeType.ALL アノテーションのため)。これを修正するには、2 つの方法があります。

双方向の関係を保つ

本当に必要な場合は、関係を双方向のままにしておくことができますが、関係を壊したい場合は、両側で関係を適切に関連付けを解除する必要があります。「所有」側から完全に関係を管理するのが普通なので、次のようにします。

public class Ads implements Serializable {
    public void setAdCategoryId(AdCategories category) {
        this.category.removeAd(this);
        this.category = category;
        this.category.addAd(this);
    }
}

非常に大まかな擬似コードです。肉付けする必要があります

二項関係を取り除く

カテゴリは、そのカテゴリを使用するすべての広告のリストを維持する必要がありますか? 概念的には、そうすべきではないと思います。リストは時間の経過とともに非常に大きくなり、カテゴリごとに保存する代わりに、いつでも動的にクエリを実行できます。しかし、それはビジネスの観点から下さなければならない決定です。

于 2013-03-03T13:00:37.353 に答える