我有两个类:Department和Employee,以及它们之间的一对多关系。一个部门可以有多个员工,一个员工属于一个部门。当我想使用JPA 2.1 RI(EclipseLink 2.5.0)将它们持久化到(MySQL-)数据库时,它可以正常工作。但是,当我将它们作为子类(Department扩展SuperClassOne和Employee扩展SuperClassTwo)由于某种原因然后我想将它们保存到数据库中时,我得到了一个例外。似乎发送到RDBMS的insert-statements的顺序并不好。我做错了什么?
SQL DDL:
CREATE TABLE SuperClassOne (
id INTEGER NOT NULL AUTO_INCREMENT,
type VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8 DEFAULT COLLATE = utf8_bin;
CREATE TABLE Department (
id INTEGER NOT NULL,
name VARCHAR(255) NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (id) REFERENCES SuperClassOne (id) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8 DEFAULT COLLATE = utf8_bin;
CREATE TABLE SuperClassTwo (
id INTEGER NOT NULL AUTO_INCREMENT,
type VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8 DEFAULT COLLATE = utf8_bin;
CREATE TABLE Employee (
id INTEGER NOT NULL,
name VARCHAR(255) NOT NULL,
departmentId INTEGER NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (id) REFERENCES SuperClassTwo (id) ON DELETE RESTRICT ON UPDATE RESTRICT,
FOREIGN KEY (departmentId) REFERENCES Department (id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8 DEFAULT COLLATE = utf8_bin;
班级部门:
@Entity
@Table(name = "Department")
public class Department extends SuperClassOne {
// @Id
// @GeneratedValue(strategy = GenerationType.IDENTITY)
// @Column(name = "id")
// private Integer id;
@Column(name = "name")
private String name;
@OneToMany(mappedBy = "department", cascade = CascadeType.PERSIST)
private List<Employee> employees = new ArrayList<>();
public Department() {
super();
}
// public Integer getId() {
// return id;
// }
//
// public void setId(Integer id) {
// this.id = id;
// }
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Employee> getEmployees() {
return employees;
}
public void setEmployees(List<Employee> employees) {
this.employees = employees;
}
public void addEmployee(Employee employee) {
employee.setDepartment(this);
employees.add(employee);
}
}
班级员工:
@Entity
@Table(name = "Employee")
public class Employee extends SuperClassTwo {
// @Id
// @GeneratedValue(strategy = GenerationType.IDENTITY)
// @Column(name = "id")
// private Integer id;
@Column(name = "name")
private String name;
@ManyToOne
@JoinColumn(name = "departmentId")
private Department department;
public Employee() {
super();
}
// public Integer getId() {
// return id;
// }
// public void setId(Integer id) {
// this.id = id;
// }
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = department;
}
}
类SuperClassOne:
@Entity
@Table(name = "SuperClassOne")
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "type")
// @DiscriminatorValue(value = "SuperClassOne")
public class SuperClassOne {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Integer id;
@Column(name = "type", nullable = false)
private String type;
public SuperClassOne() {
super();
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
类SuperclassTwo:
@Entity
@Table(name = "SuperClassTwo")
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "type")
// @DiscriminatorValue(value = "SuperclassTwo")
public class SuperClassTwo {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Integer id;
@Column(name = "type", nullable = false)
private String type;
public SuperclassTwo() {
super();
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
类主要:
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("test-PU");
EntityManager em = emf.createEntityManager();
// ----------
Department department = new Department();
department.setName("Bla");
Employee employee = new Employee();
employee.setName("Marc Beckers");
department.addEmployee(employee);
EntityTransaction et = em.getTransaction();
et.begin();
em.persist(department);
// em.persist(employee);
et.commit();
// ----------
em.close();
emf.close();
}
错误:
[EL Warning]: 2014-01-27 13:13:01.546--UnitOfWork(1647452011)--Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.0.v20130507-3faac2b): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails (`test`.`employee`, CONSTRAINT `employee_ibfk_1` FOREIGN KEY (`departmentId`) REFERENCES `department` (`id`) ON DELETE CASCADE ON UPDATE CASCADE)
Error Code: 1452
Call: INSERT INTO Employee (name, departmentId, id) VALUES (?, ?, ?)
bind => [3 parameters bound]
Query: InsertObjectQuery(nl.bla.persistence.Employee@4c32cdeb)
答案 0 :(得分:1)
似乎它是EclipseLink中的“bug”。虽然有些人认为严格来说,这不是一个错误,而是按照规定实施。另见:https://bugs.eclipse.org/bugs/show_bug.cgi?id=333100。如果我更改SQL DDL,那么外键departmentId引用超类SuperClassOne的主键id而不是子类Department,它一切正常。
旧情况:
CREATE TABLE Employee (
id INTEGER NOT NULL,
name VARCHAR(255) NOT NULL,
departmentId INTEGER NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (departmentId) REFERENCES Department (id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8 DEFAULT COLLATE = utf8_bin;
新情况:
CREATE TABLE Employee (
id INTEGER NOT NULL,
name VARCHAR(255) NOT NULL,
departmentId INTEGER NOT NULL,
PRIMARY KEY (id),
-- FOREIGN KEY (departmentId) REFERENCES Department (id) ON DELETE CASCADE ON UPDATE CASCADE
FOREIGN KEY (departmentId) REFERENCES SuperClassOne (id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8 DEFAULT COLLATE = utf8_bin;
在我看来,它可能不是一个bug,但无论如何都是一个限制,因为现在我还可以在外键departmentId中放入SuperClassOne的另一个子类的主键id。所以我必须通过代码手动检查:-(所以我希望EclipseLink会改变这种行为。
答案 1 :(得分:0)
当目标表与引用表具有相同的列名时,您只能省略连接列中的referencedColumnName。
因此,在您的实体员工中,您应该:
@ManyToOne
@JoinColumn(name = "departmentId", referencedColumnName="id")
private Department department;
也许你在测试之间重命名了列,因为它也不应该在第一次工作。
答案 2 :(得分:0)
这是完整的解决方案:
SQL DDL:
CREATE TABLE SuperClassOne (
id INTEGER NOT NULL AUTO_INCREMENT,
type VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8 DEFAULT COLLATE = utf8_bin;
CREATE TABLE SuperClassTwo (
id INTEGER NOT NULL AUTO_INCREMENT,
type VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8 DEFAULT COLLATE = utf8_bin;
CREATE TABLE Department (
id INTEGER NOT NULL,
name VARCHAR(255) NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (id) REFERENCES SuperClassOne (id) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8 DEFAULT COLLATE = utf8_bin;
CREATE TABLE Employee (
id INTEGER NOT NULL,
name VARCHAR(255) NOT NULL,
departmentId INTEGER NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (id) REFERENCES SuperClassTwo (id) ON DELETE RESTRICT ON UPDATE RESTRICT,
FOREIGN KEY (departmentId) REFERENCES Department (id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8 DEFAULT COLLATE = utf8_bin;
班级部门:
@Entity
@Table(name = "Department")
@Customizer(InheritanceCustomizer.class)
public class Department extends SuperClassOne {
@Column(name = "name")
private String name;
@OneToMany(mappedBy = "department", cascade = CascadeType.ALL)
private List<Employee> employees = new ArrayList<>();
public Department() {
super();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Employee> getEmployees() {
return employees;
}
public void setEmployees(List<Employee> employees) {
this.employees = employees;
}
public void addEmployee(Employee employee) {
employee.setDepartment(this);
employees.add(employee);
}
}
班级员工:
@Entity
@Table(name = "Employee")
public class Employee extends SuperClassTwo {
@Column(name = "name")
private String name;
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "departmentId")
private Department department;
public Employee() {
super();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = department;
}
}
class InheritanceCustomizer:
public class InheritanceCustomizer implements DescriptorCustomizer{
@Override
public void customize(ClassDescriptor descriptor) throws Exception {
descriptor.setHasMultipleTableConstraintDependecy(true);
}
}