JPA + Hibernate:如何定义一个具有ON DELETE CASCADE的约束?

15

我在想是否有一种方法可以构建我的MySQL表格,如下所示:

ALTER TABLE `USERINFO`
  ADD CONSTRAINT `FK_USER_ID` FOREIGN KEY (`USERID`) REFERENCES `USERACCOUNT` (`USERID`) 
    ON DELETE CASCADE 
    ON UPDATE CASCADE;

然而,只有当 Hibernate++JPA 开始构建我的表时,我才会在我的 DDL 中得到这个:<property name="hibernate.hbm2ddl.auto" value="create" />

ALTER TABLE `USERINFO` ADD CONSTRAINT `FK_USER_ID` FOREIGN KEY (`USERID`) REFERENCES `USERACCOUNT` (`USERID`);

在我的课程中,我设置了这些注释。
// UserAcc.java
@Entity
@Table(name = "USERACC")
public class UserAcc implements Serializable {

private static final long serialVersionUID = -5527566248002296042L;

@Id
@Column(name = "USERID")
@GeneratedValue(strategy=GenerationType.AUTO)
private Integer userId;


@OneToOne(mappedBy = "userAcc")
private UserInfo userInfo;
....


public UserInfo getUserInfo() {
    return userInfo;
}
public void setUserInfo(UserInfo userInfo) {
    this.userInfo = userInfo;
}
...

并且,
// UserInfo.java
@Entity
@Table(name = "USERINFO")
public class UserInfo implements Serializable {

private static final long serialVersionUID = 5924361831551833717L;

@Id
@Column(name = "USERINFO_ID", nullable=false)
@GeneratedValue(strategy=GenerationType.AUTO)
private Integer userInfoId;

@OneToOne(cascade = {CascadeType.ALL})
@JoinColumn(name="USERID", nullable=false)
@ForeignKey(name = "FK_USER_ID")
private UserAcc userAcc;


public Integer getUserInfoId() {
    return userInfoId;
}

public void setUserInfoId(Integer userInfoId) {
    this.userInfoId = userInfoId;
}
...

请注意,用户账户表(UserAccount table) 是主表(parent/main table),而UserInfo是扩展表(extended table),用于规范化另一个实体。非常感谢您的任何答案。我很想知道这是如何完成的,因为我也喜欢在MySQL中工作。我通常会从主表(USERACOUNT)中删除记录,这也会允许我级联删除依赖于来自父/主表的特定记录的子记录。

谢谢!


你使用jpa找到了解决这个问题的方法吗?我也遇到了同样的问题,但我不想使用双向关系,在父级上创建延迟获取。 - Stefania
5个回答

16

JPA提供了级联操作(合并、持久化、刷新、删除)到关联实体的可能性。逻辑在JPA中,不使用数据库级联。

@OneToMany(cascade=CascadeType.REMOVE)

没有符合JPA标准的方法可以实现数据库级联。如果需要这样的级联操作,我们必须使用Hibernate特有的@OnDelete构造。它至少可以用于@OneToMany,但过去在@OneToOne@OnDelete方面存在一些问题。

@OnDelete(action = OnDeleteAction.CASCADE)

请注意,将此注释添加到现有的约束条件中不会更新它。您可能需要手动删除它以正确更新模式。


1
谢谢Mikko。我尝试使用JPA注释包含OnDelete,但实际上它并没有起作用。我有一个问题,当您想要操作数据库并想要添加ON DELETE CASCADE,特别是MySQL时,您如何处理它? - toytoy

11

在JPA中没有一种干净利落的方法来实现这个。以下方法可以帮助您达到目标... 您可以使用 CascadeType.DELETE,但是此注释仅适用于EntityManager中的对象,而不适用于数据库。 您需要确保将ON DELETE CASCADE添加到数据库约束中。 为了验证,您可以配置JPA生成一个ddl文件。 查看ddl文件,您会发现ON DELETE CASCADE不是约束的一部分。 在ddl文件中添加ON DELETE CASCADE到实际的SQL语句,然后从ddl更新您的数据库模式。 这将解决您的问题。

链接演示了如何在MySQL中使用ON DELETE CASCADE来进行CONSTRAINT。 您可以在约束上执行此操作。 您还可以在CREATE TABLEALTER TABLE语句中执行此操作。 JPA可能会在ALTER TABLE语句中创建约束。 只需将ON DELETE CASCADE添加到该语句即可。

请注意,某些JPA实现者确实提供了此功能的手段。

最后,Hibernate使用OnDelete(action = OnDeleteAction.CASCADE)注释提供此功能。


4
您可以在 UserAcc.userInfo 上使用 Hibernate 注释 org.hibernate.annotations@OnDelete(action = OnDeleteAction.CASCADE):
public class UserAcc implements Serializable {

...

@OneToOne(mappedBy = "userAcc")
@OnDelete(action = OnDeleteAction.CASCADE)
private UserInfo userInfo;

...

这将在 UserInfo 表的外键上生成 ON DELETE CASCADE DDL。


1

您可以通过使用@JoinColumn(foreignKey = @ForeignKey(...))手动定义约束,如下所示:

// UserInfo.java
@Entity
@Table(name = "USERINFO")
public class UserInfo implements Serializable {

private static final long serialVersionUID = 5924361831551833717L;

@Id
@Column(name = "USERINFO_ID", nullable=false)
@GeneratedValue(strategy=GenerationType.AUTO)
private Integer userInfoId;

@OneToOne(cascade = {CascadeType.ALL})
@JoinColumn(
        name="USERID", 
        nullable=false,
        foreignKey = @ForeignKey(
                name="FK_USER_ID",
                foreignKeyDefinition = "FOREIGN KEY (USERID) REFERENCES USERACCOUNT(USERID) ON UPDATE CASCADE ON DELETE CASCADE"
        )
)
private UserAcc userAcc;


public Integer getUserInfoId() {
    return userInfoId;
}

public void setUserInfoId(Integer userInfoId) {
    this.userInfoId = userInfoId;
}
...

这将在生成 DDL 期间添加约束。

完成生成过程后,您可以检查 USERINFO 表的 DDL,您会发现该约束已经添加,并显示如下级联操作:

create table USERINFO(
    ....,
    ....,
    ....,
    ....,
    constraint FK_USER_ID foreign key (USERID) references USERACCOUNT(USERID) on update cascade on delete cascade
);

0
请配置您的MySQL,修改表格外键on update noactionon delete no action,然后应用。
 @OnDelete(action = OnDeleteAction.NO_ACTION)
    private Set<Course> courses = new HashSet<>();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接