Spring 启动 JPA,数据不会持久化在多对多关系的连接 table 中

Spring Boot JPA, data do not persist in join table of many-to-many relationship

我需要一些 help/advice,因为我是 Spring Boot 的新手。我试图在 linking table 的帮助下建立多对多关系。但由于某种原因,我无法在 link table.

中保存数据

以下是实体:

@Table(name = "providers")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor

public class ProviderEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long providerId;

    @OneToMany(mappedBy = "provider", fetch = FetchType.LAZY)
    @JsonManagedReference
    private List<ProviderPractitionersEntity> providerPractitioners; 

    ...
-------------------------------------------------------------
@Entity
@Table(name = "company_practitioner_types")
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor

public class CompanyPractitionerTypeEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToMany(mappedBy = "practitioner", fetch = FetchType.LAZY)
    @JsonManagedReference
    private List<ProviderPractitionersEntity> practitionerProviders;

    ...
}
---------------------------------------------------------------
@Entity
@Table(name = "provider_practitioners")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class ProviderPractitionersEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    private Long id;

    @ManyToOne
    @JsonBackReference
    @JoinColumn(name = "practitioner_id") /*this column is foreign key from practitioner table*/
    private CompanyPractitionerEntity practitioner;

    @ManyToOne
    @JsonBackReference
    @JoinColumn(name = "provider_id") /*this column is foreign key from provider table*/
    private ProviderEntity provider;

    @Column(name = "size")
    private String size;

}

当我持久化新的 Provider 时,我在持久化之前在每个 ProviderPractitioner 对象中设置了这个新的提供者对象的引用和从业者对象的引用。

因此,来自 List providerPractitioners 的对象的所有状态都为空值。并且数据库中的 provider_practitioners table 没有任何内容。

我尝试以这种方式设置多对多关系而不是使用@ManyToMany 注释的原因是 ProviderPractitionerEntity 中的“大小”变量包含一个提供者的一种类型的从业者的数量。

我尝试为 linking table 创建 embededId(复合 ID),但得到了相同的结果。

********* 更新 ***********

我按照建议创建了 embededId class:

@Embeddable
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString

public class ProviderPractitionersId implements Serializable {

    @Column(name = "practitioner_id")
    private Long practitionerId;

    @Column(name = "provider_id")
    private Long providerId;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof ProviderPractitionersId)) return false;
        ProviderPractitionersId that = (ProviderPractitionersId) o;
        return Objects.equals(getPractitionerId(), that.getPractitionerId()) && Objects.equals(getProviderId(), that.getProviderId());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getPractitionerId(), getProviderId());
    }
}

并将其添加到连接实体中:

@Entity
@Table(name = "provider_practitioners")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class ProviderPractitionersEntity {
    @EmbeddedId
    private ProviderPractitionersId id;

    @ManyToOne(fetch = FetchType.LAZY)
    @MapsId("practitionerId")
    @JoinColumn(name = "practitioner_id")
    private CompanyPractitionerEntity practitioner;

    @ManyToOne(fetch = FetchType.LAZY)
    @MapsId("providerId")
    @JoinColumn(name = "provider_id")
    private ProviderEntity provider;

    @Column(name = "size")
    private String size;

}

这是我在 DB

中加入 table
create table provider_practitioners
(
    practitioner_id integer,
    provider_id     integer,
    size            varchar,
    PRIMARY KEY (practitioner_id, provider_id),
    FOREIGN KEY (practitioner_id) REFERENCES company_practitioners (id)
        ON DELETE SET NULL,
    FOREIGN KEY (provider_id) REFERENCES providers (provider_id)
        ON DELETE SET NULL
);

我正在为每个 ProviderPractitionersEntity 设置值(Id、提供者参考、从业者参考、大小值)之前,我没有设置 ID 对象。

public Set<ProviderPractitionersEntity> dtoToPractitionersEntity(final ProviderDto providerDto,
                                                                     final ProviderEntity providerEntity) {
        final Set<ProviderPractitionersEntity> providerPractitionersEntities = new HashSet<>();

        //Iterate through passed list of practitioner types for given provider
        providerDto.getPractitioners().forEach(practitionerDTO -> {

            final ProviderPractitionersEntity providerPractitioner = new ProviderPractitionersEntity();

            //Check if there is current practitioner type in the codebook table with practitioner types
            final CompanyPractitionerEntity practitioner = companyPractitionerRepository.findByPractitionerId(practitionerDTO).orElseThrow();

            //add provider-practitioner set's reference to practitioner object
            practitioner.setPractitionerProviders(providerPractitionersEntities);

            //add current practitioner reference to ProviderPractitioner instance
            providerPractitioner.setPractitioner(practitioner);

            //add provider reference to ProviderPractitioner entity instance
            providerPractitioner.setProvider(providerEntity);

            //add values to the key and add key to the ProviderPractitioner instance
            final ProviderPractitionersId providerPractitionersId = new ProviderPractitionersId(practitioner.getId(), providerEntity.getProviderId());
            providerPractitioner.setId(providerPractitionersId);
    //set size
    providerPractitioner.setSize("5-10");

当我尝试保留 Provider 对象时,现在我得到 javax.persistence.EntityNotFoundException:无法找到 ID 为 ProviderPractitionersId(practitionerId=2, providerId=0) 的 providers.model.entity.ProviderPractitionersEntity。

此时providerId为0,因为provider对象还没有持久化。为什么要尝试获取它?是因为我设置了键值吗?

您必须按照您提到的那样创建 @EmbeddedId,然后在 ProviderPractitionersEntity 属性(ProviderEntityCompanyPractitionerTypeEntity)中添加 @MapsId复合 id 中 属性 的名称。

所以首先创建复合id:

@Embeddable
public class ProviderPractitionersId implements Serializable {

   @Column(name = "practitioner_id")
   private Long practitionerId;

   @Column(name = "provider_id")
   private Long providerId;

然后,在多对多实体 (ProviderPractitionersEntity) 中以这种方式映射 id 和两个实体:

    @EmbeddedId
    private ProviderPractitionersId id;

    @ManyToOne(fetch = FetchType.LAZY)
    @MapsId("practitionerId")
    private CompanyPractitionerTypeEntity practitioner;

    @ManyToOne(fetch = FetchType.LAZY)
    @MapsId("providerId")
    private ProviderEntity provider;

因此,在 ProviderPractitionersEntity 中,您可以添加任意数量的属性,例如您的 size 或其他任何属性。

评论后更新

如您所说,关系是否自动保留取决于您指定的 CascadeType。如果指定了 none,您必须以正确的顺序手动保存所有实体(包括中间的 table)。首先,Provider 和 Practitioner 必须存在于它们的 table 中,以便插入 ProviderPractitioner,因为它具有那些 table 的外键。因此,您要么根据需要微调 CascadeType,要么以正确的顺序手动执行所有插入。这取决于您的具体业务案例。