Skip to content
Advertisement

How to create LinkedList of JPA entities

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

Predecessor - Successor - relation

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