【问题标题】:Spring JPA/Hibernate @ManyToOne relationship always inserting child rowSpring JPA/Hibernate @ManyToOne 关系总是插入子行
【发布时间】:2015-02-11 16:15:19
【问题描述】:

我正在尝试使用 Spring JPA/Hibernate 来维护一个父表和一个基于 Id 相关的子表。如果我尝试两次插入同一个对象,则父表会正确“更新”,但即使子表中的条目已经存在,子表也会始终插入。

CREATE TABLE `parent_table` (
  `parent_id` int(11) NOT NULL AUTO_INCREMENT,
  `ex_column_1` int(11) NOT NULL, // there is a unique constraint on this field - I just didn't include it
  `ex_column_2` int(11) NOT NULL,
  `child_id` int(11) NOT NULL,
  PRIMARY KEY (`parent_id`),
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

子表:

CREATE TABLE `child_table` (
  `child_id` int(11) NOT NULL AUTO_INCREMENT,
  `child_col_1` varchar(200) DEFAULT NULL,
  PRIMARY KEY (`child_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

POJO:

@Entity
@Table(name = "parent_table")
public final class Parent {
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "parent_id")
    private long id;

    @Id
    @Column(name = "ex_column_1")
    private int exampleField;

    @Column(name = "ex_column_2")
    private int exampleField2;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "child_id", unique = true)
    private Child c;

    public Parent(/*params*/) {}

    // getters ...

    @Entity
    @Table(name = "child_table")
    public static final class Child {
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        @Column(name = "child_id")
        private long id;

        @Column(name = "child_col_1")
        private exampleChildField;

        public Child(/*params*/) {}

        // getters ...
    }
}

如何构建和保存 POJO 的实际示例:

@RestController
@RequestMapping("/parents")
public final class ParentController {
    private final ParentRepository parentRepository;

    @Inject
    public ParentController(final ParentRepository parentRepository) {
        parentRepository = parentRepository;
    }

    @RequestMapping(value = "", method=RequestMethod.PUT)
    public void updateParents(@RequestBody Parent[] parents) {

        // ignore input for now, test by constructing object

        Parent.Child c = new Parent.Child(0L, "test 1");
        Parent p = new Parent(0L, "unique_column_1", "column_2", c);
        Set<Parent> pSet = new HashSet<>();
        pSet.add(p);

       parentRepository.save(pSet);
    }
}

存储层:

public interface ParentRepository extends CrudRepository<Parent, Long>, ParentRepositoryCustom {}

public interface ParentRepositoryCustom {
    void save(Set<Parent> parents);    
}

@Repository
final class ParentRepositoryImpl implements ParentRepositoryCustom {
    private final EntityManager entityManager;

    @Inject
    public EmployerRepositoryImpl(final EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    @Override
    @Transactional
    public void save(Set<Parent> parents) {
        parents.forEach(parent -> {
            Session session = (Session) entityManager.getDelegate();
            session.saveOrUpdate(parent);
        });
    }
}

如果任一表中的条目不存在,它会很好地保留两个实体并在表之间链接正确的 Id。如果再次插入相同的父对象和子对象,则会出现此问题。 UPDATE 发生在 Parent 表上,但 INSERT 发生在子表上:

Hibernate: insert into child_table (child_col_1) values (?)
Hibernate: insert into parent_table (ex_column_1, ex_column_2, child_id) values (?, ?, ?)

第二次插入时:

Hibernate: insert into child_table (child_col_1) values (?)
Hibernate: update parent_table set ex_column_2=?, child_id=? where ex_column_1=? 

如果条目已经存在,我如何让 EntityManager 正确保存这些内容?

【问题讨论】:

  • 你有一个奇怪的数据库。是否每个父母只有 1 个孩子,在这种情况下应该是 OneToOne 而不是 ManyToOne。
  • 我已将示例分解为尽可能简单,但在实际用例中,子表中的一个条目可能有多个父表条目。
  • 发布您创建要持久化实体的代码。
  • 你能展示一些你用来插入和更新分贝的代码吗?最好是 JUnit 测试。
  • @AlanHay 确实没有太多代码可用于创建实体。这些对象是使用一些虚假数据创建的。我将更新我的示例以显示它们是如何构建的。我有一个惊人的怀疑,因为子 id 为 0,Spring 尝试查询该表以查找 ID 为 0 的行并且找不到它,从而执行插入。正确的问题可能是,如何正确查询第二个表,以便更新而不是作为新行插入?

标签: java hibernate jpa spring-data-jpa


【解决方案1】:

我想我发现了问题。默认情况下,用于将新的“父”对象发布到 rest 接口的客户端将主键 id 设置为 0。这适用于父级,因为 ex_column_1 是在其他机制创建对象时已知的 ID .

另一方面,Child 对象只有一个主键作为其 Id,并且通过将其设置为零,Spring 尝试根据 id 为零查找相关的子行,并且始终执行插入而不是更新如果子行已经存在。使用 EntityManager.find() 方法对两个对象进行简单检查,然后保存或合并修复它。

【讨论】:

    猜你喜欢
    • 2021-06-15
    • 1970-01-01
    • 2016-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-13
    • 2013-06-17
    • 2021-12-21
    相关资源
    最近更新 更多