Skip to content
Advertisement

Best way to save and update entity Parent and child using spring data jpa

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;
User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement