级联操作
指操作一个对象同时操作它的关联对象
在操作主体的注解上配置cascade
- CascadeType.MERGE 级联更新
- CascadeType.REFRESH 级联刷新:
- CascadeType.REMOVE 级联删除:
- CascadeType.PERSIST 级联保存:
- CascadeType.ALL 包含所有
@OneToMany(mappedBy="customer",cascade=CascadeType.ALL)
一对多 多对一
@OneToMany
- targetEntityClass:指定多的多方的类的字节码
- mappedBy:指定从表实体类中引用主表对象的名称。
- cascade:指定要使用的级联操作
- fetch:指定是否采用延迟加载
- orphanRemoval:是否使用孤儿删除
@ManyToOne
- targetEntityClass:指定一的一方实体类字节码
- cascade:指定要使用的级联操作
- fetch:指定是否采用延迟加载
- optional:关联是否可选。如果设置为false,则必须始终存在非空关系。
@JoinColumn
用于定义主键字段和外键字段的对应关系
- name:指定外键字段的名称
- referencedColumnName:指定引用主表的主键字段名称
- unique:是否唯一。默认值不唯一
- nullable:是否允许为空。默认值允许
- insertable:是否允许插入。默认值允许。
- updatable:是否允许更新。默认值允
- columnDefinition:列的定义信息。
@Getter
@Setter
@Entity
@Table(name="cst_customer")
public class Customer implements Serializable {
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
@Column(name="cust_id")
private Long custId;
@Column(name="cust_name",columnDefinition = "varchar(32) NOT NULL")
private String custName;
@Column(name="cust_source")
private String custSource;
@Column(name="cust_industry")
private String custIndustry;
@Column(name="cust_level")
private String custLevel;
@Column(name="cust_address")
private String custAddress;
@Column(name="cust_phone")
private String custPhone;
@OneToMany(mappedBy = "customer",cascade = CascadeType.ALL)
private Set<LinkMan> linkMans = new HashSet<>();
}
@Getter
@Setter
@Entity
@Table(name="cst_linkman")
@EqualsAndHashCode
public class LinkMan {
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
@Column(name="lkm_id")
private Long lkmId;
@Column(name="lkm_name")
private String lkmName;
@Column(name="lkm_gender")
private String lkmGender;
@Column(name="lkm_phone")
private String lkmPhone;
@Column(name="lkm_mobile")
private String lkmMobile;
@Column(name="lkm_email")
private String lkmEmail;
@Column(name="lkm_position")
private String lkmPosition;
@Column(name="lkm_memo")
private String lkmMemo;
@ManyToOne(targetEntity = Customer.class,fetch = FetchType.LAZY)
@JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
private Customer customer;
}
当我们建立了双向的关联关系之后,先保存主表,再保存从表时:
会产生2条insert和1条update.
而实际开发中我们只需要2条insert。
通过保存的案例,我们可以发现在设置了双向关系之后,会发送两条insert语句,一条多余的update语句,那我们的解决是思路很简单,就是一的一方放弃维护权
// @OneToMany(targetEntity=LinkMan.class)
// @JoinColumn(name="lkm_cust_id",referencedColumnName="cust_id")
@OneToMany(mappedBy="customer")
private Set<LinkMan> linkmans = new HashSet<LinkMan>(0);
Customer c = new Customer();
c.setCustName("TBD云集中心");
LinkMan l = new LinkMan();
l.setLkmName("TBD联系人");
l.setLkmMemo("还行吧");
LinkMan l2 = new LinkMan();
l2.setLkmName("TBD联系人1");
c.getLinkMans().add(l);
l.setCustomer(c);
c.getLinkMans().add(l2);
l2.setCustomer(c);
customerDao.save(c);
linkManDao.save(l);
linkManDao.save(l2);
删除
- 删除从表数据:可以随时任意删除
- 删除主表数据
- 在默认情况下,它会把外键字段置为null,然后删除主表数据。如果在数据库的表 结构上,外键字段有非空约束,默认情况就会报错了。
- 如果配置了放弃维护关联关系的权利,则不能删除(与外键字段是否允许为null, 没有关系)因为在删除时,它根本不会去更新从表的外键字段了。
- 如果还想删除,使用级联删除引用
@OneToMany(mappedBy = "customer",cascade = CascadeType.ALL)
private Set<LinkMan> linkMans = new HashSet<>();
@Test
public void one2ManyDelete(){
customerDao.deleteById(9L);
}
多对多
@ManyToMany
cascade:配置级联操作。
fetch:配置是否采用延迟加载。
targetEntity:配置目标的实体类。映射多对多的时候不用写。
@JoinTable
针对中间表的配置
nam:配置中间表的名称
joinColumns:中间表的外键字段关联当前实体类所对应表的主键字段
inverseJoinColumn:中间表的外键字段关联对方表的主键字段
@JoinColumn
用于定义主键字段和外键字段的对应关系
- name:指定外键字段的名称
- referencedColumnName:指定引用主表的主键字段名称
- unique:是否唯一。默认值不唯一
- nullable:是否允许为空。默认值允许
- insertable:是否允许插入。默认值允许。
- updatable:是否允许更新。默认值允
- columnDefinition:列的定义信息。
@Entity
@Table(name="sys_role")
@Setter
@Getter
public class SysRole {
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
@Column(name="role_id")
private Long roleId;
@Column(name="role_name")
private String roleName;
@Column(name="role_memo")
private String roleMemo;
@ManyToMany
@JoinTable(name="sys_user_role",
joinColumns={@JoinColumn(name="role_id",referencedColumnName="role_id")},
inverseJoinColumns={@JoinColumn(name="user_id",referencedColumnName="user_id")}
)
private Set<SysUser> users = new HashSet<SysUser>(0);
}
@Entity
@Table(name="sys_user")
@Setter
@Getter
public class SysUser {
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
@Column(name="user_id")
private Long userId;
@Column(name="user_code")
private String userCode;
@Column(name="user_name")
private String userName;
@Column(name="user_password")
private String userPassword;
@Column(name="user_state")
private String userState;
@ManyToMany(mappedBy="users")
private Set<SysRole> roles = new HashSet<SysRole>(0);
}
在多对多(保存)中,如果双向都设置关系,意味着双方都维护中间表,都会往中间表插入数据,中间表的2个字段又作为联合主键,所以报错,主键重复,解决保存失败的问题:只需要在任意一方放弃对中间表的维护权即可,推荐在被动的一方放弃,配置如下:
//放弃对中间表的维护权,解决保存中主键冲突的问题
@ManyToMany(mappedBy="roles")
private Set<SysUser> users = new HashSet<SysUser>(0);