org.hibernate.PersistentObjectException : 분리 된 엔티티가 지속되도록 전달되었습니다.


89

최대 절전 모드로 첫 번째 마스터 자식 예제를 성공적으로 작성했습니다. 며칠 후 다시 가져 와서 일부 라이브러리를 업그레이드했습니다. 내가 뭘했는지는 모르겠지만 다시는 실행할 수 없었습니다. 누군가 다음 오류 메시지를 반환하는 코드에서 무엇이 잘못되었는지 알아낼 수 있습니까?

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-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}]}

편집 : 일부 세부 정보 :
다음 두 가지 방법으로 송장을 저장하려고했습니다.

  1. 위에서 언급 한 json 객체를 수동으로 제작하여 서버의 새로운 세션에 전달했습니다. 이 경우 save 메소드를 호출하기 전에 아무런 활동도하지 않았으므로 save 메소드에서 열린 세션을 제외하고는 열린 세션이 없어야합니다.

  2. getInvoice 메소드를 사용하여 기존 데이터를로드하고 키 값을 제거한 후 동일한 데이터를 전달했습니다. 이것도 트랜잭션이 getInvoice 메소드에서 커밋되므로 저장하기 전에 세션을 닫아야한다고 생각합니다.

두 경우 모두 최대 절전 모드 구성 파일이나 엔티티 클래스 또는 저장 방법에 문제가 있다고 믿도록하는 동일한 오류 메시지가 표시됩니다.

더 자세한 정보를 제공해야하는지 알려주세요.

답변:


119

관련 세부 정보를 많이 제공하지 않았으므로 호출 getInvoice한 다음 결과 개체를 사용하여 일부 값을 설정하고 save개체 변경 사항이 저장된다는 가정하에 호출 했습니다.

그러나 persist작업은 새로운 임시 개체를위한 것이며 id가 이미 할당되어 있으면 실패합니다. 귀하의 경우에는 saveOrUpdate대신 persist.

JPA / EJB 코드와 함께 "오류를 지속하기 위해 전달 된 분리 된 엔티티"에서 몇 가지 논의 및 참조를 찾을 수 있습니다.


@Alex Gitelman 감사합니다. 원래 질문 하단에 몇 가지 세부 정보를 추가했습니다. 내 문제를 이해하는 데 도움이됩니까? 또는 다른 세부 정보가 도움이 될 수 있는지 알려주세요.
WSK

7
당신의 참조는 내가 어리석은 실수를 찾는 데 도움이되었습니다. 자식 테이블의 기본 키인 "itemId"에 대해 null 값이 아닙니다. 그래서 최대 절전 모드는 객체가 이미 어떤 세션에 존재한다고 가정했습니다. 조언 감사합니다
WSK

이제 "org.hibernate.PropertyValueException : not-null 속성이 null 또는 일시적인 값을 참조합니다 : example.forms.InvoiceItem.invoice"오류가 발생합니다. 힌트 좀주세요. 미리 감사드립니다
WSK

인보이스가 일시적이 아닌 지속 된 상태 여야합니다. 즉, ID가 이미 할당되어 있어야합니다. 그래서 Invoice먼저 저장 하면 id를 얻은 다음 저장 InvoiceItem합니다. 계단식으로 재생할 수도 있습니다.
Alex Gitelman

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

대부분의 경우 문제는 여기에 표시되는 코드 외부에 있습니다. 현재 세션과 연결되지 않은 개체를 업데이트하려고합니다. 인보이스가 아니라면 이미 유지되고 db에서 얻은 InvoiceItem 일 수 있으며 일종의 세션에서 살아남은 다음 새 세션에서 유지하려고합니다. 이건 불가능 해. 일반적으로 세션간에 지속 된 개체를 유지하지 마십시오.

솔루션은 즉, 유지하려는 동일한 세션에서 전체 개체 그래프를 얻는 것입니다. 웹 환경에서 이것은 다음을 의미합니다.

  • 세션 얻기
  • 연결을 업데이트하거나 추가해야하는 개체를 가져옵니다. 기본 키에 의한 선호
  • 필요한 사항 변경
  • 원하는 것을 저장 / 업데이트 / 제거 / 삭제
  • 세션 / 트랜잭션 닫기 / 커밋

계속 문제가 발생하면 서비스를 호출하는 일부 코드를 게시하십시오.


@joostschouten 감사합니다. 내가 원래 질문 하단에 추가 한 "상세 정보"에서 언급했듯이 저장 메서드를 호출하기 전에 세션이 열려 있으면 안됩니다. 저장 메서드를 호출하기 전에 세션이 있는지 확인할 수있는 방법이 있습니까?
WSK

"분명히 저장 메서드를 호출하기 전에 열려있는 세션이 없어야합니다"라는 가정이 잘못되었습니다. 귀하의 경우에는 각 저장 및 가져 오기에 대한 트랜잭션을 래핑하고 있으므로 열린 세션이 발생하지 않아야하며 사용되지 않을 것입니다. JSON을 처리하는 코드에 문제가있는 것 같습니다. 여기에서 이미 존재하는 (ID가있는) 송장 항목이있는 송장을 전달합니다. null ID로 전달하면 대부분 작동합니다. 또는 JSON을 처리하는 서비스에서 db에서 청구서 항목을 가져 와서 청구서에 추가하고 가져온 것과 동일한 세션에 저장하십시오.
joostschouten 2011-06-17

@joostschouten 이제 "org.hibernate.PropertyValueException : not-null 속성이 null 또는 일시적인 값을 참조합니다 : example.forms.InvoiceItem.invoice"라는 오류가 발생합니다. 제게 아이디어를 주시겠습니까? 미리 감사드립니다
WSK 2011-06-17

1
이것은 나에게 새로운 질문처럼 들립니다. 중요한 코드를 공유하지 않았습니다. JSON을 처리하고 모델 객체를 생성하고 호출하는 코드는 지속되고 저장됩니다. 이 예외는 null 인보이스가있는 invoiceItem을 유지하려고한다는 것을 알려줍니다. 정당하게 할 수없는 것. 실제로 모델 객체를 빌드하는 코드를 게시하세요.
joostschouten

@joostschouten 이것은 나에게 의미가 있지만 문제는 JSON 용 프레임 워크 "qooxdoo"를 사용하고 동일한 프레임 워크의 RPC 서버 유틸리티가 설치된 서버에 RPC 호출을 생성하고 있다는 것입니다. 따라서 모든 것이 프레임 워크 클래스로 래핑됩니다. 수천 줄을 추출하여 게시하는 것은 실용적이지 않을 수 있습니다. 반면에 생성 된 서버 측에서 "theInvoice"개체를 볼 수 있습니까? 또는 최대 절전 모드 디버그 / 추적 정보를 표시하여?
WSK

2

두 가지 솔루션 1. 개체를 업데이트하려면 병합을 사용합니다. 2. 새 개체를 저장하려면 save를 사용합니다 (최대 절전 모드 또는 데이터베이스에서 생성 할 수 있도록 ID가 null인지 확인) 3.
@OneToOne ( 과 같은 매핑을 사용하는 경우) fetch = FetchType.EAGER, cascade = CascadeType.ALL) @JoinColumn (name = "stock_id")

그런 다음 CascadeType.ALL을 CascadeType.MERGE에 사용하십시오.

감사합니다 Shahid Abbasi


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.