у нас проблема в том, что при сохранении сущностей с помощью 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();
}
}
Приведенный выше код завершается с нарушением Contraint:
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
имеют не сгенерированный идентификатор, поэтому проблема заключается не в идентификаторе, который еще не известен во время сохранения.Но даже тогда я предположил бы, что eclipselink обрабатывает это сам, потому что все выполняется в одной транзакции.
Проблема не возникает при использовании Hibernate в качестве диспетчера персистентности.Это также не происходит при избавлении от класса Base
, поэтому, похоже, это немного связано с наследованием этих таблиц.
Это ошибка в EclipseLink?