Я пытаюсь использовать Spring JPA/Hibernate для поддержки родительской таблицы с дочерней таблицей, связанной с идентификатором. Если я попытаюсь дважды вставить один и тот же объект, таблица родителя будет правильно обновлена, но дочерняя таблица всегда вставлена, даже если запись в дочерней таблице уже присутствует.Spring JPA/Hibernate @ManyToOne отношения всегда вставляют дочерние строки
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;
POJOs:
@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);
}
}
Repository Layer:
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);
});
}
}
Если запись в какой-либо из таблиц не существует, она сохраняет оба сущности как можно точнее и связывает правильные идентификаторы между таблицами. Проблема возникает, если снова вставлены один и тот же родительский и дочерний объекты. UPDATE происходит на столе Родитель, но ВСТАВИТЬ на ребенка:
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 правильно упорствовать их, если запись уже существует?
У вас есть странная база данных. Есть ли только один ребенок на одного родителя, и в этом случае он должен быть OneToOne not ManyToOne. – thedoctor
Я рассмотрел пример как можно более простой, но в реальном случае может быть несколько записей родительской таблицы для записи в дочерней таблице. –
Опубликуйте код, в котором вы создаете объекты, которые необходимо сохранить. –