Home > Back-end >  Problems making a many-to-many relationship work across 2 microservices using Spring Boot & Hibernat
Problems making a many-to-many relationship work across 2 microservices using Spring Boot & Hibernat

Time:10-13

So for this assignment, I'm supposed to have 2 microservices (task-service & user-service) with each having their own database. A task can have multiple users assigned it but multiple users can be assigned to multiple tasks so therefore it's a many-to-many relationship.

Both microservices are Spring Boot applications using Hibernate and I'm struggling to make this relationship happen between task and user inside the task-service because I don't think it's a good idea to copy the user model that already exists in the user-service, over to the task-service.

This assignment assumes that there is consistency in the user-ids across both databases without using foreign keys.

For context:

Desired entity relationship diagram generated from Hibernate:ERD

Task.java [task-service]

@Entity
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@EqualsAndHashCode(callSuper = true)
public class Task extends BaseEntity {

    private String name, description;

    @ManyToOne
    private Lane lane;

    @OneToMany
    private List<TaskUser> users;
}

TaskUser.java [task-service]

@Entity
@NoArgsConstructor
@AllArgsConstructor
@Data
@EqualsAndHashCode
public class TaskUser {

    @Id
    private Long id;

    @Column(name = "task_id")
    private Long taskId;

    @Column(name = "user_id")
    private Long userId;
 }

User.java [user-service]

@Entity
@NoArgsConstructor
@AllArgsConstructor
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class User extends BaseEntity {
    @Column(unique = true)
    private String username;
}

Currently, Hibernate is generating both a task_user & task_users table inside the task-service database and I'd like to just have 1 intermediate table instead of two.

Question: How would I make this many-to-many relationship work with Hibernate whilst working with two different microservices?

CodePudding user response:

Maybe a better hibernate mapping :

Task.java [task-service]

@Entity
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@EqualsAndHashCode(callSuper = true)
public class Task extends BaseEntity {

    private String name, description;

    @ManyToOne
    private Lane lane;

    @OneToMany(mappedBy="task")
    private List<TaskUser> users;
}

TaskUser.java [task-service]

@Entity
@NoArgsConstructor
@AllArgsConstructor
@Data
@EqualsAndHashCode
public class TaskUser {

    @Id
    private Long id;

    @ManyToOne
    @JoinColumn(name="task_id", nullable=false)
    private Task task;

    @Column(name = "user_id")
    private Long userId;
 }

CodePudding user response:

Use case: assign an already existing task to an already existing user. You can assign multiple users at a time to 1 single task.

PostRequest

/tasks/allocation/new

Request Body:

    {
      "task-id": 12345,
      "users": [
        {
          "username": "user-1",
          "user-id": 101
        },
        {
          "username": "user-2",
          "user-id": 102
        },
        {
          "username": "user-3",
          "user-id": 103
        }
      ]
    }

Post request is having one task and list of user-details to be allocated to that task.

TaskAllocation.java
    @Data
    public class TaskAllocation{
     @jsonProperty("task-id")
     private long taskId;
     @JsonProperty("users")
     private List<Users> userList;
}
Users.java
    @Data
    public class Users{
     @jsonProperty("username")
     private String username;
     @JsonProperty("user-id")
     private Long userId;
}
RestController

    @PostMapping("/tasks/allocation/new")
    public CompletableFuture<ResponseEntity<?>> assignTaskToUsers(@ResponseBody @Valid TaskAllocation taskAllocation){

      // service call

    }

Inside service:
  1. fetch the task from task db(verify if task exist)
  2. If needed, fetch details of users from user-service(for each user), need a async or rest call to user-service. Task service is not having details of users.
  3. For each user: 1.Create new Allocation
  4. Set task Id
  5. Set user-id or username
  6. Save

Task is already existing

Table Task-Allocation
    --------------------------------------------------------------
    alloc-id(Pk)         task-Id(Fk)        user-id    timetamp     
    --------------------------------------------------------------
    1                     12345         101         123123123123
    2                     12345         102         123123123123
    3                     12345         103         123123123123

Entities

Task and TaskAllocation has 1:n relationship i.e. task-allocation table consists of multiple records with same task-id.

Task.java


    @Getter
    @Setter
    @AllArgsConstructor
    @NoArgsConstructor
    @Entity
    @Table(name = "sirf_tournament")
    public class Task extends Auditable<String> implements Serializable {
        @Id
        @GeneratedValue
        private Long taskId;

        private String taskName;
        
        // others
        
          @OneToMany(
              fetch = FetchType.LAZY,
              mappedBy = "task",
              cascade = CascadeType.ALL,
              orphanRemoval = true)
          private Collection<TaskAllocation> taskAllocation = new HashSet<>();

    }

TaskAllocation.java

    @Getter
    @Setter
    @AllArgsConstructor
    @NoArgsConstructor
    @Entity
    @Table(name = "sirf_tournament")
    public class TaskAllocation extends Auditable<String> implements Serializable {
        @Id
        @GeneratedValue
        private Long allocId;

        private Long userId;
        
        
        @JsonIgnore
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "taskId")
        private Task task;

        // others

    }

User table and other related entities are not mentioned here. Task service has no information of users.

It would be good if you verify every users(all ids can be verified in a single call, return invalid ids from user service) and task ids before persisting them into table.

  • Related