DTO to JPA Entity in onetomany bidirectional relationship with orika

895 Views Asked by At

I am copying incoming DTO objects to my JPA entities having bidirectional OneToMany relation, using orika library(tried different other libraries like Dozer,SpringBeanUtils etc and all have same effect), though copy works fine but persisting entity is not updating foreign key of child entities. I am aware that it's happening due to missing child entity synchronisation with parent entity.

But the whole idea of using orika or any similar library is to avoid boilerplate code of copying each entity/objects separately. So I want to know is there anyway I can do this synchronisation during copy itself?

Following are my entity and DTO class

@Entity
@Data
public class Parent
{
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String name;
@OneToMany(mappedBy=“parent”,cascade=CascadeType.ALL,OrphanRemoval=true)
private List<Child> childs;
public void addChild(Child child)
{
childs.add(child);
child.setParent(this);
}
public void removeChild(Child child)
{
childs.remove(child);
child.setParent(null);
}
}


@Entity
@Data
public class Child
{
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String name;
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name=“parent_id”)
private Parent parent;
}

@Data
public class ParentDto
{
private Long id;
private String name;
private List<ChildDto> childs;
}

@Data
public class ChildDto
{
private Long id;
private String name;
}

And Orkia copy logic -

DefaultMapperFactory factory=new DefaultMapperFactory().Builder().build();
factory.classMap(ParentDto.class,Parent.class).byDefault().register();
Parent parentEntity=factory.getMapperFacade().map(parentDtoObject,Parent.class);
//parentEntity.getChilds().forEach(child -> child.setParent(parentEntity));
repository.save(parentEntity);

When repository.save() is completed foreign_key of child table parent_id is persisted as null instead of actual value. But if I uncomment the line commented it works fine, which I don't want to do as my entity has many child objects and looping will have performance impact and code looks ugly as well. Is there a better way of doing this? Or having unidirectional OneToMany will work ?

1

There are 1 best solutions below

0
Christian Beikov On

You can do that with Blaze-Persistence Entity-Views which was designed with this in mind.

I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.

A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:

@EntityView(Parent.class)
@UpdatableEntityView
public interface ParentDto {
    @IdMapping
    Long getId();
    String getName();
    void setName(String name);
    @UpdatableMapping
    Set<ChildDto> getChilds();

    @EntityView(Child.class)
    @UpdatableEntityView
    interface ChildDto {
        @IdMapping
        Long getId();
        String getName();
        void setName(String name);
    }
}

Querying is a matter of applying the entity view to a query, the simplest being just a query by id.

ParentDto a = entityViewManager.find(entityManager, ParentDto.class, id);

The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

Page<ParentDto> findAll(Pageable pageable);

The best part is, it will only fetch the state that is actually necessary!

Saving the state is also easy:

entityViewManager.save(entityManager, parentDto);