一、表關系的分析
數據庫中多表之間存在着三種關系,也就是系統設計中的三種實體關系。如圖所示。
1.1 表與表的三種關系




聯系人表中存在外鍵(lkm_cust_id),外鍵指向客戶表,表示如下圖:
2.2 創建實體:
客戶實體:
package com.Kevin.domain; /** * 創建客戶實體類 * */ import java.io.Serializable; import java.util.HashSet; import java.util.Set; public class Customer implements Serializable { private Long cust_id; private String cust_name; private String cust_address; private String cust_source; private String cust_industry; private String cust_level; private String cust_phone; private String cust_mobile; private Set<Linkman> linkmans=new HashSet<Linkman>(0); //一對多關系映射:多的一方 //主表實體應該包含從表實體的集合引用 public Set<Linkman> getLinkmans() { return linkmans; } public void setLinkmans(Set<Linkman> linkmans) { this.linkmans = linkmans; } public Long getCust_id() { return cust_id; } public void setCust_id(Long cust_id) { this.cust_id = cust_id; } public String getCust_address() { return cust_address; } public void setCust_address(String cust_address) { this.cust_address = cust_address; } public String getCust_name() { return cust_name; } public void setCust_name(String cust_name) { this.cust_name = cust_name; } public String getCust_source() { return cust_source; } public void setCust_source(String cust_source) { this.cust_source = cust_source; } public String getCust_industry() { return cust_industry; } public void setCust_industry(String cust_industry) { this.cust_industry = cust_industry; } public String getCust_level() { return cust_level; } public void setCust_level(String cust_level) { this.cust_level = cust_level; } public String getCust_phone() { return cust_phone; } public void setCust_phone(String cust_phone) { this.cust_phone = cust_phone; } public String getCust_mobile() { return cust_mobile; } public void setCust_mobile(String cust_mobile) { this.cust_mobile = cust_mobile; } @Override public String toString() { return "Customer [cust_id=" + cust_id + ", cust_address=" + cust_address + ", cust_name=" + cust_name + ", cust_source=" + cust_source + ", cust_industry=" + cust_industry + ", cust_level=" + cust_level + ", cust_phone=" + cust_phone + ", cust_mobile=" + cust_mobile + "]"; } }
聯系人實體:
package com.Kevin.domain; /** * 創建聯系人實體類 * */ import java.io.Serializable; public class Linkman implements Serializable { private Long lkm_id; private String lkm_name; private String lkm_gender; private String lkm_mobile; private String lkm_phone; private String lkm_email; private String lkm_qq; private String lkm_position; private String lkm_memo; //一對多關系影射 //從表實體包含主表實體的對象引用 private Customer customer; public Customer getCustomer() { return customer; } public void setCustomer(Customer customer) { this.customer = customer; } public Long getLkm_id() { return lkm_id; } public void setLkm_id(Long lkm_id) { this.lkm_id = lkm_id; } public String getLkm_name() { return lkm_name; } public void setLkm_name(String lkm_name) { this.lkm_name = lkm_name; } public String getLkm_gender() { return lkm_gender; } public void setLkm_gender(String lkm_gender) { this.lkm_gender = lkm_gender; } public String getLkm_mobile() { return lkm_mobile; } public void setLkm_mobile(String lkm_mobile) { this.lkm_mobile = lkm_mobile; } public String getLkm_phone() { return lkm_phone; } public void setLkm_phone(String lkm_phone) { this.lkm_phone = lkm_phone; } public String getLkm_email() { return lkm_email; } public void setLkm_email(String lkm_email) { this.lkm_email = lkm_email; } public String getLkm_qq() { return lkm_qq; } public void setLkm_qq(String lkm_qq) { this.lkm_qq = lkm_qq; } public String getLkm_position() { return lkm_position; } public void setLkm_position(String lkm_position) { this.lkm_position = lkm_position; } public String getLkm_memo() { return lkm_memo; } public void setLkm_memo(String lkm_memo) { this.lkm_memo = lkm_memo; } @Override public String toString() { return "Linkman [lkm_id=" + lkm_id + ", lkm_name=" + lkm_name + ", lkm_gender=" + lkm_gender + ", lkm_mobile=" + lkm_mobile + ", lkm_phone=" + lkm_phone + ", lkm_email=" + lkm_email + ", lkm_qq=" + lkm_qq + ", lkm_position=" + lkm_position + ", lkm_memo=" + lkm_memo + "]"; } }
2.3 創建映射
客戶映射:
<?xml version="1.0" encoding="UTF-8"?> <!-- 創建客戶類關系映射 導入dtd約束 --> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <!-- private Long cust_id; private String cust_address; private String cust_name; private String cust_source; private String cust_industry; private String cust_level; private String cust_phone; private String cust_mobile; --> <hibernate-mapping package="com.Kevin.domain"> <class name="Customer" table="cust_customer" lazy="false"> <id name="cust_id"> <generator class="native"></generator> </id> <property name="cust_name" column="cust_name"></property> <property name="cust_address" column="cust_address"></property> <property name="cust_source" column="cust_source"></property> <property name="cust_industry" column="cust_industry"></property> <property name="cust_level" column="cust_level"></property> <property name="cust_phone" column="cust_phone"></property> <property name="cust_mobile" column="cust_mobile"></property> <!-- 一對多關系影射:主表實體的映射配置 涉及的標簽:set:用於配置set集合屬性 屬性:name:指定實體類中set集合的屬性名稱 table:指定從表的名稱,在一對多配置時可以不寫 key:用於映射外鍵字段 屬性:column:指定外鍵字段名稱 one—to—many:用於建立一對多的映射配置 屬性:class:指定從表實體類的名稱 --> <set name="linkmans" table="cust_linkman" > <key column="lkm_cust_id"></key> <one-to-many class="Linkman"/> </set> </class> </hibernate-mapping>
聯系人映射:
<?xml version="1.0" encoding="UTF-8"?> <!-- 創建聯系人實體類映射 導入dtd約束 --> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <!-- private Long lkm_id; private String lkm_name; private String lkm_gender; private String lkm_mobile; private String lkm_phone; private String lkm_email; private String lkm_qq; private String lkm_position; private String lkm_memo; --> <hibernate-mapping package="com.Kevin.domain"> <class name="Linkman" table="cust_linkman"> <id name="lkm_id"> <generator class="native"></generator> </id> <property name="lkm_name" column="lkm_name"></property> <property name="lkm_gender" column="lkm_gender"></property> <property name="lkm_mobile" column="lkm_mobile"></property> <property name="lkm_phone" column="lkm_phone"></property> <property name="lkm_email" column="lkm_email"></property> <property name="lkm_qq" column="lkm_qq"></property> <property name="lkm_position" column="lkm_position"></property> <property name="lkm_memo" column="lkm_memo"></property> <!-- 一對多關系影射:從表實體的映射配置 涉及的標簽:many-to-one:建立多對一的映射配置 屬性:name:從表示實體中引用主表實體對象的引用的名稱 class:指定屬性所對應的實體類名稱 --> <many-to-one name="customer" class="Customer" column="lkm_cust_id" ></many-to-one> </class> </hibernate-mapping>
customer在 Linkman.java類中的屬性的名稱,class屬性用來指定映射的類,column屬性值對應表中的外鍵列名。
2.4 將映射添加到配置文件
<mapping resource="com/Kevin/domain/Linkman.hbm.xml"/> <mapping resource="com/Kevin/domain/Customer.hbm.xml"/>
2.5 編寫測試代碼
@Test public void test2(){ Session s=HibernateUtil.getCurrSession(); Transaction tx=s.beginTransaction(); //1.創建一個客戶 Customer c1=new Customer(); //瞬時態 c1.setCust_name("Kevin_one2many"); //2.創建一個新的聯系人 Linkman lkm1=new Linkman(); //瞬時態 lkm1.setLkm_name("Kevin_one2many1"); Linkman lkm2=new Linkman(); //瞬時態 lkm2.setLkm_name("Kevin_one2many2"); //3.建立客戶和聯系人的關聯關系(讓雙向) lkm1.setCustomer(c1); lkm2.setCustomer(c1); c1.getLinkmans().add(lkm1); c1.getLinkmans().add(lkm2); //4.保存要符合原則 s.save(c1); //持久態 有一級緩存和快照 s.save(lkm1); //持久態 有一級緩存和快照 s.save(lkm2); tx.commit(); }
在配置文件中添加了自動建表信息后,運行程序時,程序會自動創建兩張表,並且插入數據。運行方法后,控制台輸出結果如所示:
從圖的輸出結果可以看到,控制台成功輸出了三條insert語句和兩條update語句,此時查詢數據庫中的數據如圖所示:
從上圖的查詢結果可以看出,數據表創建成功,並成功插入了相應的數據。那么一個基本的一對多的關聯關系映射就已經配置好了。從以上代碼我們可以發現我們建立的關系是雙向的,即客戶關聯了聯系人,同時聯系人也關聯了客戶。
三、一對多的相關操作
級聯操作是指當主控方執行保存、更新或者刪除操作時,其關聯對象(被控方)也執行相同的操作。在映射文件中通過對 cascade屬性的設置來控制是否對關聯對象采用級聯操作,級聯操作對各種關聯關系都是有效的。
3.1 級聯保存或更新
首先要確定我們要保存的主控方是那一方,我們要保存客戶,所以客戶是主控方,那么需要在客戶的映射文件中進行如下的配置。
<!-- 一對多關系影射:主表實體的映射配置 涉及的標簽:set:用於配置set集合屬性 屬性:name:指定實體類中set集合的屬性名稱 table:指定從表的名稱,在一對多配置時可以不寫 key:用於映射外鍵字段 屬性:column:指定外鍵字段名稱 one—to—many:用於建立一對多的映射配置 屬性:class:指定從表實體類的名稱 --> <set name="linkmans" table="cust_linkman" cascade="save-update"> <key column="lkm_cust_id"></key> <one-to-many class="Linkman"/> </set>
然后可以編寫如下測試代碼:
@Test public void test(){ Session s=HibernateUtil.getCurrSession(); Transaction tx=s.beginTransaction(); //1.創建一個客戶 Customer c1=new Customer(); //瞬時態 c1.setCust_name("Kevin"); //2.創建一個新的聯系人 Linkman lkm1=new Linkman(); //瞬時態 lkm1.setLkm_name("Kevin_one2many"); //3.建立客戶和聯系人的關聯關系(讓雙向) lkm1.setCustomer(c1); c1.getLinkmans().add(lkm1); //4.保存要符合原則 s.save(c1); //持久態 有一級緩存和快照 tx.commit(); }
保存聯系人級聯客戶
同樣我們需要確定主控方,現在我們的主控方是聯系人。所以需要在聯系人的映射文件中進行配置,內容如下:
<!-- 一對多關系影射:從表實體的映射配置 涉及的標簽:many-to-one:建立多對一的映射配置 屬性:name:從表示實體中引用主表實體對象的引用的名稱 class:指定屬性所對應的實體類名稱 --> <many-to-one name="customer" class="Customer" column="lkm_cust_id" cascade="save-update"></many-to-one>
編寫如下測試代碼:
@Test public void test(){ Session s=HibernateUtil.getCurrSession(); Transaction tx=s.beginTransaction(); //1.創建一個客戶 Customer c1=new Customer(); //瞬時態 c1.setCust_name("Kevin"); //2.創建一個新的聯系人 Linkman lkm1=new Linkman(); //瞬時態 lkm1.setLkm_name("Kevin_one2many"); //3.建立客戶和聯系人的關聯關系(讓雙向) lkm1.setCustomer(c1); c1.getLinkmans().add(lkm1); //4.保存要符合原則 s.save(lkm1); //持久態 有一級緩存和快照 tx.commit(); }
3.2 級聯更新
@Test public void test5(){ Session s=HibernateUtil.getCurrSession(); Transaction tx=s.beginTransaction(); //1.查詢一個客戶 Customer c1=s.get(Customer.class, 1l); //2.創建一個新的聯系人 Linkman lkm=new Linkman(); //瞬時態 lkm.setLkm_name("one2many_update"); //3.建立客戶和聯系人的關聯關系(雙向) lkm.setCustomer(c1); c1.getLinkmans().add(lkm); //4.更新聯系人 s.update(c1); tx.commit(); }
3.3 Hibernate 的級聯刪除
@Test public void test6(){ Session s=HibernateUtil.getCurrSession(); Transaction tx =s.beginTransaction(); Customer c=s.get(Customer.class, 71); //刪除客戶 s.delete(c); tx.commit(); }
確定刪除的主控方式客戶,所以需要在客戶端配置:
<set name="linkmans" table="cust_linkman" cascade="save-update,delete">
<key column="lkm_cust_id"></key>
<one-to-many class="Linkman"/>
</set>
編寫如下測試代碼:
@Test public void test6(){ Session s=HibernateUtil.getCurrSession(); Transaction tx =s.beginTransaction(); Customer c=s.get(Customer.class, 7l); //刪除客戶 s.delete(c); tx.commit(); }

inverse的默認值是false ,代表不放棄外鍵維護權,配置值為true,代表放棄了外鍵的維護權。此時就不會再產生之前的問題。
四、Hibernate的多對多關聯關系映射
4.1 創建表
數據模型如下:
4.2 創建實體
用戶實體:
package com.Kevin.domain; /** * 用戶實體類 */ import java.io.Serializable; import java.util.HashSet; import java.util.Set; public class SysUser implements Serializable { private Long userId; private String userName; private String userPassword; private Integer userState; //多對多關系映射: private Set<SysRole> roles=new HashSet<SysRole>(0); public Set<SysRole> getRoles() { return roles; } public void setRoles(Set<SysRole> roles) { this.roles = roles; } public Long getUserId() { return userId; } public void setUserId(Long userId) { this.userId = userId; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getUserPassword() { return userPassword; } public void setUserPassword(String userPassword) { this.userPassword = userPassword; } public Integer getUserState() { return userState; } public void setUserState(Integer userState) { this.userState = userState; } @Override public String toString() { return "SysUser [userId=" + userId + ", userName=" + userName + ", userPassword=" + userPassword + ", userState=" + userState + "]"; } }
角色實體:
package com.Kevin.domain; /** * 角色的實體類 */ import java.io.Serializable; import java.util.HashSet; import java.util.Set; public class SysRole implements Serializable { private Long roleId; private String roleName; private String roleMemo; //多對多關系映射:一個角色可以賦予多個用戶 private Set<SysUser> users=new HashSet<SysUser>(0); public Set<SysUser> getUsers() { return users; } public void setUsers(Set<SysUser> users) { this.users = users; } public Long getRoleId() { return roleId; } public void setRoleId(Long roleId) { this.roleId = roleId; } public String getRoleName() { return roleName; } public void setRoleName(String roleName) { this.roleName = roleName; } public String getRoleMemo() { return roleMemo; } public void setRoleMemo(String roleMemo) { this.roleMemo = roleMemo; } @Override public String toString() { return "SysRole [roleId=" + roleId + ", roleName=" + roleName + ", roleMemo=" + roleMemo + "]"; } }
4.3 創建映射
用戶映射:
<?xml version="1.0" encoding="UTF-8"?> <!-- 創建用戶類關系映射 導入dtd約束 --> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <!-- private Long userId; private String userName; private String userPassword; private Integer userState; --> <hibernate-mapping package="com.Kevin.domain"> <class name="SysUser" table="sys_user" > <id name="userId" column="user_id"> <generator class="native"></generator> </id> <property name="userName" column="user_name"></property> <property name="userPassword" column="user_password"></property> <property name="userState" column="user_state"></property> <!-- 多對多關系映射涉及的標簽: set:用於映射set集合屬性 name:指定集合名稱 table:指定中間表的名稱 key:用於映射外鍵字段 column:指定當前實體在中間表的外鍵字段名稱 many-to-many:用於映射多對多的關系 class:對方的實體類 column: 對方在中間類的外鍵字段名稱 --> <set name="roles" table="user_role_ref" inverse="true" cascade="delete"> <key column="user_id"></key> <many-to-many class="SysRole" column="role_id" /> </set> </class> </hibernate-mapping>
角色映射:
<?xml version="1.0" encoding="UTF-8"?> <!-- 創建用戶類關系映射 導入dtd約束 --> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <!-- private Long roleId; private String roleName; private String roleMemo; --> <hibernate-mapping package="com.Kevin.domain"> <class name="SysRole" table="sys_role" > <id name="roleId" column="role_id"> <generator class="native"></generator> </id> <property name="roleName" column="role_name"></property> <property name="roleMemo" column="role_memo"></property> <!-- 多對多關系映射涉及的標簽: set:用於映射set集合屬性 name:指定集合名稱 table:指定中間表的名稱 key:用於映射外鍵字段 column:指定當前實體在中間表的外鍵字段名稱 many-to-many:用於映射多對多的關系 class:對方的實體類 column: 對方在中間類的外鍵字段名稱 --> <set name="users" table="user_role_ref" cascade="delete"> <key column="role_id"></key> <many-to-many class="SysUser" column="user_id"/> </set> </class> </hibernate-mapping>
4.4 在核心配置中加入映射文件
<mapping resource="com/Kevin/domain/SysUser.hbm.xml"/> <mapping resource="com/Kevin/domain/SysRole.hbm.xml"/>
4.5 編寫測試類
/** * 保存操作:需求:創建2個用戶和3個角色 * 讓1號用戶具備1號和2號角色 * 讓2號用戶具備2號和3號角色 * 保存用戶和角色 */ @Test public void test(){ SysUser u1=new SysUser(); u1.setUserName("User1"); SysUser u2=new SysUser(); u2.setUserName("User2"); SysRole r1=new SysRole(); r1.setRoleName("Role1"); SysRole r2=new SysRole(); r2.setRoleName("Role2"); SysRole r3=new SysRole(); r3.setRoleName("Role3"); //建立雙相關聯關系 //先建立用戶 u1.getRoles().add(r1); u1.getRoles().add(r2); u2.getRoles().add(r2); u2.getRoles().add(r3); //建立角色 r1.getUsers().add(u1); r2.getUsers().add(u1); r2.getUsers().add(u2); r3.getUsers().add(u2); Session s=HibernateUtil.getCurrSession(); Transaction tx=s.beginTransaction(); s.save(u1); s.save(u2); s.save(r1); s.save(r2); s.save(r3); tx.commit(); }
五、多對多的相關操作
5.1 級聯保存或更新
之前已經學習過一對多的級聯保存了,那么多對多也是一樣的。如果只保存單獨的一方是不可以的,還是需要保存雙方的。如果就想保存一方就需要設置級聯操作。同樣要看保存的主控方是哪一端,就需要在那一端進行配置。
保存用戶級聯角色
編寫測試代碼:
/** * 級聯保存操作: * 需求:創建2個用戶和3個角色 * 讓1號用戶具備1號和2號角色 * 讓2號用戶具備2號和3號角色 * 保存用戶和角色 */ @Test public void test1(){ SysUser u1=new SysUser(); u1.setUserName("User1"); SysUser u2=new SysUser(); u2.setUserName("User2"); SysRole r1=new SysRole(); r1.setRoleName("Role1"); SysRole r2=new SysRole(); r2.setRoleName("Role2"); SysRole r3=new SysRole(); r3.setRoleName("Role3"); //建立雙相關聯關系 //先建立用戶 u1.getRoles().add(r1); u1.getRoles().add(r2); u2.getRoles().add(r2); u2.getRoles().add(r3); //建立角色 r1.getUsers().add(u1); r2.getUsers().add(u1); r2.getUsers().add(u2); r3.getUsers().add(u2); Session s=HibernateUtil.getCurrSession(); Transaction tx=s.beginTransaction(); s.save(u1); s.save(u2); tx.commit(); }
保存角色級聯用戶
編寫測試代碼:
/** * 級聯保存操作:保存角色級聯用戶 * 需求:創建2個用戶和3個角色 * 讓1號用戶具備1號和2號角色 * 讓2號用戶具備2號和3號角色 * 保存用戶和角色 */ @Test public void test3(){ SysUser u1=new SysUser(); u1.setUserName("User1"); SysUser u2=new SysUser(); u2.setUserName("User2"); SysRole r1=new SysRole(); r1.setRoleName("Role1"); SysRole r2=new SysRole(); r2.setRoleName("Role2"); SysRole r3=new SysRole(); r3.setRoleName("Role3"); //建立雙相關聯關系 //先建立用戶 u1.getRoles().add(r1); u1.getRoles().add(r2); u2.getRoles().add(r2); u2.getRoles().add(r3); //建立角色 r1.getUsers().add(u1); r2.getUsers().add(u1); r2.getUsers().add(u2); r3.getUsers().add(u2); Session s=HibernateUtil.getCurrSession(); Transaction tx=s.beginTransaction(); s.save(r1); s.save(r2); s.save(r3); tx.commit(); }
5.2 級聯刪除(了解)
級聯刪除僅作了解,因為在實際開發中是禁止用的。由於在多對多關聯關系下,往往有多個對象是關聯的,因此只要刪除一個,使用級聯操作,就會刪除多個對象和數據。
測試代碼:
/** * 刪除操作 * 在實際開發中:多對多的雙向級聯刪除是禁止使用的 */ @Test public void test2(){ Session s=HibernateUtil.getCurrSession(); Transaction tx=s.beginTransaction(); SysUser u1=s.get(SysUser.class, 1l); s.delete(u1); tx.commit(); }
我的博客即將搬運同步至騰訊雲+社區,邀請大家一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=3gy4lgwd4jqc4