使用eclipselink引用继承的表需要手动刷新

时间:2018-10-12 11:01:30

标签: java sql jpa inheritance eclipselink

我们遇到的问题是,当使用eclipselink持久化实体时,持久化引用具有继承层次结构的其他表的表会遇到约束冲突。

请参阅以下实体:

Base类:

import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;


@lombok.Data
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "DTYPE")
@Entity
@Table(name="base")
public class Base implements Serializable {

  @Id
  @NotNull
  @Size(min = 1, max = 50)
  @Column(name = "name")
  private String name;

  @Size(max = 500)
  @Column(name = "description")
  private String description;

  public Base() {
  }

  public Base(final String name) {
    this.name= name;
  }

  public Base(final String name, final String description) {
    this.name= name;
    this.description= description;
  }
}

Target类继承自Base

import java.io.*;
import javax.persistence.*;

@lombok.Data
@lombok.ToString(callSuper = true)
@Entity
@Table(name="target")
public class Target extends Base implements Serializable {

  public Target() {
  }

  public Target(final String name) {
     super(name);
  }

  public Target(final String name, final String description) {
    super(name, description);
  }
}

Source类引用了Target

import java.io.*;
import javax.persistence.*;

@lombok.Data
@lombok.ToString
@Entity
@Table(name = "source")
public class Source implements Serializable {

  @Id 
  @SequenceGenerator(name = "source_id_seq_gen", sequenceName = "source_id_seq", allocationSize=1)
  @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "source_id_seq_gen")
  private long id; 

  /** The assigned ProjectAttribute. */
  @ManyToOne
  @JoinColumn(name="target")
  private Target target;

  protected Source() {
  }   

  public Source(final Target target) {
    this.target= target;
  }   
}

以及相应的数据库结构:

CREATE TABLE base (                                                                                                                                               
  dtype       VARCHAR(16),
  name        VARCHAR(50),
  description VARCHAR(500),
  PRIMARY KEY (name)
);                 

CREATE TABLE target (
  name        VARCHAR(50),
  PRIMARY KEY (name) ,
  FOREIGN KEY (name) REFERENCES base (name)
);                    

CREATE SEQUENCE baz_id_seq START WITH 1 INCREMENT BY 1;
CREATE TABLE baz (    
  id          BIGINT,
  name        VARCHAR(50),
  PRIMARY KEY (id)    
);                    

CREATE SEQUENCE source_id_seq START WITH 1 INCREMENT BY 1;
CREATE TABLE source (
  id                  BIGINT,
  baz                 BIGINT,
  target              VARCHAR(50),
  PRIMARY KEY (id),   
  FOREIGN KEY (baz)    REFERENCES baz    (id)   ON DELETE CASCADE,
  FOREIGN KEY (target) REFERENCES target (name) ON DELETE CASCADE
);                 

persistence.xml:

<?xml version="1.0" encoding="UTF-8"?>                            
<persistence version="2.2" 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_2.xsd">
  <persistence-unit name="myPU" transaction-type="RESOURCE_LOCAL">                      
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>                
    <!--provider>org.hibernate.jpa.HibernatePersistenceProvider</provider-->            
    <class>eu.herrn.eclipselink.entities.Target</class>           
    <class>eu.herrn.eclipselink.entities.Source</class>           
    <class>eu.herrn.eclipselink.entities.Baz</class>              
    <class>eu.herrn.eclipselink.entities.Base</class>             
    <properties>                                                  
      <property name="eclipselink.logging.parameters" value="true"/>                    
      <property name="javax.persistence.jdbc.url" value="jdbc:derby://localhost:1527/mydb"/>
      <property name="javax.persistence.jdbc.user" value="mydb"/> 
      <property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.ClientDriver"/>
      <property name="javax.persistence.jdbc.password" value="mydb"/>                                                                                             
    </properties>                                                 
  </persistence-unit>                                             
</persistence>         

和一个简单的测试类:

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

public class EclipseLinkTest {
  private static final EntityManagerFactory FACTORY= Persistence.createEntityManagerFactory("myPU");

  public EclipseLinkTest() {
  }   


  public static void main(String[] args) {
    final EntityManager em= FACTORY.createEntityManager();
    // cleanup db
    em.getTransaction().begin();
    em.createQuery("DELETE FROM Source s").executeUpdate();
    em.createQuery("DELETE FROM Target t").executeUpdate();
    em.createQuery("DELETE FROM Base b").executeUpdate();
    em.getTransaction().commit();

    em.getTransaction().begin();
    final Target pa1= new Target("target", "dummy Target");
    em.persist(pa1);

//    em.flush(); //this shouldn't be necessary

    final Source apa1= new Source(pa1);
    em.persist(apa1);
    em.getTransaction().commit();

    System.out.println("--- finished");

    FACTORY.close();
  }   
}

上面的代码因违反约束而失败:

 Internal Exception: org.apache.derby.shared.common.error.DerbySQLIntegrityConstraintViolationException: INSERT on table 'SOURCE' caused a violation of foreign key constraint 'SQL181012111707551' for key (target).  The statement has been rolled back.
Error Code: 0                                                                                                                                                                                                                                                            
Call: INSERT INTO source (ID, target) VALUES (?, ?)
  bind => [12, target]
Query: InsertObjectQuery(Source(id=12, target=Target(super=Base(name=target, description=dummy Target))))
Exception in thread "main" javax.persistence.RollbackException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.7.3.v20180807-4be1041): org.eclipse.persistence.exceptions.DatabaseException

激活上面示例中注释的em.flush()时,代码运行正常。

现在,违反约束的原因是什么?看来EclipseLink只是没有按正确的顺序持久化实体。 Base并因此Target具有未生成的ID,因此问题不在于持久时尚不知道的ID。但是即使那样,我仍然认为eclipselink会自行处理,因为一切都在同一事务中运行。

使用Hibernate作为持久性管理器时,不会发生此问题。摆脱Base类时也不会发生这种情况,因此似乎与这些表的继承有关。

这是EclipseLink中的错误吗?

0 个答案:

没有答案