表与表之间关系回顾(重点)
1 一对多
(1)分类和商品关系,一个分类里面有多个商品,一个商品只能属于一个分类
(2)客户和联系人是一对多关系
- 客户:与公司有业务往来,百度、新浪、360
- 联系人:公司里面的员工,百度里面有很多员工,联系员工
** 公司和公司员工的关系
- 客户是一,联系人是多
- 一个客户里面有多个联系人,一个联系人只能属于一个客户
(3)一对多建表:通过外键建立关系
2 多对多
(1)订单和商品关系,一个订单里面有多个商品,一个商品属于多个订单
(2)用户和角色多对多关系
- 用户: 小王、小马、小宋
- 角色:总经理、秘书、司机、保安
** 比如小王 可以 是总经理,可以是司机
** 比如小宋 可以是司机,可以是秘书,可以保安
** 比如小马 可以是 秘书,可以是总经理
-一个用户里面可以有多个角色,一个角色里面可以有多个用户
(3)多对多建表:创建第三张表维护关系
3 一对一
(1)在中国,一个男人只能有一个妻子,一个女人只能有一个丈夫
Hibernate的一对多操作(重点)
一对多映射配置(重点)
以客户和联系人为例:客户是一,联系人是多
第一步 创建两个实体类,客户和联系人
第二步 让两个实体类之间互相表示
(1)在客户实体类里面表示多个联系人
- 一个客户里面有多个联系人
(2)在联系人实体类里面表示所属客户
- 一个联系人只能属于一个客户
第三步 配置映射关系
(1)一般一个实体类对应一个映射文件
(2)把映射最基本配置完成
(3)在映射文件中,配置一对多关系
- 在客户映射文件中,表示所有联系人
- 在联系人映射文件中,表示所属客户
第四步 创建核心配置文件,把映射文件引入到核心配置文件中
代码实践:
package org.model; import java.util.HashSet;
import java.util.Set; public class Customer {
private Integer cid;
private String cname;
private String tel;
//在一方创建多的集合 并且生成set get 方法
private Set<LinkMan> setlinkman=new HashSet<LinkMan>(); public Set<LinkMan> getSetlinkman() {
return setlinkman;
}
public void setSetlinkman(Set<LinkMan> setlinkman) {
this.setlinkman = setlinkman;
}
public Integer getCid() {
return cid;
}
public void setCid(Integer cid) {
this.cid = cid;
}
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
public String getTel() {
return tel;
}
public void setTel(String tel) {
this.tel = tel;
}
}
<?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>
<class name="org.model.Customer" table="t_customer">
<id name="cid" column="cid">
<generator class="native"></generator>
</id>
<property name="cname" column="cname"></property>
<property name="tel" column="tel"></property> <set name="setlinkman">
<!-- key中column的属性值为外键的名字 可随便写 作用指定外键 在多的一方添加外键 所以 根据class中的路径 在多方 创建的表中就会有clid字段 作为外键 -->
<key column="clid"></key>
<!-- class属性中写的是关联表实体类的名称 -->
<one-to-many class="org.model.LinkMan"/>
</set>
</class>
</hibernate-mapping>
package org.model; public class LinkMan {
private Integer lid;
private String lname;
private String tel;
//在多的一方 指定所属的客户 当然也可以只写一个字段(关联外键的id) 这里写的是一个对象
private Customer customer; public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public Integer getLid() {
return lid;
}
public void setLid(Integer lid) {
this.lid = lid;
}
public String getLname() {
return lname;
}
public void setLname(String lname) {
this.lname = lname;
}
public String getTel() {
return tel;
}
public void setTel(String tel) {
this.tel = tel;
}
}
<?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>
<class name="org.model.LinkMan" table="t_linkman">
<id name="lid" column="lid">
<generator class="native"></generator>
</id>
<property name="lname" column="lname"></property>
<property name="tel" column="tel"></property>
<!-- name中写的是linkman表中关联对象的名称 column写的是外间的名称 class写的是关联对象的类的全路径 指定被关联的实体类-->
<many-to-one name="customer" column="clid" class="org.model.Customer"></many-to-one>
</class>
</hibernate-mapping>
核心配置文件
<?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>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">jay571018</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate3_demo1</property>
<!-- 第二步:配置Hibernate信息 -->
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<!-- 自动建表 -->
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- 设置数据库方言 -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- 设置与本地线程绑定session -->
<property name="hibernate.current_session_context_class">thread</property> <!-- 第三步:引入对象关系映射文件 -->
<mapping resource="org/model/Customer.hbm.xml"/>
<mapping resource="org/model/LinkMan.hbm.xml"/>
</session-factory>
</hibernate-configuration>
sessionfactory
package org.util; import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration; public class SessionFactoryUtils {
static Configuration configuration=null;
static SessionFactory sf=null; static{
configuration=new Configuration();
configuration.configure();//加载核心配置文件
sf=configuration.buildSessionFactory();
}
public static SessionFactory getsessionfactory(){
return sf;
} public static Session get(){
return sf.getCurrentSession();
} public static void main(String[] args){ } }
执行之后效果:
自动完成建表 并且在联系人表(多方)中添加了外键
一对多级联操作
级联操作
1 级联保存
(1)添加一个客户,为这个客户添加多个联系人
2 级联删除
(1)删除某一个客户,这个客户里面的所有的联系人也删除
一对多级联保存
1 添加客户,为这个客户添加一个联系人
(1)复杂写法:
//演示一对多级联保存
@Test
public void testAddDemo1() {
SessionFactory sessionFactory = null;
Session session = null;
Transaction tx = null;
try {
//得到sessionFactory
sessionFactory = HibernateUtils.getSessionFactory();
//得到session
session = sessionFactory.openSession();
//开启事务
tx = session.beginTransaction(); // 添加一个客户,为这个客户添加一个联系人
//1 创建客户和联系人对象
Customer customer = new Customer();
customer.setCustName("传智播客");
customer.setCustLevel("vip");
customer.setCustSource("网络");
customer.setCustPhone("110");
customer.setCustMobile("999"); LinkMan linkman = new LinkMan();
linkman.setLkm_name("lucy");
linkman.setLkm_gender("男");
linkman.setLkm_phone("911"); //2 在客户表示所有联系人,在联系人表示客户
// 建立客户对象和联系人对象关系
//2.1 把联系人对象 放到客户对象的set集合里面
customer.getSetLinkMan().add(linkman);
//2.2 把客户对象放到联系人里面
linkman.setCustomer(customer); //3 保存到数据库
session.save(customer);
session.save(linkman); //提交事务
tx.commit(); }catch(Exception e) {
tx.rollback();
}finally {
session.close();
//sessionFactory不需要关闭
sessionFactory.close();
}
}
执行效果:
(2)简化写法
- 一般根据客户添加联系人
第一步 在客户映射文件中进行配置
- 在客户映射文件里面set标签进行配置
第二步 创建客户和联系人对象,只需要把联系人放到客户里面就可以了,最终只需要保存客户就可以了
//演示一对多级联保存
@Test
public void testAddDemo2() {
SessionFactory sessionFactory = null;
Session session = null;
Transaction tx = null;
try {
//得到sessionFactory
sessionFactory = HibernateUtils.getSessionFactory();
//得到session
session = sessionFactory.openSession();
//开启事务
tx = session.beginTransaction();
// 添加一个客户,为这个客户添加一个联系人
//1 创建客户和联系人对象
Customer customer = new Customer();
customer.setCustName("百度");
customer.setCustLevel("普通客户");
customer.setCustSource("网络");
customer.setCustPhone("110");
customer.setCustMobile("999"); LinkMan linkman = new LinkMan();
linkman.setLkm_name("小宏");
linkman.setLkm_gender("男");
linkman.setLkm_phone("911");
//2 把联系人放到客户里面
customer.getSetLinkMan().add(linkman);
//3 保存客户
session.save(customer); //提交事务
tx.commit();
}catch(Exception e) {
tx.rollback();
}finally {
session.close();
//sessionFactory不需要关闭
sessionFactory.close();
}
}
代码实践:
修改customer配置文件
<?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>
<class name="org.model.Customer" table="t_customer">
<id name="cid" column="cid">
<generator class="native"></generator>
</id>
<property name="cname" column="cname"></property>
<property name="tel" column="tel"></property> <set name="setlinkman" cascade="save-update">
<!-- key中column的属性值为外键的名字 可随便写 作用指定外键 在多的一方添加外键 所以 根据class中的路径 在多方 创建的表中就会有clid字段 作为外键 -->
<key column="clid"></key>
<!-- class属性中写的是关联表实体类的名称 -->
<one-to-many class="org.model.LinkMan"/>
</set>
</class>
</hibernate-mapping>
package org.testdemo; import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.junit.Test;
import org.model.Customer;
import org.model.LinkMan;
import org.util.SessionFactoryUtils; public class OnetoMany {
@Test
public void test1(){
//复杂写法 没有配置cascade属性
Session session=null;
Transaction tran=null;
try{
session=SessionFactoryUtils.get();
//开启事务
tran=session.beginTransaction(); Customer c=new Customer();
c.setCname("阿里");
c.setTel("0379-1534350"); LinkMan l=new LinkMan();
l.setLname("马云");
l.setTel("15035286558"); //分别建立联系
c.getSetlinkman().add(l);
l.setCustomer(c);
//然后分别保存
session.save(c);
session.save(l); //提交事务
tran.commit(); }catch(Exception e){
e.printStackTrace();
}finally{
} }
@Test
public void test2(){
//简单写法 在客户映射文件中配置cascade属性 因为联系人表的外键 约束与客户表的主键 所以一般都是先保存客户表 所以在客户表中进行配置
Session session=null;
Transaction tran=null;
try{
session=SessionFactoryUtils.get();
//开启事务
tran=session.beginTransaction(); Customer c=new Customer();
c.setCname("百度");
c.setTel("0379-1534350"); LinkMan l=new LinkMan();
l.setLname("李彦宏");
l.setTel("15035286558"); //不需要分别建立联系
// c.getSetlinkman().add(l);
// l.setCustomer(c);
//因为客户表是主控方 只需要在客户对象中关联 联系人表的对象即可
//然后分别保存
// session.save(c);
// session.save(l);
c.getSetlinkman().add(l);
session.save(c); //提交事务
tran.commit(); }catch(Exception e){
e.printStackTrace();
}finally{
} }
}
执行效果:
一对多级联删除
1 删除某个客户,把客户里面所有的联系人删除
2 具体实现
第一步 在客户(主控方)映射文件set标签,进行配置
(1)使用属性cascade属性值 delete
第二步 在代码中直接删除客户
(1)根据id查询对象,调用session里面delete方法删除
3 执行过程:
(1)根据id查询客户
(2)根据外键id值查询联系人
(3)把联系人外键设置为null
(4)删除联系人和客户
代码实践:
@Test
public void test3(){
//进行级联删除操作 需要在主控方set标签中增加属性配置
Session session=null;
Transaction tran=null;
try{
session=SessionFactoryUtils.get();
//开启事务
tran=session.beginTransaction();
//A级联删除删除的是主控方的对象 从而把依赖的对象也删除 联系人表的外键依赖于客户表中的主键
Customer c=session.get(Customer.class,4);
session.delete(c);
//B下面这种方式不会级联删除 执行之后只会删掉联系人表中对应的记录 而客户表不会删
// LinkMan l=session.get(LinkMan.class,5);//
// session.delete(l);
tran.commit(); }catch(Exception e){
e.printStackTrace();
}finally{
}
}
删除之前:
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属性 让它放弃表的维护工作
<?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>
<class name="org.model.Customer" table="t_customer">
<id name="cid" column="cid">
<generator class="native"></generator>
</id>
<property name="cname" column="cname"></property>
<property name="tel" column="tel"></property>
<!-- 默认值是false 不放弃维护 这里设置为false 表示放弃维护 -->
<set name="setlinkman" cascade="save-update,delete" inverse="true">
<!-- key中column的属性值为外键的名字 可随便写 作用指定外键 在多的一方添加外键 所以 根据class中的路径 在多方 创建的表中就会有clid字段 作为外键 -->
<key column="clid"></key>
<!-- class属性中写的是关联表实体类的名称 -->
<one-to-many class="org.model.LinkMan"/>
</set>
</class>
</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=?
少了一次更新的语句,性能提高