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
属性(ProviderEntity
和 CompanyPractitionerTypeEntity
)中添加 @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
,要么以正确的顺序手动执行所有插入。这取决于您的具体业务案例。
我需要一些 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
中加入 tablecreate 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
属性(ProviderEntity
和 CompanyPractitionerTypeEntity
)中添加 @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
,要么以正确的顺序手动执行所有插入。这取决于您的具体业务案例。