【问题标题】:Hibernate - Spring Boot - save object with foreign key to the other tableHibernate - Spring Boot - 将具有外键的对象保存到另一个表
【发布时间】:2020-10-14 23:45:21
【问题描述】:

我使用 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 的框架排序类型对象):

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

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

id type
1  alphabetically
2  numerically

我想在我的FrameworkSortingType 表中只存储SortingType 表的外键ID,但目前我的FrameworkSortingType 中存储了SortingType 表的全新ID,其中添加了新的具有新值的行(并且不应修改 SortingType 表,因为它应该像 ENUM 一样工作,但 -> 已通过添加新行进行了更改): SortingType 表:

在我的FrameworkSortingType 表中,将全新的 ID 保存到 SortingType 行,而不是像 1 或 2 这样的外键 ID(而不是 751801 应该是 1,因为 SortingType 中的 PK,而不是 751803 应该是2 作为来自SortingType 等的PK):

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

编辑

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

【问题讨论】:

  • [按字母顺序,按数字顺序]是来自主表的枚举,即SortingType,这将在启动或手动输入时仅生成一次,您应避免对该表进行任何更新或插入操作。跨度>

标签: postgresql spring-boot hibernate spring-data-jpa


【解决方案1】:

您应该避免通过任何类型的交互来更新主(也称为主要参考)表,并且您只能离线或通过某些管理界面更新它。此外,无论您在何处使用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),以防万一您希望在测试执行后查看数据库中仍然存在的更改。

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-02-10
    • 2020-08-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-05
    • 2015-07-02
    相关资源
    最近更新 更多