表与表之间关系回顾(重点)
1 一对多
(1)分类和商品关系,一个分类里面有多个商品,一个商品只能属于一个分类
(2)客户和联系人是一对多关系
- 客户:与公司有业务往来,百度、新浪、360
- 联系人:公司里面的员工,百度里面有很多员工,联系员工
** 公司和公司员工的关系
- 客户是一,联系人是多
- 一个客户里面有多个联系人,一个联系人只能属于一个客户
(3)一对多建表:通过外键建立关系
2 多对多
(1)订单和商品关系,一个订单里面有多个商品,一个商品属于多个订单
(2)用户和角色多对多关系
- 用户: 小王、小马、小宋
- 角色:总经理、秘书、司机、保安
** 比如小王 可以 是总经理,可以是司机
** 比如小宋 可以是司机,可以是秘书,可以保安
** 比如小马 可以是 秘书,可以是总经理
- 一个用户里面可以有多个角色,一个角色里面可以有多个用户
(3)多对多建表:创建第三张表维护关系
3 一对一
(1)在中国,一个男人只能有一个妻子,一个女人只能有一个丈夫
Hibernate的一对多操作(重点)
一对多映射配置(重点)
以客户和联系人为例:客户是一,联系人是多
第一步 创建两个实体类,客户和联系人
第二步 让两个实体类之间互相表示
(1)在客户实体类里面表示多个联系人
- 一个客户里面有多个联系人
(2)在联系人实体类里面表示所属客户
- 一个联系人只能属于一个客户
第三步 配置映射关系
(1)一般一个实体类对应一个映射文件
(2)把映射最基本配置完成
(3)在映射文件中,配置一对多关系
- 在客户映射文件中,表示所有联系人
- 在联系人映射文件中,表示所属客户
第四步 创建核心配置文件,把映射文件引入到核心配置文件中
代码实践:
1 package org.model; 2 3 import java.util.HashSet; 4 import java.util.Set; 5 6 public class Customer { 7 private Integer cid; 8 private String cname; 9 private String tel; 10 //在一方创建多的集合 并且生成set get 方法 11 private Set<LinkMan> setlinkman=new HashSet<LinkMan>(); 12 13 public Set<LinkMan> getSetlinkman() { 14 return setlinkman; 15 } 16 public void setSetlinkman(Set<LinkMan> setlinkman) { 17 this.setlinkman = setlinkman; 18 } 19 public Integer getCid() { 20 return cid; 21 } 22 public void setCid(Integer cid) { 23 this.cid = cid; 24 } 25 public String getCname() { 26 return cname; 27 } 28 public void setCname(String cname) { 29 this.cname = cname; 30 } 31 public String getTel() { 32 return tel; 33 } 34 public void setTel(String tel) { 35 this.tel = tel; 36 } 37 }
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE hibernate-mapping PUBLIC 3 "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 4 "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> 5 <hibernate-mapping> 6 <class name="org.model.Customer" table="t_customer"> 7 <id name="cid" column="cid"> 8 <generator class="native"></generator> 9 </id> 10 <property name="cname" column="cname"></property> 11 <property name="tel" column="tel"></property> 12 13 <set name="setlinkman"> 14 <!-- key中column的属性值为外键的名字 可随便写 作用指定外键 在多的一方添加外键 所以 根据class中的路径 在多方 创建的表中就会有clid字段 作为外键 --> 15 <key column="clid"></key> 16 <!-- class属性中写的是关联表实体类的名称 --> 17 <one-to-many class="org.model.LinkMan"/> 18 </set> 19 </class> 20 </hibernate-mapping>
1 package org.model; 2 3 public class LinkMan { 4 private Integer lid; 5 private String lname; 6 private String tel; 7 //在多的一方 指定所属的客户 当然也可以只写一个字段(关联外键的id) 这里写的是一个对象 8 private Customer customer; 9 10 public Customer getCustomer() { 11 return customer; 12 } 13 public void setCustomer(Customer customer) { 14 this.customer = customer; 15 } 16 public Integer getLid() { 17 return lid; 18 } 19 public void setLid(Integer lid) { 20 this.lid = lid; 21 } 22 public String getLname() { 23 return lname; 24 } 25 public void setLname(String lname) { 26 this.lname = lname; 27 } 28 public String getTel() { 29 return tel; 30 } 31 public void setTel(String tel) { 32 this.tel = tel; 33 } 34 }
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE hibernate-mapping PUBLIC 3 "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 4 "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> 5 <hibernate-mapping> 6 <class name="org.model.LinkMan" table="t_linkman"> 7 <id name="lid" column="lid"> 8 <generator class="native"></generator> 9 </id> 10 <property name="lname" column="lname"></property> 11 <property name="tel" column="tel"></property> 12 <!-- name中写的是linkman表中关联对象的名称 column写的是外间的名称 class写的是关联对象的类的全路径 指定被关联的实体类--> 13 <many-to-one name="customer" column="clid" class="org.model.Customer"></many-to-one> 14 </class> 15 </hibernate-mapping>
核心配置文件
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE hibernate-configuration PUBLIC 3 "-//Hibernate/Hibernate Configuration DTD 3.0//EN" 4 "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> 5 <hibernate-configuration> 6 <session-factory> 7 <!-- 第一步:配置数据库信息 --> 8 <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> 9 <property name="hibernate.connection.username">root</property> 10 <property name="hibernate.connection.password">jay571018</property> 11 <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate3_demo1</property> 12 <!-- 第二步:配置Hibernate信息 --> 13 <property name="hibernate.show_sql">true</property> 14 <property name="hibernate.format_sql">true</property> 15 <!-- 自动建表 --> 16 <property name="hibernate.hbm2ddl.auto">update</property> 17 <!-- 设置数据库方言 --> 18 <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> 19 <!-- 设置与本地线程绑定session --> 20 <property name="hibernate.current_session_context_class">thread</property> 21 22 <!-- 第三步:引入对象关系映射文件 --> 23 <mapping resource="org/model/Customer.hbm.xml"/> 24 <mapping resource="org/model/LinkMan.hbm.xml"/> 25 </session-factory> 26 </hibernate-configuration>
sessionfactory
1 package org.util; 2 3 import org.hibernate.Session; 4 import org.hibernate.SessionFactory; 5 import org.hibernate.cfg.Configuration; 6 7 public class SessionFactoryUtils { 8 static Configuration configuration=null; 9 static SessionFactory sf=null; 10 11 static{ 12 configuration=new Configuration(); 13 configuration.configure();//加载核心配置文件 14 sf=configuration.buildSessionFactory(); 15 } 16 public static SessionFactory getsessionfactory(){ 17 return sf; 18 } 19 20 public static Session get(){ 21 return sf.getCurrentSession(); 22 } 23 24 public static void main(String[] args){ 25 26 } 27 28 }
执行之后效果:
自动完成建表 并且在联系人表(多方)中添加了外键
一对多级联操作
级联操作
1 级联保存
(1)添加一个客户,为这个客户添加多个联系人
2 级联删除
(1)删除某一个客户,这个客户里面的所有的联系人也删除
一对多级联保存
1 添加客户,为这个客户添加一个联系人
(1)复杂写法:
1 //演示一对多级联保存 2 @Test 3 public void testAddDemo1() { 4 SessionFactory sessionFactory = null; 5 Session session = null; 6 Transaction tx = null; 7 try { 8 //得到sessionFactory 9 sessionFactory = HibernateUtils.getSessionFactory(); 10 //得到session 11 session = sessionFactory.openSession(); 12 //开启事务 13 tx = session.beginTransaction(); 14 15 // 添加一个客户,为这个客户添加一个联系人 16 //1 创建客户和联系人对象 17 Customer customer = new Customer(); 18 customer.setCustName("传智播客"); 19 customer.setCustLevel("vip"); 20 customer.setCustSource("网络"); 21 customer.setCustPhone("110"); 22 customer.setCustMobile("999"); 23 24 LinkMan linkman = new LinkMan(); 25 linkman.setLkm_name("lucy"); 26 linkman.setLkm_gender("男"); 27 linkman.setLkm_phone("911"); 28 29 //2 在客户表示所有联系人,在联系人表示客户 30 // 建立客户对象和联系人对象关系 31 //2.1 把联系人对象 放到客户对象的set集合里面 32 customer.getSetLinkMan().add(linkman); 33 //2.2 把客户对象放到联系人里面 34 linkman.setCustomer(customer); 35 36 //3 保存到数据库 37 session.save(customer); 38 session.save(linkman); 39 40 //提交事务 41 tx.commit(); 42 43 }catch(Exception e) { 44 tx.rollback(); 45 }finally { 46 session.close(); 47 //sessionFactory不需要关闭 48 sessionFactory.close(); 49 } 50 }
执行效果:
(2)简化写法
- 一般根据客户添加联系人
第一步 在客户映射文件中进行配置
- 在客户映射文件里面set标签进行配置
第二步 创建客户和联系人对象,只需要把联系人放到客户里面就可以了,最终只需要保存客户就可以了
1 //演示一对多级联保存 2 @Test 3 public void testAddDemo2() { 4 SessionFactory sessionFactory = null; 5 Session session = null; 6 Transaction tx = null; 7 try { 8 //得到sessionFactory 9 sessionFactory = HibernateUtils.getSessionFactory(); 10 //得到session 11 session = sessionFactory.openSession(); 12 //开启事务 13 tx = session.beginTransaction(); 14 // 添加一个客户,为这个客户添加一个联系人 15 //1 创建客户和联系人对象 16 Customer customer = new Customer(); 17 customer.setCustName("百度"); 18 customer.setCustLevel("普通客户"); 19 customer.setCustSource("网络"); 20 customer.setCustPhone("110"); 21 customer.setCustMobile("999"); 22 23 LinkMan linkman = new LinkMan(); 24 linkman.setLkm_name("小宏"); 25 linkman.setLkm_gender("男"); 26 linkman.setLkm_phone("911"); 27 //2 把联系人放到客户里面 28 customer.getSetLinkMan().add(linkman); 29 //3 保存客户 30 session.save(customer); 31 32 //提交事务 33 tx.commit(); 34 }catch(Exception e) { 35 tx.rollback(); 36 }finally { 37 session.close(); 38 //sessionFactory不需要关闭 39 sessionFactory.close(); 40 } 41 }
代码实践:
修改customer配置文件
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE hibernate-mapping PUBLIC 3 "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 4 "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> 5 <hibernate-mapping> 6 <class name="org.model.Customer" table="t_customer"> 7 <id name="cid" column="cid"> 8 <generator class="native"></generator> 9 </id> 10 <property name="cname" column="cname"></property> 11 <property name="tel" column="tel"></property> 12 13 <set name="setlinkman" cascade="save-update"> 14 <!-- key中column的属性值为外键的名字 可随便写 作用指定外键 在多的一方添加外键 所以 根据class中的路径 在多方 创建的表中就会有clid字段 作为外键 --> 15 <key column="clid"></key> 16 <!-- class属性中写的是关联表实体类的名称 --> 17 <one-to-many class="org.model.LinkMan"/> 18 </set> 19 </class> 20 </hibernate-mapping>
1 package org.testdemo; 2 3 import org.hibernate.Session; 4 import org.hibernate.SessionFactory; 5 import org.hibernate.Transaction; 6 import org.junit.Test; 7 import org.model.Customer; 8 import org.model.LinkMan; 9 import org.util.SessionFactoryUtils; 10 11 public class OnetoMany { 12 @Test 13 public void test1(){ 14 //复杂写法 没有配置cascade属性 15 Session session=null; 16 Transaction tran=null; 17 try{ 18 session=SessionFactoryUtils.get(); 19 //开启事务 20 tran=session.beginTransaction(); 21 22 Customer c=new Customer(); 23 c.setCname("阿里"); 24 c.setTel("0379-1534350"); 25 26 LinkMan l=new LinkMan(); 27 l.setLname("马云"); 28 l.setTel("15035286558"); 29 30 //分别建立联系 31 c.getSetlinkman().add(l); 32 l.setCustomer(c); 33 //然后分别保存 34 session.save(c); 35 session.save(l); 36 37 //提交事务 38 tran.commit(); 39 40 }catch(Exception e){ 41 e.printStackTrace(); 42 }finally{ 43 } 44 45 } 46 @Test 47 public void test2(){ 48 //简单写法 在客户映射文件中配置cascade属性 因为联系人表的外键 约束与客户表的主键 所以一般都是先保存客户表 所以在客户表中进行配置 49 Session session=null; 50 Transaction tran=null; 51 try{ 52 session=SessionFactoryUtils.get(); 53 //开启事务 54 tran=session.beginTransaction(); 55 56 Customer c=new Customer(); 57 c.setCname("百度"); 58 c.setTel("0379-1534350"); 59 60 LinkMan l=new LinkMan(); 61 l.setLname("李彦宏"); 62 l.setTel("15035286558"); 63 64 //不需要分别建立联系 65 // c.getSetlinkman().add(l); 66 // l.setCustomer(c); 67 //因为客户表是主控方 只需要在客户对象中关联 联系人表的对象即可 68 //然后分别保存 69 // session.save(c); 70 // session.save(l); 71 c.getSetlinkman().add(l); 72 session.save(c); 73 74 //提交事务 75 tran.commit(); 76 77 }catch(Exception e){ 78 e.printStackTrace(); 79 }finally{ 80 } 81 82 } 83 }
执行效果:
一对多级联删除
1 删除某个客户,把客户里面所有的联系人删除
2 具体实现
第一步 在客户(主控方)映射文件set标签,进行配置
(1)使用属性cascade属性值 delete
第二步 在代码中直接删除客户
(1)根据id查询对象,调用session里面delete方法删除
3 执行过程:
(1)根据id查询客户
(2)根据外键id值查询联系人
(3)把联系人外键设置为null
(4)删除联系人和客户
代码实践:
1 @Test 2 public void test3(){ 3 //进行级联删除操作 需要在主控方set标签中增加属性配置 4 Session session=null; 5 Transaction tran=null; 6 try{ 7 session=SessionFactoryUtils.get(); 8 //开启事务 9 tran=session.beginTransaction(); 10 //A级联删除删除的是主控方的对象 从而把依赖的对象也删除 联系人表的外键依赖于客户表中的主键 11 Customer c=session.get(Customer.class,4); 12 session.delete(c); 13 //B下面这种方式不会级联删除 执行之后只会删掉联系人表中对应的记录 而客户表不会删 14 // LinkMan l=session.get(LinkMan.class,5);// 15 // session.delete(l); 16 tran.commit(); 17 18 }catch(Exception e){ 19 e.printStackTrace(); 20 }finally{ 21 } 22 }
删除之前:
A代码 级联删除之后:
执行控制台打印 删除过程
Hibernate: select customer0_.cid as cid1_0_0_, customer0_.cname as cname2_0_0_, customer0_.tel as tel3_0_0_ from t_customer customer0_ where customer0_.cid=? Hibernate: select setlinkman0_.clid as clid4_1_0_, setlinkman0_.lid as lid1_1_0_, setlinkman0_.lid as lid1_1_1_, setlinkman0_.lname as lname2_1_1_, setlinkman0_.tel as tel3_1_1_, setlinkman0_.clid as clid4_1_1_ from t_linkman setlinkman0_ where setlinkman0_.clid=? Hibernate: update t_linkman set clid=null where clid=? Hibernate: delete from t_linkman where lid=? Hibernate: delete from t_customer where cid=?
B代码 删除之后:
删除过程:
Hibernate: select linkman0_.lid as lid1_1_0_, linkman0_.lname as lname2_1_0_, linkman0_.tel as tel3_1_0_, linkman0_.clid as clid4_1_0_ from t_linkman linkman0_ where linkman0_.lid=? Hibernate: delete from t_linkman where lid=?
一对多修改操作(inverse属性)
1 让lucy联系人所属客户不是传智播客,而是百度
2 inverse属性
(1)因为hibernate双向维护外键,在客户和联系人里面都需要维护外键,修改客户时候修改一次外键,修改联系人时候也修改一次外键,造成效率问题
(2)解决方式:让其中的一方不维护外键
- 一对多里面,让其中一方放弃外键维护
- 一个国家有总统,国家有很多人,总统不能认识国家所有人,国家所有人可以认识总统
(3)具体实现:
在放弃关系维护映射文件中,进行配置,在set标签上使用inverse属性
代码实现:
操作之前:
操作之后:
测试代码:
@Test public void test4(){ Session session=null; Transaction tran=null; try{ session=SessionFactoryUtils.get(); //开启事务 tran=session.beginTransaction(); Customer al=session.get(Customer.class,2); LinkMan linkman=session.get(LinkMan.class,111); al.getSetlinkman().add(linkman); linkman.setCustomer(al);//因为是持久态对象 所以不需要进行save保存 当事务提交的时候 会自动进行保存 tran.commit(); }catch(Exception e){ e.printStackTrace(); }finally{ } }
没有添加inverse属性执行过程:
Hibernate:
select
customer0_.cid as cid1_0_0_,
customer0_.cname as cname2_0_0_,
customer0_.tel as tel3_0_0_
from
t_customer customer0_
where
customer0_.cid=?
Hibernate:
select
linkman0_.lid as lid1_1_0_,
linkman0_.lname as lname2_1_0_,
linkman0_.tel as tel3_1_0_,
linkman0_.clid as clid4_1_0_
from
t_linkman linkman0_
where
linkman0_.lid=?
Hibernate:
select
setlinkman0_.clid as clid4_1_0_,
setlinkman0_.lid as lid1_1_0_,
setlinkman0_.lid as lid1_1_1_,
setlinkman0_.lname as lname2_1_1_,
setlinkman0_.tel as tel3_1_1_,
setlinkman0_.clid as clid4_1_1_
from
t_linkman setlinkman0_
where
setlinkman0_.clid=?
Hibernate:
update
t_linkman
set
lname=?,
tel=?,
clid=?
where
lid=?
Hibernate:
update
t_linkman
set
clid=?
where
lid=?
多了一次更新
现在在一方set表签中添加inverse属性 让它放弃表的维护工作
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE hibernate-mapping PUBLIC 3 "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 4 "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> 5 <hibernate-mapping> 6 <class name="org.model.Customer" table="t_customer"> 7 <id name="cid" column="cid"> 8 <generator class="native"></generator> 9 </id> 10 <property name="cname" column="cname"></property> 11 <property name="tel" column="tel"></property> 12 <!-- 默认值是false 不放弃维护 这里设置为false 表示放弃维护 --> 13 <set name="setlinkman" cascade="save-update,delete" inverse="true"> 14 <!-- key中column的属性值为外键的名字 可随便写 作用指定外键 在多的一方添加外键 所以 根据class中的路径 在多方 创建的表中就会有clid字段 作为外键 --> 15 <key column="clid"></key> 16 <!-- class属性中写的是关联表实体类的名称 --> 17 <one-to-many class="org.model.LinkMan"/> 18 </set> 19 </class> 20 </hibernate-mapping>
执行代码观察控制台:
Hibernate:
select
customer0_.cid as cid1_0_0_,
customer0_.cname as cname2_0_0_,
customer0_.tel as tel3_0_0_
from
t_customer customer0_
where
customer0_.cid=?
Hibernate:
select
linkman0_.lid as lid1_1_0_,
linkman0_.lname as lname2_1_0_,
linkman0_.tel as tel3_1_0_,
linkman0_.clid as clid4_1_0_
from
t_linkman linkman0_
where
linkman0_.lid=?
Hibernate:
select
setlinkman0_.clid as clid4_1_0_,
setlinkman0_.lid as lid1_1_0_,
setlinkman0_.lid as lid1_1_1_,
setlinkman0_.lname as lname2_1_1_,
setlinkman0_.tel as tel3_1_1_,
setlinkman0_.clid as clid4_1_1_
from
t_linkman setlinkman0_
where
setlinkman0_.clid=?
Hibernate:
update
t_linkman
set
lname=?,
tel=?,
clid=?
where
lid=?
少了一次更新的语句,性能提高