I have a scenario where I need to create an entity which has references to list of predecessors and list of successors. when a request is received, I need to create entity like below:
Request:
{ // create request details for task2 "predecessors": ["task1"], "successors": ["task3"] }
My new entity “task2” should now be created as below in DB.
task1 <- task2 -> task3 task1 -> task2 task2 <- task3
While saving task2 with previous as task1 and next as task3, I also would like to update task1’s next as task2 and task3’s previous as task1.
I am struggling to come up with a JPA entity relationship for the above scenario.
@Entity @Table(name = "task") public class Entity { @ManyToMany private Set<TaskDatabaseEntity> successors; @ManyToMany private Set<TaskDatabaseEntity> predecessors; }
Update: Below is the the code how I am trying to save the entities.
TaskDatabaseEntity newTaskEntity = taskEntityFromCreateEvent(); List<TaskDatabaseEntity> taskEntities = List.ofAll(new ArrayList<>()); if(domainEvent.getPredecessors() != null && !domainEvent.getPredecessors().isEmpty()) { for(TaskEvent event : domainEvent.getPredecessors()) { TaskDatabaseEntity entity = taskEntityFromUpdateEvent((TaskUpdate) event, userEntity); java.util.Set<TaskDatabaseEntity> suc = entity.getSuccessors() == null? new HashSet<>() : entity.getSuccessors(); suc.add(newTaskEntity); taskEntities.append(entity); newTaskEntity.getPredecessors().add(entity); } } if(domainEvent.getSuccessors() != null && !domainEvent.getSuccessors().isEmpty()) { for(TaskEvent event : domainEvent.getSuccessors()) { taskEntityFromUpdateEvent((TaskUpdate) event, userEntity); TaskDatabaseEntity entity = taskEntityFromUpdateEvent((TaskUpdate) event, userEntity); java.util.Set<TaskDatabaseEntity> pre = entity.getPredecessors() == null? new HashSet<>() : entity.getPredecessors(); pre.add(newTaskEntity); taskEntities.append(entity); newTaskEntity.getSuccessors().add(entity); } } taskEntities.append(newTaskEntity); List<TaskDatabaseEntity> savedEntities = List.ofAll(taskEntityRepository.saveAll(taskEntities)); newTaskEntity = savedEntities.filter(task -> task.getObjectId().equals(domainEvent.getEventId())).toList().get(0);
I am updating all the entities together using saveAll
, but the return saved list coming as empty. I do not understand why. Any suggestions on where I am going wrong? I am doubting my entity relationship, may be I have defined relation wrong.
Update 1:
I have tried to maintain the relationship of one another through mappedBy
, that way the relation will be implied and I dont need to update all the entities.
@ToString.Exclude @EqualsAndHashCode.Exclude @ManyToMany(mappedBy="predecessors", fetch = FetchType.LAZY) private Set<TaskDatabaseEntity> successors; @ToString.Exclude @EqualsAndHashCode.Exclude @ManyToMany(fetch = FetchType.LAZY) @JoinTable(schema = "eai", name = "task_ancestors") private Set<TaskDatabaseEntity> predecessors;
Here is how I am now saving it
TaskDatabaseEntity newTaskEntity = taskEntityFromCreateEvent(); if(domainEvent.getPredecessors() != null && !domainEvent.getPredecessors().isEmpty()) { for(TaskEvent event : domainEvent.getPredecessors()) { TaskDatabaseEntity entity = taskEntityFromUpdateEvent((TaskUpdate) event, userEntity); newTaskEntity.getPredecessors().add(entity); } } newTaskEntity = taskEntityRepository.save(newTaskEntity);
I am able to now save the new entity with its predecessors and the successors can be implied from the table as below
Advertisement
Answer
In title you mention LinkedList
but in your entity the relationship is set ManyToMany so that successors and predecessors an joined to your entity in a “flat” manner. I prepared my answer according to this ManyToMany approach but it is easily applied to OneToOne approach that is more like LinkedList implementation.
I think that you are missing cascade and maybe not correctly setting both sides of relationship. See my simplified and “synchronized” (your naming was a bit confusing) example:
Task (was Entity)
@Entity @Table(name = "task") @Getter @Setter public class Task { @Id @GeneratedValue private Long id; // Sets the persist operation persist also successors @ManyToMany(cascade = CascadeType.PERSIST) private Set<Task> successors = new HashSet<>(); // Sets the persist operation persist also predecessors @ManyToMany(cascade = CascadeType.PERSIST) private Set<Task> predecessors = new HashSet<>(); }
Test it
private Task task1 = new Task(); private Task task2 = new Task(); private Task task3 = new Task(); @Resource private TaskRepository repo; @Test void test() { // assuming history order task1 > task2 > task3 // You need to handle both sides of relationship before persisting // for both lists. task2.getPredecessors().add(task1); task1.getSuccessors().add(task2); task2.getSuccessors().add(task3); task3.getPredecessors().add(task2); repo.save(task2); }