[JPA] How to save when there are items other than keys in the intersection table related to ManyToMany

Introduction

Mapping with an intersection table is a headache every time. This time, I will introduce the registration method when you have a non-composite key in the intersection table.

Registration of compound key only

First, suppose you have an entity with the following relationships:

1.png

The saving method in this case is as follows.

User master


@Getter
@NoArgsConstructor
@EqualsAndHashCode(of = {"id"})
@Entity
@Table
public class User implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String status;

    @ManyToMany
    @JoinTable(name = "user_service",joinColumns = @JoinColumn(name="user_id"), referencedColumnName="id"
              inverseJoinColumns = @JoinColumn(name="service_id", referencedColumnName="id"))
    private List<Service> services = new ArrayList<>();

    public User(String name,String status){
        this.name = name;
        this.status = status;
    }

    //Add services to use
    public void addService(Service service){
        services.add(service);
    }
}

Service master


@Getter
@NoArgsConstructor
@EqualsAndHashCode(of = {"id"})
@Entity
@Table
public class Service implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String status;

    public Service(String name,String status) {
        this.name = name;
        this.status = status;
    }
}

registration process


User user = entityManager.find(User.class,<User ID>);
Service service = entityManager.find(Service.class,<Service ID to use>);
user.addService(service);
entityManager.persist(user);

Registration when there is an item other than the compound key

Add "Start date" to the usage service table.

1.png

In this case, since the usage start date cannot be saved only with `` `@ JoinTable```, the entity class will be created as a usage service.

Service used


@Getter
@NoArgsConstructor
@EqualsAndHashCode(of = {"id"})
@Entity
@Table(name = "user_service")
public class UserService implements Serializable {

    @EmbeddedId
    private PrimaryKey pk;
    
    @Column(name = "begin_date")
    private LocalDate beginDate;

    @ManyToOne
    @MapsId("userId")
    private User user;

    @ManyToOne
    @MapsId("serviceId")
    private Service service;

    public UserService(User user, Service service,LocalDate beginDate) {
        this.user = user;
        this.service = service;
        this.beginDate = beginDate;
    }

    @Getter
    @NoArgsConstructor
    @Embeddable
    public static class PrimaryKey implements Serializable {

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

        @Column(name = "service_id")
        private Long serviceId;
    }
}

The important thing here is @ MapsId. Specify the field name defined in the PrimaryKey class that you want to map to the argument. This associates the entity class with the composite key.

Also modify the user master. Originally, I was able to get the service I am using from the user master, Make sure to get it via the service class you are using.

User master


@Getter
@NoArgsConstructor
@EqualsAndHashCode(of = {"id"})
@Entity
@Table
public class User implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String status;

    @OneToMany(mappedBy = "user")
    private List<UserService> services = new ArrayList<>();

    public User(String name,String status){
        this.name = name;
        this.status = status;
    }
    
    //Add services to use
    public void addService(Service service){
        services.add(new UserService(this, service, LocalDate.now()));
    }

    //I'll return a list of services instead of a list of intersection tables
    public List<Service> getServices(){
        return services.stream().map(UserService::getService).collect(Collectors.toList());
    }
}

Registration process sample


User user = entityManager.find(User.class,<User ID>);
Service service = entityManager.find(Service.class,<Service ID to use>);
user.addServices(service);
entityManager.persist(user);

Recommended Posts

[JPA] How to save when there are items other than keys in the intersection table related to ManyToMany
How to output the value when there is an array in the array
[Rails] How to register multiple records in the intermediate table with many-to-many association
How to change the process depending on the list pressed when there are multiple ListViews
How to set chrony when the time shifts in CentOS7
How to disable existing selected items in the select box
What to do when the changes in the Servlet are not reflected
[jOOQ] How to CASE WHEN in the WHERE / AND / OR clause
(Spring-data-jpa) How to get POJO (other than entity class) using JPA2.1
How to solve the unknown error when using slf4j in Java
How to write the view when Vue is introduced in Rails?
Why does it stop when I'm debugging in Eclipse and there are no breakpoints? Links related to
How to find the total number of pages when paging in Java
How to constrain the action of the transition destination when not logged in
[Ruby] How to prevent errors when nil is included in the operation
How to reference a column when overriding the column name method in ActiveRecord