CascadeType.ALL 和 "insertable = false, updatable = false" 互相排斥吗?
Do CascadeType.ALL and "insertable = false, updatable = false" exclude each other?
如果我有这样的配置,可以吗?
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinColumn(name = "FOREIGN_ID", nullable = false, insertable = false, updatable = false)
ForeignClass foreignClass;
我认为不是,因为级联类型的行为与可插入和可更新参数冲突。
你怎么看?
长话短说,不,这些设置并不相互排斥。
想象一下,我们有以下模型。
create table APP_USER
(
USER_ID number,
USER_NAME varchar2(400),
constraint APP_USER_PK primary key(USER_ID)
);
create table APP_BILLING_NUMBER
(
BILL_ID number,
BILL_ACCOUNT varchar2(20),
BILL_BANK_NAME varchar2(300),
BILL_USER_ID number,
constraint APP_BILLING_NUMBER_PK primary key(BILL_ID),
);
以及适当的休眠映射:
@Entity
@Table(name = "APP_USER")
public class User
{
@Id
@Column(name = "USER_ID")
private Long id;
@Column(name = "USER_NAME")
private String name;
}
@Entity
@Table(name = "APP_BILLING_NUMBER")
public class BillingNumber
{
@Id
@Column(name = "BILL_ID")
private Long id;
@Column(name = "BILL_ACCOUNT")
private String account;
@Column(name = "BILL_BANK_NAME")
private String bankName;
@ManyToOne
@JoinColumn(name="BILL_USER_ID")
private User user;
}
如果我们尝试做这样的事情:
User user = new User();
user.setId(1L);
user.setName("Alex");
BillingNumber bill = new BillingNumber();
bill.setId(1L);
bill.setAccount("EA12345678");
bill.setBankName("BNP Paribas");
bill.setUser(user);
session.persist(bill);
我们会发现账单被添加到 APP_BILLING_NUMBER
table,
SQL> select * from APP_BILLING_NUMBER;
BILL_ID BILL_ACCOUNT BILL_BANK_NAME BILL_USER_ID
------- ------------ -------------- ------------
1 EA12345678 BNP Paribas 1
但用户不是。发生这种情况是因为默认情况下我们有 @ManyToOne(cascade={})
。如果我们想用 BillingNumber
实体持久化 User
实体,我们应该添加 CascadeType.PERSIST
:
@ManyToOne(cascade = {CascadeType.PERSIST})
@JoinColumn(name="BILL_USER_ID")
private User user;
那么,如果我们要使用 session.merge(bill)
操作,我们也应该添加 CascadeType.MERGE
。
现在想象一下,我们有
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinColumn(name="BILL_USER_ID", insertable = false, updatable = false)
private User user;
执行完上面的持久化操作后我们会得到:
SQL> select * from APP_USER;
USER_ID USER_NAME
------- ---------
1 Alex
SQL> select * from APP_BILLING_NUMBER;
BILL_ID BILL_ACCOUNT BILL_BANK_NAME BILL_USER_ID
------- ------------ -------------- ------------
1 EA12345678 BNP Paribas (null)
如您所见,两个实体都已插入,但 APP_BILLING_NUMBER.BILL_USER_ID
引用是 null
,因为 @JoinColumn(..., insertable = false, ...)
。而且你不能更新它因为 updatable = false
.
所以,这是有效但非常奇特的案例。
意思不同,互不冲突。
所以给定以下映射:
@Entity
@Table(name="player")
public class Player {
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinColumn(name = "team_id", nullable = false, insertable = false, updatable = false)
Team team;
}
cascade
表示在 player
上使用 EntityManager
到 persist()
或 merge()
时,JPA 将 自动呼叫persist()
/merge()
此玩家的队伍。
insertable
和 updatable
是关于是否允许分配新团队或更新玩家的团队。就数据库 table 而言,它是关于是否允许插入或更新 Player
table 的 team_id
列的值。
所以一个是关于 inserting/updating Team
table 中的记录,而另一个是关于 inserting/updating Player
table 的值的 team_id
专栏,它们完全不同。
如果我有这样的配置,可以吗?
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinColumn(name = "FOREIGN_ID", nullable = false, insertable = false, updatable = false)
ForeignClass foreignClass;
我认为不是,因为级联类型的行为与可插入和可更新参数冲突。
你怎么看?
长话短说,不,这些设置并不相互排斥。
想象一下,我们有以下模型。
create table APP_USER
(
USER_ID number,
USER_NAME varchar2(400),
constraint APP_USER_PK primary key(USER_ID)
);
create table APP_BILLING_NUMBER
(
BILL_ID number,
BILL_ACCOUNT varchar2(20),
BILL_BANK_NAME varchar2(300),
BILL_USER_ID number,
constraint APP_BILLING_NUMBER_PK primary key(BILL_ID),
);
以及适当的休眠映射:
@Entity
@Table(name = "APP_USER")
public class User
{
@Id
@Column(name = "USER_ID")
private Long id;
@Column(name = "USER_NAME")
private String name;
}
@Entity
@Table(name = "APP_BILLING_NUMBER")
public class BillingNumber
{
@Id
@Column(name = "BILL_ID")
private Long id;
@Column(name = "BILL_ACCOUNT")
private String account;
@Column(name = "BILL_BANK_NAME")
private String bankName;
@ManyToOne
@JoinColumn(name="BILL_USER_ID")
private User user;
}
如果我们尝试做这样的事情:
User user = new User();
user.setId(1L);
user.setName("Alex");
BillingNumber bill = new BillingNumber();
bill.setId(1L);
bill.setAccount("EA12345678");
bill.setBankName("BNP Paribas");
bill.setUser(user);
session.persist(bill);
我们会发现账单被添加到 APP_BILLING_NUMBER
table,
SQL> select * from APP_BILLING_NUMBER;
BILL_ID BILL_ACCOUNT BILL_BANK_NAME BILL_USER_ID
------- ------------ -------------- ------------
1 EA12345678 BNP Paribas 1
但用户不是。发生这种情况是因为默认情况下我们有 @ManyToOne(cascade={})
。如果我们想用 BillingNumber
实体持久化 User
实体,我们应该添加 CascadeType.PERSIST
:
@ManyToOne(cascade = {CascadeType.PERSIST})
@JoinColumn(name="BILL_USER_ID")
private User user;
那么,如果我们要使用 session.merge(bill)
操作,我们也应该添加 CascadeType.MERGE
。
现在想象一下,我们有
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinColumn(name="BILL_USER_ID", insertable = false, updatable = false)
private User user;
执行完上面的持久化操作后我们会得到:
SQL> select * from APP_USER;
USER_ID USER_NAME
------- ---------
1 Alex
SQL> select * from APP_BILLING_NUMBER;
BILL_ID BILL_ACCOUNT BILL_BANK_NAME BILL_USER_ID
------- ------------ -------------- ------------
1 EA12345678 BNP Paribas (null)
如您所见,两个实体都已插入,但 APP_BILLING_NUMBER.BILL_USER_ID
引用是 null
,因为 @JoinColumn(..., insertable = false, ...)
。而且你不能更新它因为 updatable = false
.
所以,这是有效但非常奇特的案例。
意思不同,互不冲突。
所以给定以下映射:
@Entity
@Table(name="player")
public class Player {
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinColumn(name = "team_id", nullable = false, insertable = false, updatable = false)
Team team;
}
cascade
表示在player
上使用EntityManager
到persist()
或merge()
时,JPA 将 自动呼叫persist()
/merge()
此玩家的队伍。insertable
和updatable
是关于是否允许分配新团队或更新玩家的团队。就数据库 table 而言,它是关于是否允许插入或更新Player
table 的team_id
列的值。
所以一个是关于 inserting/updating Team
table 中的记录,而另一个是关于 inserting/updating Player
table 的值的 team_id
专栏,它们完全不同。