当前位置: 首页 > 知识库问答 >
问题:

级联属性导致的分离实体错误

柯轶
2023-03-14

我是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

编辑:实现此示例,该示例使用注释为 null 可为 null = false 的@ManyToOne列,我意识到我的问题与此无关。如前面的服务器日志所示,问题是由级联类引起的。SO 中的一些解决方案建议通过级联类型.MERGE 来更改级联类型.ALL。其他的则展示了不同的策略。甚至有些人也说,只有协会的OneToMany端才应该使用级联。坦率地说,我认为在协会的双方使用 CascadeType.ALL 是最好的选择。但看来我错了。虽然,正如我所指出的,我是JPA和冬眠的新手,我不明白其中的道理。

共有2个答案

梁丘兴腾
2023-03-14

实际上,通过这段代码,我可以让您的示例正常工作:

砰.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>

基地实体.java(布罗克豪斯的阴谋)

包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>

主类

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_VENDORKEY仍然是空的。

你必须进一步描述你的背景。

年良骏
2023-03-14

您可以使用可选标记进行空控制:

@ManyToOne(cascade = CascadeType.ALL, optional = false)
@JoinColumn(name="VENDOR_ID")
private Vendor vendor;
 类似资料:
  • 我有两个实体类Organization和User具有一对多关系(为了简单起见,Groovy代码!): 当我为一个新的组织创建一个新的用户时,一切都运行良好: 但当我使用一个现有的组织,将其设置为新用户的组织属性时,我会得到一个错误: 将导致: org.hibernate.PersistentObjectException:传递给Persist:Organization的分离实体 我知道不能对一个分

  • 关联实体和关联关系属性有什么区别?在我的一本名为《现代数据库管理》(Hoffer,第11版)的书中,作者陈述了两者之间的区别。然而,它并没有真正解释为什么会有差别,相反,它只是给出了它们是如何不同的例子。 据我所知,一个有一个属性关联的关系是一个关联关系属性,并用一条虚线表示一个圆角矩形,该矩形内有该属性。而关联实体是描述关系的多个属性。两者都只能用于ER图解中的多对多关系。我的思维过程正确吗?

  • 问题内容: “独立实体”是什么意思?在交易期间如何将托管实体转换为独立实体? 问题答案: 分离实体是状态不能由JPA提供程序反映的实体。 换句话说,如果您更改其状态(即通过setter方法),则这些更改将不会保存到基础数据库中,因为JPA提供程序不必“观察”此类实体。 如果实体E1是受管实体,则可以使其分离调用(非常合理的命名)方法。您还可以使用它将清除整个PersistenceContext并有

  • 将只填充Enabled属性(下图),而不填充Application或CustomVariableGroup属性(下图)。我该怎么做?

  • 问题内容: 我有以下代码: 这会在第四行引发IllegalArgumentException,提示“实体未管理”。如果我将第三行改为而不是,那么一切似乎都可以正常工作。 这里发生了什么?我可以防止这种情况发生吗? 更新: @DataNucleus将我引向PersistenceContext。如何在代码中更改持久性上下文? 问题答案: 根据Eval 2.0 Eval的JSR-000317持久性规范: