3

親と子の間に2つのテーブルがあり、それらの間に@oneTomany関係があります。以下は私のテーブル構造です。

テーブル構造

CREATE TABLE `parent` (
  `id_parent` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id_parent`)
)

CREATE TABLE `child` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `parent_id` int(3) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `fk_parent_child` (`group_id`),
  CONSTRAINT `fk_parent_child` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id_parent`)
)

このためのエンティティクラスを次のように作成しました

親クラス。

@Entity
@Table(name = "parent")
public class Parent {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id_parent")
    private int id;

    @Column(name = "name")
    private String name;

    @OneToMany(mappedBy = "defaultchild", fetch = FetchType.EAGER)
    @Cascade({ CascadeType.SAVE_UPDATE, CascadeType.MERGE })
    private Set<Child> childs;

    //setter and getters.
}

子クラス。

@Entity
@Table (name = "child")
public class Report {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column (name = "id")
    private int id;
    @Column (name = "name")
    private String name;

    @ManyToOne
    @Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE,org.hibernate.annotations.CascadeType.MERGE})
    @JoinColumn(name="parent_id")
    private Parent defaultchild;    

    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(name = "group",joinColumns={@JoinColumn(name="fk_child_id")},inverseJoinColumns={@JoinColumn(name="fk_group_id")})
    private Set<XXX> groups = new HashSet<XXX>(0);
    //Setter and Getter methods
}

サービス クラス

public UIGroup getParentByName(String name) {
    return DAO.getParentByName(name);
}

@Transactional(readOnly = false, propagation = Propagation.REQUIRED)
public boolean updateParent(Parent parent) {
    return DAO.updateParent(parent);
}


@Transactional(readOnly = false, propagation = Propagation.REQUIRED)
public boolean deleteParent(Parent parent) {
    return DAO.deleteParent(parent);
}

DAO クラス。

public UIGroup getParentByName(String name) {
    Query query;
    Parent parent = null;
    try {
        String queryString = " from Parent where name = :name";
        query = sessionFactory.getCurrentSession().createQuery(queryString);
        query.setParameter("name", name);
        uiGroup = (Parent) query.uniqueResult();
    } catch (Exception e) {
        logger.error(e);
    }
    return parent;
}

public boolean updateParent(Parent parent) {
    boolean result = true;
    Session session = null;
    Transaction tx = null;
    try {
        session = sessionFactory.getCurrentSession();
        tx = session.beginTransaction();
        session.merge(parent);
        tx.commit();
        session.flush();
    } catch (HibernateException e) {
        result = false;
        logger.error(e);
    }// end of try-catch block.
    return result;
}


public boolean deleteParent(Parent parent) {
    boolean result = true;
    Session session = null;
    try {
        session = sessionFactory.getCurrentSession();
        session.delete(parent);
    } catch (HibernateException e) {
        result = false;
        logger.error( + e);
    }
    return result;
}

しかし、次のコードを呼び出そうとすると

Parent otherParent = Service.getParentByName("Other");
Parent parent = Service.getParentByName("XYZ");
//here I am assigning childs assign to XYX parent to other Parent
Set<Child> childs = new TreeSet<Child>(Child.COMPARE_BY_ID);
childs.addAll(otherParent.getchildes());
childs.addAll(parent.getchilde());
otherParent.setChilds(childs);
Service.updateParent(otherParent);
Service.deleteParent(parent);

次のエラーが発生します。

エラー

java.sql.BatchUpdateException: Cannot delete or update a parent row: a foreign key constraint fails (`database`.`child`, CONSTRAINT `fk_parent_child` FOREIGN KEY (`parent_id`) REFERENCES `child` (`id_parent`))

これは、Service.updateParent(otherParent) ステートメントのログに続いて、更新コードが正しく機能していないことを意味します

SELECT parent0_.id_parent AS id1_135_1_, parent0_.name AS name135_1_, childs1_.parent_id AS parent4_3_, childs1_.id AS id3_, childs1_.id AS id143_0_, childs1_.child_name AS child2_143_0_, childs1_.is_sea_child AS is3_143_0_, childs1_.parent_id AS parent4_143_0_ 
FROM ui_parent parent0_ 
LEFT OUTER JOIN child childs1_ ON parent0_.id_ui_parent=childs1_.parent_id 
WHERE parent0_.id_parent=1

このコードで何が問題になったのかわかりません。事前に感謝します。

4

4 に答える 4

3

おそらく、DAO メソッドの moveAllChildren(String srcName, String dstName) を次のように記述します。

public class ParentDAO {
    @PersistenceContext
    private EntityManager em;

    public Parent findParentByName(name) {
      TypedQuery<Parent> q = em.createQuery("select p from Parent where p.name = :name", Parent.class);
      return q.setParameter("name", name).getSingleResult();
    }

    public void moveAllChildren(String srcName, String dstName) {
      Parent src = findParentByName(srcName);
      Parent dst = findParentByName(dstName);
      Set<Child> children = new HashSet<Child>();
      for (Child c: src.getChildren()) {
        children.add(c);
      }
      src.getChildren().removeAll(children);
      dst.getChildren().addAll(children);
    }
}

一般に、カスケードを使用する場合は、dst.setChildren(allChildren) と言うよりも、子を明示的に追加および削除して、JPA に関係の両側を管理する機会を与えることをお勧めします。そうしないと、子供がまだ src を自分の親だと思っている可能性があり、データベースからの制約違反が発生する可能性があります。

さらに、可能な限り多くのエンティティーを JPA で管理できるようにすることをお勧めします。したがって、アプリケーションで DAO を呼び出して親を取得し、子を移動するためだけにサービスを呼び出してから、同じサービスを呼び出して同じ DAO を呼び出して、これらの変更をデータベースにマージする必要はありません。これを DAO レベルの低レベル操作として実装し、それを呼び出して @Transaction を処理するサービスを追加することをお勧めします。

于 2013-03-08T15:55:47.743 に答える
1

タイプを制限することを選択した特定の理由がない場合は、@CascadeALL に切り替えることを選択できます。

  @Cascade({ CascadeType.ALL})

または単に

  @OneToMany(cascade=CascadeType.ALL, mappedBy="defaultchild")

編集:「親」のスペルを数回間違えましたが、それを修正しても問題はありません。

于 2013-03-07T13:05:08.653 に答える
0

削除しようとしていますが、おそらくparentまだ削除されています。削除する前にコレクションchildsを空にしてみてください。childs

for(Child child : parent.getChilds()){
    child.setParent(null);
}
parent.getChilds().clear();
Service.deleteParent(parent);

parentまだ参照がある場合は削除できませんChild

于 2013-03-08T12:14:32.890 に答える
0

最後に、wallenborn と次のリンクの助けを借りて解決策を得ました

JPA が外部キーを @OneToMany 関係に保存しない

これが私のクラスで行った変更です。

エンティティ クラス。

@Entity
@Table(name = "parent")
public class Parent {

    .....   

    @OneToMany(cascade = CascadeType.PERSIST,mappedBy = "defaultchild", fetch = FetchType.EAGER)
    private Set<Child> childs;

    //setter and getters.
}

@Entity
@Table (name = "child")
public class Child {

    ....

    @ManyToOne
    @JoinColumn(name="parent_id")
    private Child defaultchild; 

    //Setter and Getter methods
}

次のように、wallenborn によって提案されたサービスと DAO クラスを変更しました。

サービス クラス。

@Transactional(readOnly = false, propagation = Propagation.REQUIRED)
public void moveAllChildren(String srcName, String dstName) {
    DAO.moveAllChildren(srcName, dstName);
} 

DAO クラス

public Parent findParentByName(name) {
      TypedQuery<Parent> q = em.createQuery("select p from Parent where p.name = :name", Parent.class);
      return q.setParameter("name", name).getSingleResult();
    }

    public void moveAllChildren(String srcName, String dstName) {
      Parent src = findParentByName(srcName);
      Parent dst = findParentByName(dstName);
      Set<Child> children = new HashSet<Child>();
      for (Child c: src.getChildren()) {
        children.add(c);
    }
    src.getChildren().removeAll(children);
    dst.getChildren().addAll(children);
}
于 2013-03-12T07:33:43.330 に答える