JPA OneToMany 关系:外键未保留在子表中

问题描述 投票:0回答:1

在保留与其子实体具有 OneToMany 关系的父 JPA 实体时遇到问题。使用 EclipseLink 保存父实体时,父实体和子实体都保存到各自的表中,但链接回父实体的子表中的外键保留为空,从而破坏了数据库中的关系。

具体来说,问题出在application_address表上。我无法在 TblApplicationAdress 和 TblApplicationMembership 之间获得正确的配置,以便 JPA 将 application_membership_id 外键链接保存到 application_address 表中。 (就上下文而言,一个应用程序包含一个成员资格,其中包含许多地址。)

以下是我的设置的详细信息:

  • 数据库:PostgreSQL 14
  • Java版本:Java 11
  • 注解:使用Lombok的@Getter和@Setter
  • 持久性:EclipseLink 与 JPA

表格:

create table application
(
    application_id                bigserial primary key,
    application_membership_id     bigint references application_membership,
    application_reference_number  varchar,
    -- other fields...
);

create table application_membership
(
    application_membership_id   bigserial primary key,
    membership_reference_number varchar,
    -- other fields...
);

create table application_address
(
    application_address_id    bigserial primary key,
    application_membership_id bigint references application_membership,
    address_type_code         varchar,
    address_line1_number      varchar,
    -- other fields...
);

JPA 实体:

@Getter
@Setter
@Entity
@Table(name = "application", schema = "obd")
public class TblApplication {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "application_id")
    private Long applicationId;

    @Column(name = "application_reference_number")
    private String applicationReferenceNumber;

    // Other fields...

    @ManyToOne
    @JoinColumn(name = "application_membership_id", referencedColumnName = "application_membership_id")
    private TblApplicationMembership applicationMembershipByApplicationMembershipId;
}

@Getter
@Setter
@Entity
@Table(name = "application_membership", schema = "obd")
public class TblApplicationMembership {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "application_membership_id")
    private Long applicationMembershipId;

    @Column(name = "membership_reference_number")
    private String membershipReferenceNumber;

    // Other fields...

    @OneToMany(mappedBy = "applicationMembershipByApplicationMembershipId", cascade = CascadeType.PERSIST)
    @JsonManagedReference
    private Collection<TblApplicationAddress> applicationAddressesByApplicationMembershipId;
}

@Getter
@Setter
@Entity
@Table(name = "application_address", schema = "obd")
public class TblApplicationAddress {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "application_address_id")
    private Long applicationAddressId;

    @Column(name = "address_type_code")
    private String addressTypeCode;

    @Column(name = "address_line1_number")
    private String addressLine1Number;

    // Other fields...

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "application_membership_id", referencedColumnName = "application_membership_id")
    @JsonBackReference
    private TblApplicationMembership applicationMembershipByApplicationMembershipId;
}

服务层:

tblApplication = applicationDataService.save(tblApplication);

数据服务:

public T save(T t) throws DataServiceException {
    try {
        t = em.merge(t);
        em.flush();
        em.clear();
        return t;
    } catch (Exception ex) {
        throw new DataServiceException("Error BaseDataService.save...", ex);
    }
}

我已经尝试使用@JsonBackReference、@JsonManagedReference和cascade = CascadeType.PERSIST,正如这些讨论中所建议的:

不幸的是,这些方法并没有解决问题。如果我无法正常工作,我可能必须先手动保存父实体,然后在保存之前将外键 ID 分配给子实体,但由于复杂性和大小,我宁愿避免这种解决方法对象结构。

问题: 在持久化这些实体时,什么可能导致子表中的外键保持为空?是否有我可能丢失的配置或注释,或者是否有更好的方法来确保在保存父实体时正确设置外键?

java json jpa eclipselink one-to-many
1个回答
0
投票

我找不到我想要的分辨率,因此采用了更手动的方法。

首先,在父 JPA 类上将级联设置为 CascadeType.ALL 非常重要。

其次,@JsonManagedReference 和 @JsonBackReference 没有区别,所以我删除了注释。

以下是更新的 JPA 实体:

@Getter
@Setter
@Entity
@Table(name = "application", schema = "obd")
public class TblApplication {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "application_id")
    private Long applicationId;

    @Column(name = "application_reference_number")
    private String applicationReferenceNumber;

    // Other fields...

    @ManyToOne
    @JoinColumn(name = "application_membership_id", referencedColumnName = "application_membership_id", cascade = CascadeType.ALL)
    private TblApplicationMembership applicationMembershipByApplicationMembershipId;
}

@Getter
@Setter
@Entity
@Table(name = "application_membership", schema = "obd")
public class TblApplicationMembership {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "application_membership_id")
    private Long applicationMembershipId;

    @Column(name = "membership_reference_number")
    private String membershipReferenceNumber;

    // Other fields...

    @OneToMany(mappedBy = "applicationMembershipByApplicationMembershipId", cascade = CascadeType.ALL)
    private Collection<TblApplicationAddress> applicationAddressesByApplicationMembershipId;
}

@Getter
@Setter
@Entity
@Table(name = "application_address", schema = "obd")
public class TblApplicationAddress {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "application_address_id")
    private Long applicationAddressId;

    @Column(name = "address_type_code")
    private String addressTypeCode;

    @Column(name = "address_line1_number")
    private String addressLine1Number;

    // Other fields...

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "application_membership_id", referencedColumnName = "application_membership_id")
    private TblApplicationMembership applicationMembershipByApplicationMembershipId;
}

这是我试图避免的服务层中的手动代码

tblApplication = applicationDataService.save(tblApplication);
Collection<TblApplicationAddress> addresses = tblApplicationMembership.getApplicationAddressesByApplicationMembershipId();
for (TblApplicationAddress entity : addresses) {
    entity.setApplicationMembershipByApplicationMembershipId(tblApplicationMembership);
}
tblApplication = applicationDataService.save(tblApplication);

我确信有一种方法可以配置 JPA 类来避免这种手动解决方法,但如果有人发现自己面临时间压力,并且需要解决方案,这就可以了。

© www.soinside.com 2019 - 2024. All rights reserved.