【问题标题】:Detached entity error caused by cascade attribute级联属性导致的分离实体错误
【发布时间】:2016-11-28 17:12:15
【问题描述】:

我是 JPA 和 Hibernate 的新手。我有两个实体,供应商和产品,如下所述。

@Entity
public class Vendor extends BaseEntity
{
    private static final long serialVersionUID = 267250313080292374L;

    @NotNull
    private String name;

    @NotNull
    private String city;

    @OneToMany(mappedBy = "vendor", cascade = CascadeType.ALL)
    private List<Product> products = new ArrayList<Product>();

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public List<Product> getProducts() {
        return products;
    }

    public void setProducts(List<Product> products) {
        this.products = products;
    }
}

@Entity
public class Product extends BaseEntity
{
    private static final long serialVersionUID = -4676899182130380017L;

    @NotNull
    private String name;

    @Column(nullable = false, precision = 10, scale = 2)
    private double price;

    @NotNull
    private int quantity;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name="VENDOR_ID", nullable = false)
    private Vendor vendor;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getPrice() {
        return price;
    }

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

    public int getQuantity() {
        return quantity;
    }

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

    public Vendor getVendor() {
        return vendor;
    }

    public void setVendor(Vendor vendor) {
        this.vendor = vendor;
    }
}

我的问题与@ManyToOne注解中属性nullable = false的使用有关。我希望用@ManyToOne 注释的相应列不为空。但是,当我使用以下测试测试实体的持久性时:

public void testCreateEntity()
{

        // create vendor1, set name and city
        vendor1 = new Vendor();
        vendor1.setName("MediaMarkt");
        vendor1.setCity("Berlin");
        vendor1 = service.createOrUpdateEntity(vendor1);

        //  create product1, set name, price and quantity
        product1 = new Product();
        product1.setName("Samsung Galaxy S7");
        product1.setPrice(new Double("819.00"));
        product1.setQuantity(1);

        vendor1.getProducts().add(product1);
        product1.setVendor(vendor1);

        //  create product at service
        product1 = service.createOrUpdateEntity(product1);
...
}

createOrUpdateEntity 方法是 DAO Bean 对象的一部分

 @PersistenceContext
 private EntityManager em;

    public <T extends BaseEntity> T createOrUpdateEntity(T entity)
    {
        // as we have no id yet, it must be freshly brewed
        if (entity.getId() == 0)
        {
            log.debug("createOrUpdateEntity::create:: {}", entity);
            this.em.persist(entity);
        }
        // if there is an id, we must have dealt with it before
        else
        {
            log.debug("createOrUpdateEntity::update:: {}", entity);
            this.em.merge(entity);
        }
        this.em.flush();

        return entity;
    }

我得到错误:

Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: de.brockhaus.stock.entity.Stock
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:124)
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:765)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:758)
at org.hibernate.jpa.event.internal.core.JpaPersistEventListener$1.cascade(JpaPersistEventListener.java:80)
at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:398)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:323)
at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:162)
at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:111)
at org.hibernate.event.internal.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:425)
at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:249)
at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:178)
at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:121)
at org.hibernate.jpa.event.internal.core.JpaPersistEventListener.saveWithGeneratedId(JpaPersistEventListener.java:67)
at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:189)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:132)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:58)
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:775)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:748)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:753)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:1146)
... 138 more

编辑:实现这个example,它使用注释为nullable = false 的@M​​anyToOne 列,我意识到我的问题与此无关。如之前的服务器日志所示,问题是由 Cascade 类引起的。 SO中的一些solutions建议通过CascadeType.MERGE更改CascadeType.ALL。其他ones 显示不同的策略。甚至一些ones 告诉只有OneToMany 的关联方应该使用级联。坦率地说,我认为在关联的双方都使用 CascadeType.ALL 是最好的选择。但似乎我错了。尽管正如我所指出的,我是 JPA 和 Hibernate 的新手,但我不明白其中的原因。

【问题讨论】:

  • 能给我们看看createOrUpdateEntity(Object)方法的代码吗?
  • @ArthurNoseda 我已经添加了。
  • 您可能需要确保正确使用merge。它实际上在 PersistenceContext 中创建了一个新实例,并在不更改输入参数的情况下返回该实例。 persist 实际上使传递的参数成为托管的。
  • @Naros 我不明白你想告诉我什么。
  • docs.oracle.com/javaee/6/api/javax/persistence/… - 基本上你应该使用entity = this.em.merge(entity)

标签: java hibernate jpa


【解决方案1】:

您可以使用optional标签进行空控制:

@ManyToOne(cascade = CascadeType.ALL, optional = false)
@JoinColumn(name="VENDOR_ID")
private Vendor vendor;

【讨论】:

  • 虽然这可能是一种选择,但我很想知道nullable = false 的情况会发生什么。
【解决方案2】:

实际上,使用此代码,我可以让您的示例正常工作:

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>demo</groupId>
    <artifactId>hibernate4-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>hibernate4-demo</name>
    <url>http://maven.apache.org</url>

    <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <hibernate-core.version>4.3.11.Final</hibernate-core.version>
    </properties>

    <dependencies>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>${hibernate-core.version}</version>
    </dependency>
    <dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
        <version>1.1.0.Final</version>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-entitymanager</artifactId>
        <version>${hibernate-core.version}</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.6</version>
        <scope>runtime</scope>
    </dependency>
    </dependencies>
    <build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.5.1</version>
            <configuration>
                <source>1.7</source>
                <target>1.7</target>
            </configuration>
        </plugin>
    </plugins>
    </build>
</project>

BaseEntity.java(Brockhaus 的礼貌)

包demo.hibernate4;

import java.io.Serializable;
import java.util.Date;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Version;

@MappedSuperclass
public abstract class BaseEntity implements Serializable {

    @Version
    private long version;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @Temporal(TemporalType.DATE)
    private Date creationDate;

    public BaseEntity() {
    //lazy
    }

    public Date getCreationDate() {
    return creationDate;
    }

    public void setCreationDate(Date creationDate) {
    this.creationDate = creationDate;
    }

    public long getId() {
    return id;
    }

}

GenericDAOBean.java

package demo.hibernate4;

import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;

public class GenericDAOBean {

    private EntityManager em;

    @Override
    public <T extends BaseEntity> T createOrUpdateEntity(T entity) {
    if (entity.getId() == 0) {
        this.em.persist(entity);
    } else {
        this.em.merge(entity);
    }
    this.em.flush();

    return entity;
    }

    public void setEm(EntityManager em) {
        this.em = em;
    }

}

persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
  <persistence-unit name="hibernate4-demo" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <properties>
      <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/mysql?zeroDateTimeBehavior=convertToNull"/>
      <property name="javax.persistence.jdbc.password" value=""/>
      <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
      <property name="javax.persistence.jdbc.user" value="root"/>
      <property name="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider"/>
      <property name="javax.persistence.schema-generation.database.action" value="none"/>
    </properties>
  </persistence-unit>
</persistence>

Main.java

package demo.hibernate4;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

public class Main {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hibernate4-demo");
        EntityManager em = emf.createEntityManager();
        GenericDAOBean service = new GenericDAOBean();
        service.setEm(em);

        EntityTransaction transaction = em.getTransaction();
        transaction.begin();

        // create vendor1, set name and city
        Vendor vendor1 = new Vendor();
        vendor1.setName("MediaMarkt");
        vendor1.setCity("Berlin");
        vendor1 = service.createOrUpdateEntity(vendor1);

        //  create product1, set name, price and quantity
        Product product1 = new Product();
        product1.setName("Samsung Galaxy S7");
        product1.setPrice(new Double("819.00"));
        product1.setQuantity(1);

        vendor1.getProducts().add(product1);
        product1.setVendor(vendor1);

        //  create product at service
        product1 = service.createOrUpdateEntity(product1);
        transaction.commit();

        System.exit(0);
    }

}

使用此代码和您的映射,我在 MySQL 中获得了 2 行,并建立了 Product 和 Vendor 之间的关系。

但是,我不明白您想通过供应商密钥列表实现什么。目前,表 PRODUCT_VENDORKEY 保持为空。

您必须进一步描述您的上下文。

【讨论】:

  • 列表vendor keys的使用只是为了了解@ElementCollection注解是如何工作的。
  • 在我的解决方案中,我不使用任何 Transaction 对象,所以我不确定它是否会起作用,因为在您的主类中,用于持久化供应商和产品对象的代码与我的测试中的代码相同.
  • 您需要一种或另一种方式的交易。如果你使用容器,它可以由容器管理,如果你让 Spring 处理,它可以由 Spring 处理,或者这是你的责任。也许如果你展示了你的代码的最后一部分,我们可以深入了解这个?
  • 在persistence.xml文件中,我使用的是这个配置:&lt;persistence-unit name="stock_pu" transaction-type="JTA"&gt; &lt;provider&gt;org.hibernate.ejb.HibernatePersistence&lt;/provider&gt;
猜你喜欢
  • 2013-09-30
  • 2015-08-10
  • 2011-10-03
  • 2013-12-17
  • 2016-07-29
  • 2014-06-20
  • 1970-01-01
  • 2023-03-08
  • 1970-01-01
相关资源
最近更新 更多