org.hibernate.PersistentObjectException:永続化するために渡されたデタッチされたエンティティ


89

私は最初のマスターチャイルドの例をhibernateでうまく作成しました。数日後、私はそれを再び取り、いくつかのライブラリをアップグレードしました。何をしたのかわかりませんが、二度と実行させることはできませんでした。次のエラーメッセージを返すコードの何が問題なのかを誰かが理解するのを手伝ってくれるでしょうか。

org.hibernate.PersistentObjectException: detached entity passed to persist: example.forms.InvoiceItem
    at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:127)
    at org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:799)
    at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:791)
    .... (truncated)

Hibernateマッピング:

<hibernate-mapping package="example.forms">
    <class name="Invoice" table="Invoices">
        <id name="id" type="long">
            <generator class="native" />
        </id>
        <property name="invDate" type="timestamp" />
        <property name="customerId" type="int" />
        <set cascade="all" inverse="true" lazy="true" name="items" order-by="id">
            <key column="invoiceId" />
            <one-to-many class="InvoiceItem" />
        </set>
    </class>
    <class name="InvoiceItem" table="InvoiceItems">
        <id column="id" name="itemId" type="long">
            <generator class="native" />
        </id>
        <property name="productId" type="long" />
        <property name="packname" type="string" />
        <property name="quantity" type="int" />
        <property name="price" type="double" />
        <many-to-one class="example.forms.Invoice" column="invoiceId" name="invoice" not-null="true" />
    </class>
</hibernate-mapping>

編集: InvoiceManager.java

class InvoiceManager {

    public Long save(Invoice theInvoice) throws RemoteException {
        Session session = HbmUtils.getSessionFactory().getCurrentSession();
        Transaction tx = null;
        Long id = null;
        try {
            tx = session.beginTransaction();
            session.persist(theInvoice);
            tx.commit();
            id = theInvoice.getId();
        } catch (RuntimeException e) {
            if (tx != null)
                tx.rollback();
            e.printStackTrace();
            throw new RemoteException("Invoice could not be saved");
        } finally {
            if (session.isOpen())
                session.close();
        }
        return id;
    }

    public Invoice getInvoice(Long cid) throws RemoteException {
        Session session = HbmUtils.getSessionFactory().getCurrentSession();
        Transaction tx = null;
        Invoice theInvoice = null;
        try {
            tx = session.beginTransaction();
            Query q = session
                    .createQuery(
                            "from Invoice as invoice " +
                            "left join fetch invoice.items as invoiceItems " +
                            "where invoice.id = :id ")
                    .setReadOnly(true);
            q.setParameter("id", cid);
            theInvoice = (Invoice) q.uniqueResult();
            tx.commit();
        } catch (RuntimeException e) {
            tx.rollback();
        } finally {
            if (session.isOpen())
                session.close();
        }
        return theInvoice;
    }
}

Invoice.java

public class Invoice implements java.io.Serializable {

    private Long id;
    private Date invDate;
    private int customerId;
    private Set<InvoiceItem> items;

    public Long getId() {
        return id;
    }

    public Date getInvDate() {
        return invDate;
    }

    public int getCustomerId() {
        return customerId;
    }

    public Set<InvoiceItem> getItems() {
        return items;
    }

    void setId(Long id) {
        this.id = id;
    }

    void setInvDate(Date invDate) {
        this.invDate = invDate;
    }

    void setCustomerId(int customerId) {
        this.customerId = customerId;
    }

    void setItems(Set<InvoiceItem> items) {
        this.items = items;
    }
}

InvoiceItem.java

public class InvoiceItem implements java.io.Serializable {

    private Long itemId;
    private long productId;
    private String packname;
    private int quantity;
    private double price;
    private Invoice invoice;

    public Long getItemId() {
        return itemId;
    }

    public long getProductId() {
        return productId;
    }

    public String getPackname() {
        return packname;
    }

    public int getQuantity() {
        return quantity;
    }

    public double getPrice() {
        return price;
    }

    public Invoice getInvoice() {
        return invoice;
    }

    void setItemId(Long itemId) {
        this.itemId = itemId;
    }

    void setProductId(long productId) {
        this.productId = productId;
    }

    void setPackname(String packname) {
        this.packname = packname;
    }

    void setQuantity(int quantity) {
        this.quantity = quantity;
    }

    void setPrice(double price) {
        this.price = price;
    }

    void setInvoice(Invoice invoice) {
        this.invoice = invoice;
    }
}

編集:クライアントから送信されたJSONオブジェクト:

{"id":null,"customerId":3,"invDate":"2005-06-07T04:00:00.000Z","items":[
{"itemId":1,"productId":1,"quantity":10,"price":100},
{"itemId":2,"productId":2,"quantity":20,"price":200},
{"itemId":3,"productId":3,"quantity":30,"price":300}]}

編集:いくつかの詳細:
私は2つの方法で請求書を保存しようとしました:

  1. 上記のjsonオブジェクトを手動で作成し、サーバーの新しいセッションに渡しました。この場合、saveメソッドを呼び出す前にアクティビティがまったく行われていないため、saveメソッドで開かれたセッション以外に開いているセッションはありません。

  2. getInvoiceメソッドを使用して既存のデータをロードし、キー値を削除した後に同じデータを渡しました。これも、トランザクションがgetInvoiceメソッドでコミットされているため、保存する前にセッションを閉じる必要があると思います。

どちらの場合も、同じエラーメッセージが表示され、Hibernate構成ファイルまたはエンティティクラスまたはsaveメソッドのいずれかに問題があると思わせられます。

詳細をお知らせいただく必要がある場合はお知らせください

回答:


119

関連する詳細をあまり提供しなかったのでgetInvoice、呼び出した後、結果オブジェクトを使用していくつかの値を設定saveし、オブジェクトの変更が保存されることを前提として呼び出したと思います。

ただし、persist操作は新しい一時オブジェクトを対象としており、IDがすでに割り当てられている場合は失敗します。あなたの場合、おそらくのsaveOrUpdate代わりに電話をかけたいでしょうpersist

ここで、JPA / EJBコードを使用した「永続エラーに渡されたデタッチエンティティ」に関するいくつかの説明と参照を見つけることができます。


ありがとう@AlexGitelman。元の質問の最後にいくつかの詳細を追加しました。私の問題を理解するのに役立ちますか?または、他にどのような詳細が役立つか教えてください。
WSK 2011年

7
あなたの参照は私が愚かな間違いを見つけるのを助けました。子テーブルの主キーである「itemId」にnull以外の値を送信していました。したがって、休止状態は、オブジェクトがすでにいくつかのセッションに存在することを前提としていました。アドバイスありがとうございます
WSK 2011年

現在、次のエラーが発生しています: "org.hibernate.PropertyValueException:not-nullプロパティはnullまたは一時的な値を参照しています:example.forms.InvoiceItem.invoice"。ヒントを教えてください。よろしく
お願いします

請求書は一時的なものではなく、永続的な状態にする必要があります。つまり、IDはすでに割り当てられている必要があります。したがって、Invoice最初に保存して、IDを取得してから保存しInvoiceItemます。カスケードで遊ぶこともできます。
Alex Gitelman 2011年

13

ここでは、ネイティブを使用し、プライマリキーに値を割り当てています。ネイティブでは、プライマリキーが自動生成されます。

したがって、問題が発生しています。


1
すでに回答が受け入れられている質問に対して提供する追加情報があると思われる場合は、より実質的な説明を提供してください。
ChicagoRedSox 2014

8

これは@ManyToOneリレーションに存在します。CascadeType.PERSISTまたはCascadeType.ALLの代わりにCascadeType.MERGEを使用するだけで、この問題を解決しました。それがあなたを助けることを願っています。

@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name="updated_by", referencedColumnName = "id")
private Admin admin;

解決:

@ManyToOne(cascade = CascadeType.MERGE)
@JoinColumn(name="updated_by", referencedColumnName = "id")
private Admin admin;

4

おそらく、問題はここに表示しているコードの外にあります。現在のセッションに関連付けられていないオブジェクトを更新しようとしています。Invoiceでない場合は、すでに永続化され、dbから取得され、ある種のセッションで存続しているInvoiceItemである可能性があります。その後、新しいセッションで永続化しようとします。これは不可能です。原則として、永続化されたオブジェクトをセッション間で存続させないでください。

解決策は、つまり、永続化しようとしているのと同じセッションからオブジェクトグラフ全体を取得することです。Web環境では、これは次のことを意味します。

  • セッションを取得する
  • 更新または関連付けの追加が必要なオブジェクトをフェッチします。主キーが望ましい
  • 必要なものを変更する
  • 必要なものを保存/更新/削除/削除
  • セッション/トランザクションを閉じる/コミットする

問題が発生し続ける場合は、サービスを呼び出しているコードの一部を投稿してください。


@joostschoutenありがとうございます。どうやら、元の質問の下部に追加した「詳細」で述べたように、saveメソッドを呼び出す前に開いているセッションがあってはなりません。saveメソッドを呼び出す前にセッションが存在するかどうかを確認する方法はありますか?
WSK 2011年

「どうやらsaveメソッドを呼び出す前に開いているセッションがあってはならない」というあなたの仮定は間違っています。あなたの場合、各保存と取得の周りにトランザクションをラップしています。これは、開いているセッションが発生してはならず、それらが役に立たない場合を意味します。あなたの問題はあなたのJSONを処理するコードにあるようです。ここでは、すでに存在する(IDを持つ)請求書アイテムを含む請求書を渡します。null IDを付けて渡すと、おそらく機能します。または、JSONを処理するサービスにデータベースから請求書アイテムを取得させ、請求書に追加して、取得したのと同じセッションに保存します。
joostschouten 2011年

@joostschoutenこのエラーが発生しました: "org.hibernate.PropertyValueException:not-nullプロパティはnullまたは一時的な値を参照しています:example.forms.InvoiceItem.invoice"。アイデアをいただけませんか。よろしく
お願いします

1
これは私には新しい質問のように聞こえます。あなたは私たちと重要なコードを共有していません。JSONを処理し、モデルオブジェクトを生成し、呼び出しを永続化して保存するコード。この例外は、請求書がnullのinvoiceItemを永続化しようとしていることを示しています。当然のことながらできません。モデルオブジェクトを実際にビルドするコードを投稿してください。
joostschouten 2011年

@joostschoutenこれは私には理にかなっていますが、問題は、JSONにフレームワーク「qooxdoo」を使用していて、同じフレームワークのRPCサーバーユーティリティがインストールされているサーバーへのRPC呼び出しを作成していることです。したがって、すべてがフレームワーククラスにラップされます。何千行も抽出して投稿するのは現実的ではないかもしれません。一方、作成されたサーバー側の「theInvoice」オブジェクトを見ることができますか?または休止状態のデバッグ/トレース情報を表示することによって?
WSK 2011年

2

2つの解決策1.オブジェクトを更新する場合はmergeを使用します2.新しいオブジェクトを保存するだけの場合はsaveを使用します(休止状態またはデータベースで生成できるようにIDがnullであることを確認してください)3。
@ OneToOne(のようなマッピングを使用している場合fetch = FetchType.EAGER、cascade = CascadeType.ALL)@JoinColumn(name = "stock_id")

次に、CascadeType.ALLをCascadeType.MERGEに使用します

ありがとうShahidAbbasi


0

persist()の代わりにEntityManager merge()を使用して修正されたJPAの場合

EntityManager em = getEntityManager();
    try {
        em.getTransaction().begin();
        em.merge(fieldValue);
        em.getTransaction().commit();
    } catch (Exception e) {
        //do smthng
    } finally {
        em.close();
    }

0

私が書いていたので私は「同じ」問題を抱えていました

@GeneratedValue(strategy = GenerationType.IDENTITY)

現時点では必要ないため、その行を削除しました。オブジェクトなどでテストしていました。<generator class="native" />あなたの場合だと思います

私はコントローラーを持っておらず、APIにアクセスしていません。これは、テスト専用です(現時点では)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.