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



我是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注释的相应列不为null。然而,当我用以下测试来测试实体的持久性时:

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

EDIT:实现这个例子时,使用了一个注释为nullable=false的@ManyToOne列,我意识到我的问题与此无关。如前一个服务器日志中所示,问题是由Cascade类引起的。SO中的一些解决方案建议更改CascadeType。全部按级联类型。合并。其他的则显示出不同的策略。甚至有些人说,只有关联的OneToMany方应该使用级联。坦率地说,我认为使用CascadeType。协会双方的ALL是最好的选择。但我好像错了。尽管,正如我所指出的,我是JPA和Hibernate的新手,我不理解其中的原因。

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

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

事实上,有了这段代码,我可以让你的例子发挥作用:

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_VENDORKEY保持为空。

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

最新更新