I'm facing some behaviour about spring data jpa and I need to understand.
consider this :
Document
@Getter
@Setter
@ToString
@Entity
@Table(name = "document")
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Document{
@Id
@GeneratedValue(strategy = IDENTITY)
private Long id;
private String title;
private String description;
@OneToMany(cascade = ALL, fetch = FetchType.LAZY, mappedBy = "document")
@ToString.Exclude
private Set<Template> templates = new HashSet<>();
public void addTemplates(Template template) {
templates.add(template);
template.setDocument(this);
}
}
Template
@Getter
@Setter
@ToString
@Entity
@Table
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Template{
@Id
@GeneratedValue(strategy = IDENTITY)
private Long id;
private String name;
@ManyToOne(fetch = LAZY)
@JoinColumn(name = "document")
private Document document;
}
Service
@RequiredArgsConstructor
@Service
@Transactional
@Slf4j
public class DocumentService {
private final DocumentRepository documentRepository;
private final TemplateRepository templateRepository;
public void createTemplate() {
Set<Template> template = new HashSet<>();
template.add("template1");
template.add("template2");
templateRepository.saveAll(template);
}
public void createMovie(DocumentDTO documentRequest) {
Set<Template> templates = toTemplate(documentRequest.getTemplates());
Document document = Document.builder()
.title(documentRequest.getTitle())
.description(documentRequest.getDescription())
.template(new HashSet<>())
.build();
templates.forEach(document::addTemplates);
documentRepository.save(document);
}
private Set<Template> toTemplate(TemplateDTO templatesDTO) {
return documentRequest.getTemplates().stream().map(templateDTO ->
Template.builder()
.id(templateDTO.getId())
.firstName(templateDTO.getFirstName())
.lastName(templateDTO.getLastName())
.build()
).collect(Collectors.toSet());
}
}
First => I created the template And When I created a new document for instance
{
"title": "tre",
"description": "opppp",
"template": [
{
"id": 1,
"name": "template1"
},
{
"id": 2,
"name": "template2",
}
]
}
with this data configuration I'm getting this error detached entity passed to persist
. So to resolve this error I put CASCADE Merge instead ALL on Parent like this
@OneToMany(cascade = MERGE, fetch = FetchType.LAZY, mappedBy = "document")
@ToString.Exclude
private Set<Template> template= new HashSet<>();
again I try to save document. document are ok but nothing happen in template. this is the sql in console
Hibernate: insert into document (id, description, title) values (null, ?, ?)
Why the child is not update with document ID ?? Because the Entity manager of spring jpa call persist instead merge.
Is there a way to save without having to explicitly call merge ??? because if I call a merge I'm getting a bunch of select child before update. So How Can I avoid this problem.
CodePudding user response:
Couple of observations, when there is a bidirectional mapping, JoinColumn annotation at the child entity is unnecessary.
Always rely on persist, persistAndFlush or persistAllAndFlush instead of save as it persist only if there is new entity else it will try to merge. This is where you are able to see SQL statement generated is only on the parent entity and not on the child entity.
Refer, https://vladmihalcea.com/best-spring-data-jparepository/
CodePudding user response:
I think is helped some one. please follow Vlad blog. He explain why we need to add Version to avoid a multiple Select. https://vladmihalcea.com/jpa-persist-and-merge/
So in my case I add somethink like this in my child entity:
@Version
private Long version;