一、JavaWeb中一对多的设计及其建表原则
![关联关系图解](https://i-blog.csdnimg.cn/blog_migrate/7a70095c7a0a28e1522ae630e9819e64.jpeg)
关联关系图解
二、SQL的建表
![新建数据库](https://i-blog.csdnimg.cn/blog_migrate/ac83e7ad3361c8365ede77568faca314.jpeg)
CREATE TABLE `cst_customer` (
`cust_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)',
`cust_name` varchar(32) NOT NULL COMMENT '客户名称(公司名称)',
`cust_user_id` bigint(32) DEFAULT NULL COMMENT '负责人id',
`cust_create_id` bigint(32) DEFAULT NULL COMMENT '创建人id',
`cust_source` varchar(32) DEFAULT NULL COMMENT '客户信息来源',
`cust_industry` varchar(32) DEFAULT NULL COMMENT '客户所属行业',
`cust_level` varchar(32) DEFAULT NULL COMMENT '客户级别',
`cust_linkman` varchar(64) DEFAULT NULL COMMENT '联系人',
`cust_phone` varchar(64) DEFAULT NULL COMMENT '固定电话',
`cust_mobile` varchar(16) DEFAULT NULL COMMENT '移动电话',
PRIMARY KEY (`cust_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
CREATE TABLE `cst_linkman` (
`lkm_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '联系人编号(主键)',
`lkm_name` varchar(16) DEFAULT NULL COMMENT '联系人姓名',
`lkm_cust_id` bigint(32) DEFAULT NULL COMMENT '客户id',
`lkm_gender` char(1) DEFAULT NULL COMMENT '联系人性别',
`lkm_phone` varchar(16) DEFAULT NULL COMMENT '联系人办公电话',
`lkm_mobile` varchar(16) DEFAULT NULL COMMENT '联系人手机',
`lkm_email` varchar(64) DEFAULT NULL COMMENT '联系人邮箱',
`lkm_qq` varchar(16) DEFAULT NULL COMMENT '联系人qq',
`lkm_position` varchar(16) DEFAULT NULL COMMENT '联系人职位',
`lkm_memo` varchar(512) DEFAULT NULL COMMENT '联系人备注',
PRIMARY KEY (`lkm_id`),
KEY `FK_cst_linkman_lkm_cust_id` (`lkm_cust_id`),
CONSTRAINT `FK_cst_linkman_lkm_cust_id` FOREIGN KEY (`lkm_cust_id`) REFERENCES `cst_customer` (`cust_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
四、编写常规的客户和联系人的JavaBean程序与映射配置文件
JavaBean
/Hibernate5_d03_c01/src/hibernate/domain/Customer.java
程序代码如下:
package hibernate.domain;
public class Customer {
private Long cust_id;
private String cust_name;
private Long cust_user_id;
private Long cust_create_id;
private String cust_source;
private String cust_industry;
private String cust_level;
private String cust_linkman;
private String cust_phone;
private String cust_mobile;
//省略 getter 和 setter 方法
@Override
public String toString() {
return "Custom [cust_id=" + cust_id + ", cust_name=" + cust_name + ", cust_user_id=" + cust_user_id
+ ", cust_create_id=" + cust_create_id + ", cust_source=" + cust_source + ", cust_industry="
+ cust_industry + ", cust_level=" + cust_level + ", cust_linkman=" + cust_linkman + ", cust_phone="
+ cust_phone + ", cust_mobile=" + cust_mobile + "]";
}
}
/Hibernate5_d03_c01/src/hibernate/domain/LinkMan.java
程序代码如下:
package hibernate.domain;
public class LinkMan {
private Integer lkm_id;
private String lkm_name;
// lkm_cust_id 外键,不用写
private String lkm_gender;
private String lkm_phone;
private String lkm_mobile;
private String lkm_email;
private String lkm_qq;
private String lkm_position;
private String lkm_memo;
//省略 getter 和 setter 方法
}
映射配置文件
/Hibernate5_d03_c01/src/hibernate/domain/Customer.hbm.xml
程序代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- javabean与表之间的对应关系 -->
<class name="hibernate.domain.Customer" table="cst_customer">
<!-- 主键对应 -->
<id name="cust_id" column="cust_id">
<!-- 主键策略(与自增长相关) -->
<generator class="native"></generator>
</id>
<!-- 其他字段 -->
<property name="cust_name" column="cust_name"></property>
<property name="cust_user_id" column="cust_user_id"></property>
<property name="cust_create_id" column="cust_create_id"></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_linkman" column="cust_linkman"></property>
<property name="cust_phone" column="cust_phone"></property>
<property name="cust_mobile" column="cust_mobile"></property>
</class>
</hibernate-mapping>
/Hibernate5_d03_c01/src/hibernate/domain/LinkMan.hbm.xml
程序代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- javabean与表之间的对应关系 -->
<class name="hibernate.domain.LinkMan" table="cst_linkman">
<!-- 主键对应 -->
<id name="lkm_id" column="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_phone" column="lkm_phone"></property>
<property name="lkm_mobile" column="lkm_mobile"></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>
</class>
</hibernate-mapping>
/Hibernate5_d03_c01/src/hibernate.cfg.xml
程序代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- 必选配置 -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- jdbc:mysql:///hibernate_day01 -->
<property name="hibernate.connection.url">jdbc:mysql://127.0.0.1:3306/hibernate_day03</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">root</property>
<!--数据库方言 使用的数据库类型 -->
<property name="hibernate.dialect org.hibernate.dialect.MySQLDialect"></property>
<!-- 可选配置 -->
<!-- 在控制台输出sql语句 -->
<property name="show_sql">true</property>
<!-- 在控制台输出的sql语句格式化 -->
<property name="hibernate.format_sql">true</property>
<!-- 配置c3p0连接池 -->
<!-- C3P0 的供应商 -->
<property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
<!-- 最小连接 -->
<property name="hibernate.c3p0.min_size">5</property>
<!-- 最大连接数 -->
<property name="hibernate.c3p0.max_size">10</property>
<!-- 每 120 秒检查空闲连接 -->
<property name="hibernate.c3p0.timeout">120</property>
<!-- 自动建表 -->
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- 把session绑定到当前线程中,使用getCurrentSession()获取当前session -->
<property name="hibernate.current_session_context_class">thread</property>
<!-- 映射文件 -->
<mapping resource="hibernate/domain/Customer.hbm.xml"/>
<mapping resource="hibernate/domain/LinkMan.hbm.xml"/>
</session-factory>
</hibernate-configuration>
五、编写Hibernate的客户和联系人的JavaBean程序与映射配置文件
JavaBean
Hibernate框架通过JavaBean类来操作数据库,所以JavaBean类需要表现出一对多的关联关系,所以需要关联两个类。
一个客户对应多个联系人,在Java中可以用集合来表示。
客户跟联系人之间没有顺序,在Java的集合类型中可以选用set集合表示。
/Hibernate5_d03_c01/src/hibernate/domain/Customer.java
程序代码如下:
package hibernate.domain;
import java.util.HashSet;
import java.util.Set;
public class Customer {
...
//set集合表示多个联系人
//设置与多方关联的属性
private Set<LinkMan> linkMans = new HashSet<>();
public Set<LinkMan> getLinkMans() {
return linkMans;
}
public void setLinkMans(Set<LinkMan> linkMans) {
this.linkMans = linkMans;
}
//省略 getter 和 setter 方法
...
}
一个联系人对应一个客户,在Java中可以直接用一个客户类表示。
/Hibernate5_d03_c01/src/hibernate/domain/LinkMan.java
程序代码如下:
package hibernate.domain;
public class LinkMan {
...
//客户类
//与一方关联的属性
private Customer customer;
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
...
}
映射配置文件
/Hibernate5_d03_c01/src/hibernate/domain/Customer.hbm.xml
程序代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
...
<!-- 配置一对多的关系 -->
<!--
name:set集合的名称
key:column:外键名称
one-to-many class:set集合中类的全路径
-->
<set name="linkMans">
<!-- 外键 -->
<key column="lkm_cust_id"></key>
<!-- 一对多关系 -->
<one-to-many class="hibernate.domain.LinkMan"/>
</set>
</class>
</hibernate-mapping>
/Hibernate5_d03_c01/src/hibernate/domain/LinkMan.hbm.xml
程序代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
...
<!-- 配置多对一的关系 -->
<!-- name:客户类属性名称 -->
<!-- class:属性的类的全路径 -->
<!-- column:外键 -->
<many-to-one name="customer" class="hibernate.domain.Customer" column="lkm_cust_id"></many-to-one>
</class>
</hibernate-mapping>
六、一对多配置文件和具体的说明
![一方与多方各自的关系](https://i-blog.csdnimg.cn/blog_migrate/44b96a8308b15e0e57f660ef5d1ffdcd.jpeg)
一方与多方各自的关系
七、一对多的级联保存——双向关联
/Hibernate5_d03_c01/src/hibernate/test/TestOne2Many.java
程序代码如下:
package hibernate.test;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;
import hibernate.domain.Customer;
import hibernate.domain.LinkMan;
import hibernate.util.HibernateUtils;
public class TestOne2Many {
//双向关联
@Test
public void testSave(){
Session session = HibernateUtils.getCurrentSession();
Transaction tr = session.beginTransaction();
//新建1个客户和2个联系人
Customer c = new Customer();
c.setCust_name("马蓉");
LinkMan m1 = new LinkMan();
m1.setLkm_name("强哥");
LinkMan m2 = new LinkMan();
m2.setLkm_name("小宋");
//双向关联
//客户关联联系人
c.getLinkMans().add(m1);
c.getLinkMans().add(m2);
//联系人关联客户
m1.setCustomer(c);
m2.setCustomer(c);
//开始保存
session.save(c);
session.save(m1);
session.save(m2);
tr.commit();
//不需要手动关闭session,当提交操作完成后,hibernate框架会自动关闭session
//session.close();
}
}
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/f20f702cb44d3debe611b05c1c14abd8.jpeg)
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/61d1963198ef85698d07c78417b09e73.jpeg)
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/cdf6c966bb28023786f9c9d939079b54.jpeg)
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/70c3c4338cba3c0daa48929079b9f7bb.jpeg)
八、一对多的级联保存——单向关联
保存客户及其关联的所有联系人
1、测试:如果现在代码只插入其中的一方的数据。
1)如果只保存其中的一方的数据,那么程序会抛出异常。
2)如果想完成只保存一方的数据,并且把相关联的数据都保存到数据库中,那么需要配置级联!
3)级联保存是方向性。
/Hibernate5_d03_c01/src/hibernate/test/TestOne2Many.java
程序代码如下:
...
public class TestOne2Many {
...
//保存客户及其关联的所有联系人
@Test
public void testSave1(){
Session session = HibernateUtils.getCurrentSession();
Transaction tr = session.beginTransaction();
//新建1个客户和2个联系人
Customer c = new Customer();
c.setCust_name("马蓉1");
LinkMan m1 = new LinkMan();
m1.setLkm_name("强哥1");
LinkMan m2 = new LinkMan();
m2.setLkm_name("小宋1");
//单向关联
//客户关联联系人
c.getLinkMans().add(m1);
c.getLinkMans().add(m2);
//单向关联
session.save(c);
tr.commit();
//不需要手动关闭session,当提交操作完成后,hibernate框架会自动关闭session
//session.close();
}
}
![运行报错](https://i-blog.csdnimg.cn/blog_migrate/77ac4cf93baeee8cde0cc6442a393e3a.jpeg)
运行报错
![运行报错](https://i-blog.csdnimg.cn/blog_migrate/2db2bf48c2c50af80e7f36b88fbf1cc9.jpeg)
运行报错
原因结论: hibernate中,在session.flush前,不允许持久态对象关联瞬时态对象
。持久态对象只能关联持久态对象
!
2、级联保存效果
1)级联保存:保存一方同时可以把关联的对象也保存到数据库中!
2)使用 cascade=”save-update”
/Hibernate5_d03_c01/src/hibernate/domain/Customer.hbm.xml
程序代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- javabean与表之间的对应关系 -->
<class name="hibernate.domain.Customer" table="cst_customer">
...
<!-- 配置级联保存:让瞬时态对象变成持久态。cascade="save-update" -->
<set name="linkMans" cascade="save-update">
<!-- 外键 -->
<key column="lkm_cust_id"></key>
<!-- 一对多关系 -->
<one-to-many class="hibernate.domain.LinkMan"/>
</set>
</class>
</hibernate-mapping>
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/cd5f48bc4f26a055e34330ba7a3e3139.jpeg)
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/46306a09bbbedeb5801a1b7a805f26b3.jpeg)
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/3e1a39e34ecc03f269f79b739cb05c96.jpeg)
保存联系人及其对应的客户
/Hibernate5_d03_c01/src/hibernate/test/TestOne2Many.java
程序代码如下:
...
public class TestOne2Many {
...
//保存联系人及其对应的客户
@Test
public void testSave2(){
Session session = HibernateUtils.getCurrentSession();
Transaction tr = session.beginTransaction();
//新建1个客户和2个联系人
Customer c = new Customer();
c.setCust_name("马蓉1");
LinkMan m1 = new LinkMan();
m1.setLkm_name("强哥1");
LinkMan m2 = new LinkMan();
m2.setLkm_name("小宋1");
//单向关联
//联系人关联客户
m1.setCustomer(c);
m2.setCustomer(c);
//单向关联
session.save(m1);
session.save(m2);
tr.commit();
//不需要手动关闭session,当提交操作完成后,hibernate框架会自动关闭session
//session.close();
}
}
/Hibernate5_d03_c01/src/hibernate/domain/LinkMan.hbm.xml
程序代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- javabean与表之间的对应关系 -->
<class name="hibernate.domain.LinkMan" table="cst_linkman">
...
<!-- 配置级联保存:把瞬时态的客户变成持久态。cascade="save-update" -->
<many-to-one name="customer"
class="hibernate.domain.Customer"
column="lkm_cust_id"
cascade="save-update">
</many-to-one>
</class>
</hibernate-mapping>
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/a6eaa979b6aa1e6f59a6609dfed13e7c.jpeg)
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/8c5c72ceba80921866844ec96f61182d.jpeg)
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/50d1279b49e171602be1b14f0b35f1ef.jpeg)
由此可见,在联系人关联客户中,只执行了3条SQL语句,故较客户关联联系人,此配置性能较好。
九、一对多的级联删除
1、 先来给大家在数据库中演示含有外键的删除客户功能,那么 SQL 语句是会报出错误的。delete from cst_customer where cust_id = 1;
2、如果使用 Hibernate 框架直接删除客户的时候,测试发现是可以删除的
3、上述的删除是普通的删除,那么也可以使用级联删除,
4、级联删除也是有方向性的。<many-to-one cascade="delete" />
删除客户及其关联的所有联系人
/Hibernate5_d03_c01/src/hibernate/test/TestOne2Many.java
程序代码如下:
...
public class TestOne2Many {
...
//删除客户及其关联的所有联系人
@Test
public void testDelete(){
Session session = HibernateUtils.getCurrentSession();
Transaction tr = session.beginTransaction();
//获取一个客户
//cust_id是Long类型,所以要写4L
Customer c = session.get(Customer.class, 4L);
//删除客户
session.delete(c);
tr.commit();
}
}
/Hibernate5_d03_c01/src/hibernate/domain/Customer.hbm.xml
程序代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- javabean与表之间的对应关系 -->
<class name="hibernate.domain.Customer" table="cst_customer">
...
<!-- 配置级联删除。 cascade="delete" -->
<set name="linkMans" cascade="delete">
<!-- 外键 -->
<key column="lkm_cust_id"></key>
<!-- 一对多关系 -->
<one-to-many class="hibernate.domain.LinkMan"/>
</set>
</class>
</hibernate-mapping>
![找出 id 对应的客户](https://i-blog.csdnimg.cn/blog_migrate/00b42f4f875d32c3332e46c071efa402.jpeg)
找出 id 对应的客户
![找出 id 对应的联系人](https://i-blog.csdnimg.cn/blog_migrate/252d2097be2e8ea586bf8949242e04c9.jpeg)
找出 id 对应的联系人
![把找出的联系人对应的客户 id 设置为 null](https://i-blog.csdnimg.cn/blog_migrate/a3c57b94e29b5400efe99c55aafe8cbb.jpeg)
把找出的联系人对应的客户 id 设置为 null
![删除联系人](https://i-blog.csdnimg.cn/blog_migrate/9235a1d5afe36e262aa9a64aab50e598.jpeg)
删除联系人
![删除客户](https://i-blog.csdnimg.cn/blog_migrate/5f570a1b7a440382bfc877d4aed1d1ed.jpeg)
删除客户
删除联系人及其对应的客户
删掉客户配置的级联删除信息
/Hibernate5_d03_c01/src/hibernate/domain/Customer.hbm.xml
程序代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- javabean与表之间的对应关系 -->
<class name="hibernate.domain.Customer" table="cst_customer">
...
<set name="linkMans">
<!-- 外键 -->
<key column="lkm_cust_id"></key>
<!-- 一对多关系 -->
<one-to-many class="hibernate.domain.LinkMan"/>
</set>
</class>
</hibernate-mapping>
/Hibernate5_d03_c01/src/hibernate/domain/LinkMan.hbm.xml
程序代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- javabean与表之间的对应关系 -->
<class name="hibernate.domain.LinkMan" table="cst_linkman">
...
<!-- 配置级联保存:把瞬时态的客户变成持久态。cascade="save-update" -->
<!-- 配置级联删除:会产生无用的记录。cascade="delete" -->
<many-to-one name="customer"
class="hibernate.domain.Customer"
column="lkm_cust_id"
cascade="delete">
</many-to-one>
</class>
</hibernate-mapping>
/Hibernate5_d03_c01/src/hibernate/test/TestOne2Many.java
程序代码如下:
...
public class TestOne2Many {
...
//删除联系人及其对应的客户
@Test
public void testDelete1(){
Session session = HibernateUtils.getCurrentSession();
Transaction tr = session.beginTransaction();
//获取一个联系人
LinkMan linkMan = session.get(LinkMan.class, 1);
//删除联系人
session.delete(linkMan);
tr.commit();
}
}
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/4c4ba14b002afe06b611c4e5dc1f2fa9.jpeg)
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/0aa33dffb5b285c6304a575a7dad11e7.jpeg)
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/49be1ae71d11b49e1a778e6e11fd0d46.jpeg)
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/ce71a8db207cb9737e722d6928460fec.jpeg)
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/b915bf935fde881c067c4a24709287f2.jpeg)
由此可见,剩下一条联系人数据没有关联到客户,这条数据已无用,说明这种方式不合理。所以不要把级联删除配置到多方,这样会产生无用的记录。
十、”孤儿删除”和”cascade取值”
孤儿删除(孤子删除)
在一对多的关系中,可以将一的一方认为是父方.将多的一方认为是子方.孤儿删除:在解除了父子关系的时候.将子方记录就直接删除。
<set name="linkmans" cascade="delete-orphan">
![孤儿删除](https://i-blog.csdnimg.cn/blog_migrate/b3723c2b3bb60b7ca0532920c838d5e4.jpeg)
删掉联系人配置的级联删除信息
/Hibernate5_d03_c01/src/hibernate/domain/LinkMan.hbm.xml
程序代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- javabean与表之间的对应关系 -->
<class name="hibernate.domain.LinkMan" table="cst_linkman">
...
<many-to-one name="customer"
class="hibernate.domain.Customer"
column="lkm_cust_id">
</many-to-one>
</class>
</hibernate-mapping>
/Hibernate5_d03_c01/src/hibernate/domain/Customer.hbm.xml
程序代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- javabean与表之间的对应关系 -->
<class name="hibernate.domain.Customer" table="cst_customer">
...
<!-- 配置孤儿删除。cascade="delete-orphan" -->
<set name="linkMans" cascade="delete-orphan">
<!-- 外键 -->
<key column="lkm_cust_id"></key>
<!-- 一对多关系 -->
<one-to-many class="hibernate.domain.LinkMan"/>
</set>
</class>
</hibernate-mapping>
/Hibernate5_d03_c01/src/hibernate/test/TestOne2Many.java
程序代码如下:
...
public class TestOne2Many {
...
//孤儿删除
@Test
public void testDelete2(){
Session session = HibernateUtils.getCurrentSession();
Transaction tr = session.beginTransaction();
//获取id为10的客户
//id为10客户下有2个联系人:id为9和10
Customer c = session.get(Customer.class, 10L);
//要删除id为10的联系人
LinkMan m2 = session.get(LinkMan.class, 10);
//联系人与客户解除关系
c.getLinkMans().remove(m2);
//由快照功能实现
tr.commit();
}
}
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/1c0aec3ae86ccc41239298ff93c3bd7b.jpeg)
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/7ed66a6b2d6524ef9ffc5e41e11d2f15.jpeg)
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/40de64b3802cfa04e6fc35f8463041ac.jpeg)
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/a69eb361443389dce198583a7030d3e5.jpeg)
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/806bdf3f5ad4eb24233dc7af0e6a604a.jpeg)
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/80644fa8208914cdd53e8b592b0c7bff.jpeg)
总结:一般在业务开发中,不要两端都配置级联。(多方尽量不要配置级联,尽量在一方配置级联)。
cascade的取值
Hibernate级联开发配置, cascade的部分取值 | |
none | 不使用级联 |
save-update | 级联保存或更新(对关联瞬时对象执行 save 操作,对关联托管对象执行 update) |
delete | 级联删除(对关联对象进行删除) |
delete-orphan | 孤儿删除.(注意:只能应用在一对多关系) |
all | 除了 delete-orphan 的所有情况.(包含save-update、delete) |
all-delete-orphan | 包含了 delete-orphan 的所有情况.(包含 save-update、delete、delete-orphan) |
十一、放弃外键维护
维护外键
将没有关系的一个联系人和客户建立关系。(双方)
先测试双方都维护外键的时候,会产生多余的 SQL 语句。
1、想修改客户和联系人的关系,进行双向关联,双方都会维护外键,会产生多余的 SQL 语句。
2、产生的原因:session 的一级缓存中的快照机制,会让双方都更新数据库,产生了多余的 SQL 语句。
删掉客户配置的级联删除信息
/Hibernate5_d03_c01/src/hibernate/domain/Customer.hbm.xml
程序代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- javabean与表之间的对应关系 -->
<class name="hibernate.domain.Customer" table="cst_customer">
...
<set name="linkMans">
<!-- 外键 -->
<key column="lkm_cust_id"></key>
<!-- 一对多关系 -->
<one-to-many class="hibernate.domain.LinkMan"/>
</set>
</class>
</hibernate-mapping>
手动在数据表添加信息
![添加信息](https://i-blog.csdnimg.cn/blog_migrate/747e2b267f8f99ee5d0c525a52c3487b.jpeg)
![添加信息](https://i-blog.csdnimg.cn/blog_migrate/5ccc3dfaf81688d6fce1b74aaece865c.jpeg)
/Hibernate5_d03_c01/src/hibernate/test/TestOne2Many.java
程序代码如下:
public class TestOne2Many {
...
//inverse 放弃外键维护
@Test
public void testsave3(){
Session session = HibernateUtils.getCurrentSession();
Transaction tr = session.beginTransaction();
//获取一个客户
Customer c = session.get(Customer.class, 13L);
//获取一个联系人
LinkMan m = session.get(LinkMan.class, 15);
//双向关联
//联系人与客户做双向关联
c.getLinkMans().add(m);
m.setCustomer(c);
session.save(c);
session.save(m);
tr.commit();
}
}
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/549a74e181067a00fd1817806c33ae6b.jpeg)
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/95248c7b9bd60ea0201d1da5a3e151b8.jpeg)
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/f78c848ccbf1a82a1ad488fea80df8a7.jpeg)
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/d948b2bdb4ad295a978950ee51d7ae5c.jpeg)
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/4340b1171533f8c1444ad51a1342b5ed.jpeg)
由此可知,在控制台打印了两条SQL语句,而这两条SQL语句的功能都是一样的,所以有一条是多余的,我们需要改进。
放弃外键维护
将没有关系的一个联系人和客户建立关系。(双方)
如果不想产生多余的 SQL 语句,那么需要一方来放弃外键的维护。
在标签上配置一个<inverse="true">
。
true:放弃.;alse:不放弃。默认值是 false。
手动在数据表修改信息
![修改信息](https://i-blog.csdnimg.cn/blog_migrate/e58304b9a8164082912ac8123089d83d.jpeg)
/Hibernate5_d03_c01/src/hibernate/domain/Customer.hbm.xml
程序代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- javabean与表之间的对应关系 -->
<class name="hibernate.domain.Customer" table="cst_customer">
...
<!-- 配置放弃外键维护。inverse="true" -->
<set name="linkMans" inverse="true">
<!-- 外键 -->
<key column="lkm_cust_id"></key>
<!-- 一对多关系 -->
<one-to-many class="hibernate.domain.LinkMan"/>
</set>
</class>
</hibernate-mapping>
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/e9c59652d2587ad5ddcbb3d1fc6f384b.jpeg)
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/c9248e6d12825dc1c0247fc35dd10466.jpeg)
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/78543202c8c71b30a545c7de4356bb91.jpeg)
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/2403a323fe8d14035c53a24d0a17f47e.jpeg)
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/a585c1ea919393aa5abfde54e5481766.jpeg)
由此可知,在控制台只打印了一条SQL语句
十二、cascade和inverse的区别
1、cascade用来级联操作(保存、修改和删除) 在一方设置。
2、inverse用来维护外键的,在一方设置。一般,我们都让一方放弃外键维护。