17

孤立した子オブジェクトを削除することは、SO に関するよくある質問であり、Hibernate を初めて使用する人にとってよくある問題であり、かなり標準的な答えは、または子コレクションに何らかのバリエーションがあることを確認することであることを知っていcascade=all,delete-orphanますcascade=all-delete-orphan

子コレクションが親オブジェクトから空/削除されたことをHibernateに検出させ、親オブジェクトが更新されたときに子テーブルの行をデータベースから削除できるようにしたいと思います。例えば:

Parent parent = session.get(...);
parent.getChildren().clear();
session.update(parent);

Parentクラスの現在のマッピングは次のようになります。

<bag name="children" cascade="all-delete-orphan">
    <key column="parent_id" foreign-key="fk_parent_id"/>
    <one-to-many class="Child"/>
</bag>

アタッチされたオブジェクトを更新する場合、これはうまく機能しますが、デタッチされたオブジェクト (HTTP/JSON を介してリモート クライアントによって API メソッドに送信されたもの) を取得できるようにしたいユース ケースがあります。そしてそれを Hibernate Session に直接渡します - クライアントが好きな方法で親オブジェクトを操作し、変更を永続化できるようにします。

デタッチされたオブジェクトを呼び出すsession.update(parent)と、子テーブルの行は孤立しますが (FK 列は null に設定されます)、削除されません。を呼び出しているsession.update()とき、Hibernate セッションがこのオブジェクト インスタンスを確認するのはこれが初めてであることに注意してください。他の方法でオブジェクトをセッションに再アタッチしたり、マージしたりすることはありません。私はクライアントに依存して、データベース内の実際のオブジェクトに対応する識別子を持つオブジェクトを渡します。たとえば、API サービス メソッドのロジックは次のようになります。

String jsonString = request.getParameter(...);
Parent parent = deserialize(jsonString);
session.update(parent);

Hibernate が に渡されたときに、切り離された親オブジェクトの孤立した子コレクションを検出することは可能session.update(parent)ですか? または、切り離されたオブジェクトを何らかの方法で誤用していますか?

私の望みは、切り離されたインスタンスへの変更を保持するために、Hibernate とのあらゆる種類の複雑な対話を回避できることでした。私の API メソッドは、 への呼び出し後にデタッチされたオブジェクトをさらに変更する必要はありませんsession.update(parent)。このメソッドは、リモート クライアント アプリケーションによって行われた変更を保持するだけです。

4

2 に答える 2

8

マッピング (簡略化)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="br.com._3988215.model.domain">
    <class name="Parent" table="PARENT">
        <id name="id">
            <generator class="native"/>
        </id>
        <bag cascade="all,delete-orphan" name="childList">
            <key column="PARENT_ID" not-null="false"/>
            <one-to-many class="Child"/>
        </bag>
    </class>
    <class name="Child" table="CHILD">
        <id name="id" column="CHILD_ID">
            <generator class="native"/>
        </id>
    </class>
</hibernate-mapping>

生産する

PARENT
    ID

CHILD
    CHILD_ID
    PARENT_ID

あなたの言ったことによると

子コレクションが親オブジェクトから削除されたことを Hibernate に検出させ、親オブジェクトが更新されたときに子テーブルの行をデータベースから削除できるようにしたいと考えています。

何かのようなもの

Parent parent = session.get(...);
parent.getChildren().clear();

session.update(parent);

親インスタンスがアタッチされているため、正常に機能すると言いました

次に、次のものを見てみましょう (通知Assert.assertNull(second) )

public class WhatYouWantTest {

    private static SessionFactory sessionFactory;

    private Serializable parentId;

    private Serializable firstId;
    private Serializable secondId;

    @BeforeClass
    public static void setUpClass() {
        Configuration c = new Configuration();
        c.addResource("mapping.hbm.3988215.xml");

        sessionFactory = c.configure().buildSessionFactory();
    }

    @Before
    public void setUp() throws Exception {
        Parent parent = new Parent();
        Child first   = new Child();
        Child second  = new Child();

        Session session = sessionFactory.openSession();
        session.beginTransaction();

        parentId = session.save(parent);
        firstId  = session.save(first);
        secondId = session.save(second);

        parent.getChildList().add(first);
        parent.getChildList().add(second);

        session.getTransaction().commit();
        session.close();
    }

    @Test
    public void removed_second_from_parent_remove_second_from_database() {
        Parent parent = new Parent();
        parent.setId((Integer) parentId);

        Child first = new Child();
        first.setId((Integer) firstId);

        /**
          * It simulates the second one has been removed
          */
        parent.getChildList().add(first);

        Session session = sessionFactory.openSession();
        session.beginTransaction();

        session.update(parent);

        session.getTransaction().commit();
        session.close();

        session = sessionFactory.openSession();
        session.beginTransaction();

        Child second = (Child) session.get(Child.class, secondId);
        Assert.assertNull(second);

        session.getTransaction().commit();
        session.close();
    }
}

残念ながら、テストはパスしません。あなたにできること???

  • 長時間の会話を有効にする

休止状態の参照は言う

拡張 (または長い) セッション - Hibernate セッションは、データベース トランザクションがコミットされた後に基礎となる JDBC 接続から切断され、新しいクライアント リクエストが発生したときに再接続される場合があります。このパターンは会話ごとのセッションとして知られており、再接続さえ不要になります。自動バージョン管理は、同時変更を分離するために使用され、セッションは通常、自動的にフラッシュされることは許可されていませんが、明示的にフラッシュされます。

免責事項:長時間の会話を使用するシナリオはありません。Java EE ステートフル セッション Bean は長時間実行される会話をサポートします。ただし、そのサポートはJPA(Hibernateではありません)用です

または、子を複合要素として有効にする代替マッピングを作成できます。そのライフサイクルは親オブジェクトに依存するため、複合要素に依存して必要なものを取得できます

Parent を拡張する AlternativeParent という名前のクラスを作成します

public class AlternativeParent extends Parent {}

現在はマッピングされています (プレーンな @Entity ではなく複合要素としての子に注意してください)

<class name="AlternativeParent" table="PARENT">
    <id name="id">
        <generator class="native"/>
    </id>
    <bag name="childList" table="CHILD">
        <key column="PARENT_ID" not-null="false"/>
        <composite-element class="Child">
            <property column="CHILD_ID" name="id"/>
        </composite-element>
    </bag>
</class>

Child クラスに便利な equals メソッドを実装するようになりました

public boolean equals(Object o) {
    if (!(o instanceof Child))
        return false;

    Child other = (Child) o;
    // identity equality
    // Used by composite elements
    if(getId() != null) {
        return new EqualsBuilder()
                   .append(getId(), other.getId())
                   .isEquals();
    } else {
        // object equality
     }
}

上記のテスト ケースをリファクタリングすると (代わりに AlternativeParent を使用して)

@Test
public void removed_second_from_parent_remove_second_from_database() {
    AlternativeParent parent = new AlternativeParent();
    parent.setId((Integer) parentId);

    Child first = new Child();
    first.setId((Integer) firstId);

    /**
      * It simulates the second one has been removed
      */
    parent.getChildList().add(first);

    Session session = sessionFactory.openSession();
    session.beginTransaction();

    session.update(parent);

    session.getTransaction().commit();
    session.close();

    session = sessionFactory.openSession();
    session.beginTransaction();

    Child second = (Child) session.get(Child.class, secondId);
    Assert.assertNull(second);

    session.getTransaction().commit();
    session.close();

}

緑色のバーが表示されます

于 2010-10-30T19:54:57.200 に答える
1

切り離されたセッションを使用すると、コレクションで問題が発生する可能性があると思います。最初にエンティティをコレクションでロードしてから、そのエンティティを変更で更新することをお勧めします。

于 2010-10-27T04:52:56.297 に答える