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.
Advertisement
Answer
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;