首页 > 解决方案 > Hibernate - Spring Boot - 将具有外键的对象保存到另一个表

问题描述

我使用 Hibernate 和 Spring Boot,但遇到以下问题:我想在 PostgreSQL 中保存我的 Framework 类型的对象:

public class Framework extends BaseEntity {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

@OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "sorting_type_id", unique = true)
    private FrameworkSortingType frameworkSortingType;

我的FrameworkSortingType实体:

@Entity(name = "framework_sorting_type")
@Data
@NoArgsConstructor
public class FrameworkSortingType extends BaseEntity {

    private static final long serialVersionUID = 1L;

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "environmental_id", referencedColumnName = "id")
    private SortingType environmental;

    @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "social_id", referencedColumnName = "id")
    private SortingType social;

    @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "governance_id", referencedColumnName = "id")
    private SortingType governance;
}

SortingType实体:

@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "sorting_type")
public class SortingType extends BaseEntity {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(name = "type")
    private String type;
}

我正在做以下测试 -Postman我想保存这样一个对象(没有 ID 的 frameworkSortingType 对象):

{
"id": 335799,
"frameworkSortingType": {
    "environmental":{
        "type": "alphabetically"
    },
     "social":{
        "type": "numerically"
    },
     "governance":{
        "type": "alphabetically"
    }
}
}

我的 SortingType 表只有 2 个常量行:

id type
1  alphabetically
2  numerically

而且我只想在我的表中存储FrameworkSortingType表的外键 ID,SortingType但目前存储在我FrameworkSortingType全新的 ID 中的SortingType表中,其中添加了具有新值的新行(并且SortingType表不应该被修改,因为应该像一个 ENUM 但 -> 已通过添加新行进行更改): SortingType表: 在此处输入图像描述

并且在我的FrameworkSortingType表中保存了全新的 ID,SortingType而不是像 1 或 2 这样的外键 ID(而不是 751801 应该是 1 作为 PK in SortingType,而不是 751803 应该是 2 作为 PK fromSortingType等): 在此处输入图像描述

问题出在哪里?我在哪里犯错了?也许我应该更改从前端发送的数据?当然,我想发送到后端值,如“按字母顺序”而不是 ID,因为每个数据库的 ID 可能不同。

编辑

当我从 FrameworkSortingType 实体中删除 cascade=CascadeType.ALL 时,一切都按预期工作,但我宁愿只从前端发送值而没有 SortingType 表的 ID(在我的情况下,它就像 DB 中的枚举)

标签: postgresqlspring-boothibernatespring-data-jpa

解决方案


您应该避免从任何类型的交互中更新主(也称为主要参考)表,您只能离线或通过某些管理界面更新它。此外,无论您在何处使用 JoinColumn该实体,复习都会成为该实体的拥有方。因此,在这种情况下,来自该实体的任何操作都将适用于 OneToOne 等关联注释。所以请谨慎使用 Cascade.ALL。

以下是我对实体所做的一些更改以及我是如何实现的。

@Entity
@Table(name = "sorting_type")
public class SortingType {
    @Id
    @GeneratedValue
    @Type(type = "uuid-char")
    private UUID id;

    @Enumerated(value = EnumType.STRING)
    private SortingTypeName name;


    public enum SortingTypeName {
        ALPHABETICAL,
        NUMERICALLY
    }

    public UUID getId() {
        return id;
    }

    public SortingTypeName getName() {
        return name;
    }

    public void setName(SortingTypeName name) {
        this.name = name;
    }

    public static SortingType newAlphabetSortingType() {
        SortingType sortingType = new SortingType();
        sortingType.name = SortingTypeName.ALPHABETICAL;
        return sortingType;
    }

    public static SortingType newNumericSortingType() {
        SortingType sortingType = new SortingType();
        sortingType.name = SortingTypeName.NUMERICALLY;
        return sortingType;
    }
}

@Entity
@Table(name = "framework_sorting_type")
public class FrameworkSortingType {
    @Id
    @GeneratedValue
    @Type(type = "uuid-char")
    private UUID id;

    @OneToOne
    @JoinColumn(name = "environmental_id")
    private SortingType environmental;

    @OneToOne
    @JoinColumn(name = "social_id")
    private SortingType social;

    @OneToOne
    @JoinColumn(name = "governance_id")
    private SortingType governance;

    public UUID getId() {
        return id;
    }

    public SortingType getEnvironmental() {
        return environmental;
    }

    public void setEnvironmental(SortingType environmental) {
        this.environmental = environmental;
    }

    public SortingType getSocial() {
        return social;
    }

    public void setSocial(SortingType social) {
        this.social = social;
    }

    public SortingType getGovernance() {
        return governance;
    }

    public void setGovernance(SortingType governance) {
        this.governance = governance;
    }
}

@Entity
@Table(name = "framework")
public class Framework {
    @Id
    @GeneratedValue
    @Type(type = "uuid-char")
    private UUID id;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "framework_id")
    private FrameworkSortingType frameworkSortingType;


    public UUID getId() {
        return id;
    }

    public FrameworkSortingType getFrameworkSortingType() {
        return frameworkSortingType;
    }

    public void setFrameworkSortingType(FrameworkSortingType frameworkSortingType) {
        this.frameworkSortingType = frameworkSortingType;
    }
}

单元测试来证明这个概念

@ExtendWith(SpringExtension.class)
@Transactional
@SpringBootTest(classes = TestApplication.class)
class MasterTableExample {

    @Autowired
    private EntityManager entityManager;


    @Rollback(false)
    @Test
    void testSortingTypeSave() {
        SortingType alphabetType = SortingType.newAlphabetSortingType();
        SortingType numericType = SortingType.newNumericSortingType();

        entityManager.persist(alphabetType);
        entityManager.persist(numericType);

        Assertions.assertNotNull(alphabetType.getId());
        Assertions.assertNotNull(numericType.getId());

        // Prepare FrameworkSortingType object
        FrameworkSortingType frameworkSortingType = new FrameworkSortingType();
        frameworkSortingType.setEnvironmental(searchSortingType(SortingType.SortingTypeName.ALPHABETICAL));
        frameworkSortingType.setSocial(searchSortingType(SortingType.SortingTypeName.NUMERICALLY));
        frameworkSortingType.setGovernance(searchSortingType(SortingType.SortingTypeName.NUMERICALLY));

        // Prepare Framework object
        Framework framework = new Framework();
        framework.setFrameworkSortingType(frameworkSortingType);
        entityManager.persist(framework);
        Assertions.assertNotNull(frameworkSortingType.getId());
        Assertions.assertNotNull(framework.getId());
    }

    private SortingType searchSortingType(SortingType.SortingTypeName name) {
        return (SortingType) entityManager.createQuery("select st from SortingType st where st.name='" + name + "'").getSingleResult();
    }
}

我使用Rollback(false)了以防万一您希望在测试执行后查看 db 中仍然存在的更改。

我们之前存储了 Enum 实体。当使用 FrameworkStoringType 创建框架时,无论SoringType表是否更新,数据都会持久保存。尝试添加entityManager.remove(framework);您将看到的语句的结尾framework以及frameworkstoringtype从数据库中删除的语句,并且 SortingType 持久行没有任何反应。你可以看到我没有使用entity.persist(frameworkSortingType)协会为我做的。

请注意,我已从中删除cascade = CascadeType.ALLFrameworkSortingType因为这可能会导致从SortingType表中额外添加和删除。


推荐阅读