JPA在同一事务中插入父实体和子实体

时间:2013-06-17 15:02:28

标签: mysql spring hibernate jpa

我完全陷入了这个问题,也许有人可以提供帮助。

我有两个实体,父母和孩子。 Test1Entity是父级,Test1ChildEntity是子级。当然,伟大的命名。数据库是Mysql,JPA提供者是Hibernate。以下是两个实体的定义:

@Table(name = "Test1", schema = "", catalog = "")
@Entity
public class Test1Entity {
    private int id;

    @Column(name = "id")
    @Id
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    private String name;

    @Column(name = "name")
    @Basic
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Test1Entity that = (Test1Entity) o;

        if (id != that.id) return false;
        if (name != null ? !name.equals(that.name) : that.name != null) return false;

        return true;
    }

    @Override
    public int hashCode() {
        int result = id;
        result = 31 * result + (name != null ? name.hashCode() : 0);
        return result;
    }

    private Collection<Test1ChildEntity> test1ChildrenById;

    @OneToMany(mappedBy = "test1ByParentId")
    public Collection<Test1ChildEntity> getTest1ChildrenById() {
        return test1ChildrenById;
    }

    public void setTest1ChildrenById(Collection<Test1ChildEntity> test1ChildrenById) {
        this.test1ChildrenById = test1ChildrenById;
    }

}


@Table(name = "Test1_Child", schema = "", catalog = "")
@Entity
public class Test1ChildEntity {
    private int id;

    @Column(name = "id")
    @Id
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    private String name;

    @Column(name = "name")
    @Basic
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Test1ChildEntity that = (Test1ChildEntity) o;

        if (id != that.id) return false;
        if (name != null ? !name.equals(that.name) : that.name != null) return false;

        return true;
    }

    @Override
    public int hashCode() {
        int result = id;
        result = 31 * result + (name != null ? name.hashCode() : 0);
        return result;
    }

    private Test1Entity test1ByParentId;

    @ManyToOne
    @JoinColumn(name = "parent_id")
    public Test1Entity getTest1ByParentId() {
        return test1ByParentId;
    }

    public void setTest1ByParentId(Test1Entity test1ByParentId) {
        this.test1ByParentId = test1ByParentId;
    }
}

尝试在同一事务中插入一个父项和一个子项的代码如下:

@Transactional
    public void createItWell(String parentName, String childName) {

        Test1Entity parent = new Test1Entity();

        parent.setName(parentName);

        Test1ChildEntity child = new Test1ChildEntity();

        child.setName(childName);
        child.setTest1ByParentId(parent);

        Set<Test1ChildEntity> mySet = new HashSet<>();

        mySet.add(child);
        parent.setTest1ChildrenById(mySet);

        this.entityManager.persist(parent);
        this.entityManager.persist(child);

    }

尝试执行此代码时出现以下异常:

Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails (`comwork`.`test1_child`, CONSTRAINT `test1_child_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `Test1` (`id`))
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:525)
    at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
    at com.mysql.jdbc.Util.getInstance(Util.java:386)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1041)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4187)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4119)
    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2570)
    at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2731)
    at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2815)
    at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2155)
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2458)
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2375)
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2359)
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:133)

单独插入其他实体(不涉及任何关系)可以很好地完成。

两个表定义是:

CREATE TABLE `Test1` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  UNIQUE KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

LOCK TABLES `Test1` WRITE;
/*!40000 ALTER TABLE `Test1` DISABLE KEYS */;

INSERT INTO `Test1` (`id`, `name`)
VALUES
    (6,'Parent');

/*!40000 ALTER TABLE `Test1` ENABLE KEYS */;
UNLOCK TABLES;


# Dump of table Test1_Child
# ------------------------------------------------------------

CREATE TABLE `Test1_Child` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `parent_id` int(11) unsigned NOT NULL,
  `name` varchar(20) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `parent_id` (`parent_id`),
  CONSTRAINT `test1_child_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `Test1` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

我是JPA的新手,很抱歉这可能是一个微不足道的问题。

谢谢。

2 个答案:

答案 0 :(得分:1)

尝试在OneToMany关系上设置Cascade属性:

@OneToMany(cascade = {CascadeType.ALL}, fetch = FetchType.LAZY)
private Collection<Test1ChildEntity> test1ChildrenById;

答案 1 :(得分:1)

对于遇到此问题的人......实体代码是使用Idea Intellij persistance生成工具生成的。

显然,该工具无法使用@GeneratedValue注释@Id字段,因此在插入第一个(父)实体后禁用了id提取。这样,子实体将插入外键字段的0(显然无效)值。

只需将@GeneratedValue添加到@Id带注释的属性,它就可以正常工作。