One-to-many и many-to-one связь в JPA и Hibernate

One-to-many и many-to-one связь в JPA и Hibernate.
Более подробно можно прочитать в официальной документации на Hibernate. Здесь статья размещена только для краткого ознакомления. Пример описания связи с помощью аннотаций:

@Entity
@Table(name="parent")
class Parent {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
private Set attachments = new HashSet();
.....
}
@Entity
@Table(name="child)
class Child {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "parent_id", nullable = false)
private Parent parent;
....
}

В приведённом выше куске кода через @JoinColumn описана колонка в базе данных, которая связывает child с родительской таблицей. В @OneToMany аннотации через mappedBy указано свойство дочернего класса, которое связывает его с родительским. CascadeType.ALL все операции изменения в коллекции (добавление/изменение) отражает в базе данных. Атрибут аннотации orphanRemoval=true указывает, что при удалении элемента из коллекции, нужно удалить элемент в базе данных. FetchType.LAZY — ленивая выборка. Элементы коллекции будут выбираться из базы данных только при обращении к какому-либо свойству коллекции.

Тут ещё нужно заметить, что при сохранении объектов через em.persist() или em.merge() будут проставляться идентификаторы вставленных записей. Но при этом для того, чтобы узнать идентификатор вставленной дочерней записи нужно смотреть не тот объект, который вставляли в коллекцию, а тот, который будет в этой коллекции после вызова метода сохранения persist/merge. Я не знаю точно, с чем это связано, но работает почему-то именно так. То есть:

Car attachParent = parentDao.findById(1L);
Child child = new Child();
Date currentDate = new Date();
child.setCreatedAt(currentDate);
child.setParent(attachParent);
attachParent.getChilds().add(child);
parentDao.save(attachParent);
// Здесь child.getId() будет всё ещё null.
// Узнаём настоящий id у самой последней вставленной записи.
// Либо по какому-то другому, более уникальному реквизиту, если он есть.
Optional optionalChild = attachParent
.getchilds().stream().sorted((a, b) -> {
return -a.getCreatedAt().compareTo(b.getCreatedAt());
}).findFirst();
Long insertedChildId = optionalChild.isPresent() ? optionalChild.get()
: null;