一對多:
示例:客戶和聯系人關系
在實體類中,由於客戶是少的一方,它應該包含多個聯系人,所以實體類要體現出客戶中有多個聯系人的信息
/** * 客戶的實體類 */ @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") 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(targetEntity = LinkMan.class, fetch = FetchType.LAZY) // @JoinColumn(name = "lkm_cust_id", referencedColumnName = "cust_id") /** * 放棄外鍵維護權 * mappedBy: 對方配置關系的屬性名稱 * cascade: 配置級聯(可以配置到設置多表的映射關系的注解上) * CascadeType.all : 所有 * MERGE :更新 * PERSIST :保存 * REMOVE :刪除 * fetch : 配置關聯對象的加載方式 * EAGER :立即加載 * LAZY :延遲加載 */ @OneToMany(mappedBy = "customer") private Set<LinkMan> linkMans = new HashSet<>(0); /************************ get/set方法 ************************/ }
由於聯系人是多的一方,在實體類中要體現出,每個聯系人只能對應一個客戶
/** * 聯系人實體類 */ @Entity @Table(name = "cst_linkman") 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; /** * 配置聯系人到客戶的多對一關系 * 使用注解的形式配置多對一關系 * 1.配置表關系 * @ManyToOne : 配置多對一關系 * targetEntity:對方的實體類字節碼 * 2.配置外鍵(多對多配置中間表) * 配置外鍵的過程,配置到了多的一方,就會在多的一方維護外鍵 */ @ManyToOne(targetEntity = Customer.class) @JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id") private Customer customer; /************************ get/set方法 ************************/ }
持久層接口
/** * 客戶持久層接口 * JpaRepository<實體類類型,主鍵類型>:用來完成基本CRUD操作 * JpaSpecificationExecutor<實體類類型>:用於復雜查詢(分頁等查詢操作) */ public interface CustomerDao extends JpaRepository<Customer, Long>, JpaSpecificationExecutor<Customer> { }
/** * 聯系人持久層接口 */ public interface LinkManDao extends JpaRepository<LinkMan, Long>, JpaSpecificationExecutor<LinkMan> { }
測試新增記錄
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:applicationContext.xml") public class OneToManyTest { @Autowired private CustomerDao customerDao; @Autowired private LinkManDao linkManDao; /** * 保存一個客戶,保存一個聯系人 */ @Test @Transactional @Rollback(false) public void testAdd() { Customer customer = new Customer(); customer.setCustName("夫子"); LinkMan linkMan = new LinkMan(); linkMan.setLkmName("寧缺"); /** * 配置了客戶到聯系人的關系 * 從客戶的角度上:發送兩條insert語句,發送一條更新語句更新數據庫(更新外鍵) * 由於我們配置了客戶到聯系人的關系:客戶可以對外鍵進行維護 */ customer.getLinkMans().add(linkMan); /** * 配置聯系人到客戶的關系(多對一) * 只發送了兩條insert語句 * 由於配置了聯系人到客戶的映射關系(多對一):聯系人也可以對外鍵進行維護 */ // linkMan.setCustomer(customer); customerDao.save(customer); linkManDao.save(linkMan); } /** *** 最終建議使用方式 *** * 會有一條多余的update語句 * * 由於一的一方可以維護外鍵:會發送update語句 * * 解決此問題:只需要在一的一方放棄維護權即可 * */ @Test @Transactional @Rollback(false) public void testAdd2() { //創建一個客戶,創建一個聯系人 Customer customer = new Customer(); customer.setCustName("陳某"); LinkMan linkMan = new LinkMan(); linkMan.setLkmName("隆慶"); linkMan.setCustomer(customer);//由於配置了多的一方到一的一方的關聯關系(當保存的時候,就已經對外鍵賦值) customer.getLinkMans().add(linkMan);//由於配置了一的一方到多的一方的關聯關系(發送一條update語句) customerDao.save(customer); linkManDao.save(linkMan); } }
測試刪除記錄
@Test @Transactional @Rollback(false) public void testDelete() { customerDao.delete(1L); }
刪除操作的說明如下:
刪除從表數據:可以隨時任意刪除。
刪除主表數據:
有從表數據
1、在默認情況下,它會把外鍵字段置為null,然后刪除主表數據。如果在數據庫的表結構上,外鍵字段有非空約束,默認情況就會報錯。
2、如果配置了放棄維護關聯關系的權利,則不能刪除(與外鍵字段是否允許為null, 沒有關系)因為在刪除時,它根本不會去更新從表的外鍵字段了。
3、如果還想刪除,使用級聯刪除引用(慎用)
沒有從表數據引用:隨便刪
級聯操作:指操作一個對象同時操作它的關聯對象
使用方法:只需要在操作主體的注解上配置cascade
@OneToMany(mappedBy = "customer", cascade = CascadeType.ALL) private Set<LinkMan> linkMans = new HashSet<>(0);
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:applicationContext.xml") public class CascadeTest { @Autowired private CustomerDao customerDao; @Autowired private LinkManDao linkManDao; /** * 級聯添加:保存一個客戶的同時,保存客戶的所有聯系人 * 需要在操作主體的實體類上,配置casacde屬性 */ @Test @Transactional @Rollback(false) public void testCascadeAdd() { Customer customer = new Customer(); customer.setCustName("百度"); LinkMan linkMan = new LinkMan(); linkMan.setLkmName("小李"); linkMan.setCustomer(customer); customer.getLinkMans().add(linkMan); customerDao.save(customer); } /** * 級聯刪除: * 刪除1號客戶的同時,刪除1號客戶的所有聯系人 */ @Test @Transactional @Rollback(false) public void testCascadeRemove() { //1.查詢1號客戶 Customer customer = customerDao.findOne(1L); //2.刪除1號客戶 customerDao.delete(customer); } }
多對多:
示例:用戶和角色關系
多對多的表關系建立靠的是中間表,其中用戶表和中間表的關系是一對多,角色表和中間表的關系也是一對多
實體類關系建立以及映射配置:
一個用戶可以具有多個角色,所以在用戶實體類中應該包含多個角色的信息
/** * 用戶實體類 */ @Entity @Table(name = "sys_user") public class SysUser implements Serializable { @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;
/** * 配置用戶到角色的多對多關系 * 配置多對多的映射關系 * 1.聲明表關系的配置 * @ManyToMany(targetEntity = SysRole.class) //多對多 * targetEntity:代表對方的實體類字節碼 * 2.配置中間表(包含兩個外鍵) * @JoinTable * name : 中間表的名稱 * joinColumns:配置當前對象在中間表的外鍵 * @JoinColumn的數組 * name:外鍵名 * referencedColumnName:參照的主表的主鍵名 * inverseJoinColumns:配置對方對象在中間表的外鍵 */ @ManyToMany(targetEntity = SysRole.class) @JoinTable(name = "sys_user_role", //joinColumns,當前對象在中間表中的外鍵 joinColumns = {@JoinColumn(name = "sys_user_id",referencedColumnName = "user_id")}, //inverseJoinColumns,對方對象在中間表的外鍵 inverseJoinColumns = {@JoinColumn(name = "sys_role_id",referencedColumnName = "role_id")} ) private Set<SysRole> roles = new HashSet<SysRole>(0);
/************************ get/set方法 ************************/
}
一個角色可以賦予多個用戶,所以在角色實體類中應該包含多個用戶的信息
/** * 角色實體類 */ @Entity @Table(name = "sys_role") public class SysRole implements Serializable { @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(mappedBy = "roles") // 對方配置關系的屬性名稱,表示由對方來維護中間表關系 private Set<SysUser> users = new HashSet<SysUser>(0);
/************************ get/set方法 ************************/ }
持久層接口:
/** * 用戶持久層接口 */ public interface SysUserDao extends JpaRepository<SysUser, Long>, JpaSpecificationExecutor<SysUser> { }
/** * 角色持久層接口 */ public interface SysRoleDao extends JpaRepository<SysRole, Long>, JpaSpecificationExecutor<SysRole> { }
測試:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:applicationContext.xml") public class ManyToManyTest { @Autowired private SysRoleDao sysRoleDao; @Autowired private SysUserDao sysUserDao; /** * 保存一個用戶,保存一個角色 * 多對多放棄維護權:被動的一方放棄 */ @Test @Transactional @Rollback(false) public void testAdd() { SysUser sysUser = new SysUser(); sysUser.setUserName("小強"); SysRole sysRole = new SysRole(); sysRole.setRoleName("java程序員"); // 配置用戶到角色關系,可以對中間表中的數據進行維護 sysUser.getRoles().add(sysRole); // 配置角色到用戶的關系,可以對中間表的數據進行維護(放棄了維護) sysRole.getUsers().add(sysUser); sysUserDao.save(sysUser); sysRoleDao.save(sysRole); } }
在多對多(保存)中,如果雙向都設置關系,意味着雙方都維護中間表,都會往中間表插入數據,中間表的2個字段又作為聯合主鍵,主鍵重復,所以報錯
解決保存失敗的問題:只需要在任意一方放棄對中間表的維護權即可,推薦在被動的一方放棄,配置如下:
// 放棄對中間表的維護權,解決保存中主鍵沖突的問題
@ManyToMany(mappedBy = "roles")
private Set<SysUser> users = new HashSet<SysUser>(0);
多對多級聯操作:
和一對多一樣,只需要在操作主體的注解上配置cascade
@ManyToMany(targetEntity = SysRole.class, cascade = CascadeType.ALL) @JoinTable(name = "sys_user_role", //joinColumns,當前對象在中間表中的外鍵 joinColumns = {@JoinColumn(name = "sys_user_id",referencedColumnName = "user_id")}, //inverseJoinColumns,對方對象在中間表的外鍵 inverseJoinColumns = {@JoinColumn(name = "sys_role_id",referencedColumnName = "role_id")} ) private Set<SysRole> roles = new HashSet<SysRole>(0);
測試:
/** * 測試級聯添加(保存一個用戶的同時保存用戶的關聯角色) */ @Test @Transactional @Rollback(false) public void testCasCadeAdd() { SysUser sysUser = new SysUser(); sysUser.setUserName("小李"); SysRole sysRole = new SysRole(); sysRole.setRoleName("java程序員"); sysUser.getRoles().add(sysRole); sysRole.getUsers().add(sysUser); sysUserDao.save(sysUser); } /** * 刪除操作 * 在多對多的刪除時,雙向級聯刪除根本不能配置 * 禁用 * 如果配了的話,如果數據之間有相互引用關系,可能會清空所有數據 * * 案例:刪除id為1的用戶,同時刪除他的關聯對象 */ @Test @Transactional @Rollback(false) public void testCasCadeRemove() { //查詢1號用戶 SysUser user = sysUserDao.findOne(1L); //刪除1號用戶 sysUserDao.delete(user); }