Updating nested child entities with OneToMany relationships in Spring Data JPA

Viewed 392

I currently have a Parent entity that has a OneToMany mapping with its Children, which in turn also has its own OneToMany mapping with its children, and so on. All in all, it ends up becoming several levels deep. I'm wondering what's the best way to handle updates. Here's what I have so far (I've renamed some of the class names and omitted getters/setters for brevity, so please excuse any typos).

@Entity
@Table(name = "parent")
public class Parent implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @Column(name = "id")
    private Long id;

    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinColumn(name = "parent_id")
    private List<Children> children = new ArrayList<>();

    public void setChildren(List<Children> children) {
        this.children.clear();
        for (Children child : children) {
            child.setParent(this);
        }
        this.children.addAll(children);
    }

    public void addChild(Children child) {
        child.setParent(this);
        this.children.add(child);
    }
   
    // additional setters and getters omitted for brevity 
@Entity
@Table(name = "children")
public class Children implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinColumn(name = "children_id")
    private List<ChildrenOfChildren> childrenOfChildren = new ArrayList<>();

    @ManyToOne(fetch = FetchType.LAZY)
    private Parent parent;

    // setters and getters omitted for brevity
@Entity
@Table(name = "children_of_children")
public class ChildrenOfChildren implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinColumn(name = "children_of_children_id")
    private List<ChildrenOfChildrenOfChildren> childrenOfChildrenOfChildren = new ArrayList<>();

    @ManyToOne(fetch = FetchType.LAZY)
    private Children children;

    // setters and getters omitted for brevity

...and so on. I have a JPA repository class and a service class that uses the repository class to fetch my Parent objects and its associated children and children of children, and so on. This works as I expected.

My question is how should I handle updates in an elegant and efficient manner, if given the Parent object? A client would send a Protobuf request, which I then map out to the entity classes I've created. I'm currently doing it like so:

@Service
public class ParentService {

    private final ParentRepository parentRepository;
    private final ChildrenRepository childrenRepository;
    private final ChildrenOfChildrenRepository childrenOfChildrenRepository;
    // additional repos as needed for each level

    @Autowired
    public ParentService(ParentRepository parentRepository,
                             ChildrenRepository childrenRepository,
                             ChildrenOfChildrenRepository childrenOfChildrenRepository) {
        this.parentRepository = parentRepository;
        this.childrenRepository = childrenRepository;
        this.childrenOfChildrenRepository = childrenOfChildrenRepository;
    }

    @Transactional
    public void updateParent(ProtobufPojo pojo) {
        Parent parentEntity = parentRepository.findById(pojo.getId()).get();
        if (parentEntity == null) {
            parentEntity = new Parent();
        }

        parentEntity.setField(pojo.getField());
        parentEntity.setField2(pojo.getField2());
        parentEntity.setChildren(mapChildren(pojo.getChildrenList()));
        parentRepository.save(parentEntity );
    }

    @Transactional
    public List<Children> mapChildren(List<ChildrenPojo> childrenPojoList) {
        List<Children> childrenEntities = new ArrayList<>();
        for (ChildrenPojo pojo : childrenPojoList) {
            Children childrenEntity = childrenRepository.findById(pojo.getId()).get();
            if (childrenEntity == null) {
                childrenEntity = new Children();
            }

            childrenEntity.setField(pojo.getName());
            // continue mapping children of children, and so on
            childrenEntity.setChildrenOfChildren(mapChildrenOfChildren(pojo));
            childrenEntities.add(childrenEntity);
        }
        return childrenEntities;
    }

    @Transactional
    public List<ChildrenOfChildren> mapChildrenOfChildren(List<ChildrenOfChildrenPojo> childrenOfChildrenPojoList) {
        // repeat...
    }
}

Is this the right way to approach this? From the top-level Parent, I'm essentially checking if it exists, and if it does, map all of its fields from the client POJO, then continuing down to its Children, mapping its fields, and so on. I'm not entirely convinced this is the best method, as I'm essentially overwriting fields (even if they're the same), but from what I can see so far, this would handle updates, addition of new nodes, and removal of nodes. Am I missing something?

Any advice would be appreciated. Thanks in advance!

0 Answers
Related