首先介绍一下一对多是什么,一对多关系是关系数据库中两个表之间的一种关系,该关系中第一个表中的单个行可以与第二个表中的一个或多个行相关,但第二个表中的一个行只可以与第一个表中的一个行相关。
例如:我有一个Customer类表示客户,一个Linkman类表示联系人。在这个关系中,我定义一个客户可以对应多个联系人,而一个联系人只能对应一个客户,那么这里的客户就是“一”,联系人就是“多”。
一对多关系的建表原则:在“多”的一方创建字段作为外键,指向“一”的一方的主键。
例如上图的例子,在“多”的一方创建一个外键cid,指向“一”的一方。例子中的客户张三就有两个联系人。
首先创建两个实体类Customer类和Linkman类,注意在这两个类中要相互关联
我的Customer类中采用Set集合的方式关联Linkman类来表示一个客户可以有多个联系人
package com.hibernate.entity;
import java.util.HashSet;
import java.util.Set;
public class Customer {
//客户id
private Integer cid;
//客户姓名
private String cName;
//客户等级
private String cLevel;
//客户来源
private String cSource;
//客户联系电话
private String cPhone;
//客户邮箱
private String cMail;
//联系人
private Set linkmans = new HashSet();
public Set getLinkmans() {
return linkmans;
}
public void setLinkmans(Set linkmans) {
this.linkmans = linkmans;
}
public Integer getCid() {
return cid;
}
public String getcName() {
return cName;
}
public String getcLevel() {
return cLevel;
}
public String getcSource() {
return cSource;
}
public String getcPhone() {
return cPhone;
}
public String getcMail() {
return cMail;
}
public void setCid(Integer cid) {
this.cid = cid;
}
public void setcName(String cName) {
this.cName = cName;
}
public void setcLevel(String cLevel) {
this.cLevel = cLevel;
}
public void setcSource(String cSource) {
this.cSource = cSource;
}
public void setcPhone(String cPhone) {
this.cPhone = cPhone;
}
public void setcMail(String cMail) {
this.cMail = cMail;
}
@Override
public String toString() {
return "Customer [cid=" + cid + ", cName=" + cName + ", cLevel=" + cLevel + ", cSource=" + cSource + ", cPhone="
+ cPhone + ", cMail=" + cMail + ", linkmans=" + linkmans + "]";
}
}
package com.hibernate.entity;
public class Linkman {
//联系人id
private Integer lkm_id;
//联系人姓名
private String lkm_name;
//联系人性别
private String lkm_gender;
//联系人电话
private String lkm_phone;
//所属客户
private Customer lkm_customer;
public Customer getLkm_customer() {
return lkm_customer;
}
public void setLkm_customer(Customer lkm_customer) {
this.lkm_customer = lkm_customer;
}
public Integer getLkm_id() {
return lkm_id;
}
public String getLkm_name() {
return lkm_name;
}
public String getLkm_gender() {
return lkm_gender;
}
public String getLkm_phone() {
return lkm_phone;
}
public void setLkm_id(Integer lkm_id) {
this.lkm_id = lkm_id;
}
public void setLkm_name(String lkm_name) {
this.lkm_name = lkm_name;
}
public void setLkm_gender(String lkm_gender) {
this.lkm_gender = lkm_gender;
}
public void setLkm_phone(String lkm_phone) {
this.lkm_phone = lkm_phone;
}
@Override
public String toString() {
return "Linkman [lkm_id=" + lkm_id + ", lkm_name=" + lkm_name + ", lkm_gender=" + lkm_gender + ", lkm_phone="
+ lkm_phone + ", lkm_customer=" + lkm_customer + "]";
}
}
实体类有了,我们可以开始配置hibernate的对象映射文件了,推荐采用一个实体类创建一个对象映射文件的方式,注意在这里的配置中我们就要进行级联的相关配置了
Customer.hbm.xml文件的配置如下:
Linkman.hbm.xml文件的配置如下:
•
– name: 设定待映射的持久化类的属性的名字
– column: 设定和持久化类的属性对应的表的外键
– class:设定待映射的持久化类的属性的类型
•
– name: 设定待映射的持久化类的属性的
•
– column: 指定关联表的外键名
•
class: 指定关联的持久化类的类名
配置了对象映射文件后就是hibernate的核心配置文件hibernate.cfg.xml了
com.mysql.jdbc.Driver
jdbc:mysql://localhost:3306/javadatabase
root
*******
thread
true
true
update
org.hibernate.dialect.MySQL5InnoDBDialect
这里我配置了
对应上文Customer.hbm.xml中的save-update
测试代码如下:
//简化级联保存,需在配置“一”一方的对象映射文件中为标签配置cascade属性为save-update
@Test
public void testSave(){
Session session = null;
Transaction tx = null;
try {
session = HibernateUtil.getSession();
tx = session.beginTransaction();
//创建客户
Customer customer = new Customer();
customer.setcName("张三");
customer.setcLevel("vip");
customer.setcSource("网站");
customer.setcPhone("13622222222");
customer.setcMail("[email protected]");
//创建联系人
Linkman linkman = new Linkman();
linkman.setLkm_name("王舞");
linkman.setLkm_gender("女");
linkman.setLkm_phone("13678944321");
//关联客户和联系人
customer.getLinkmans().add(linkman);
//保存
session.save(customer);
tx.commit();
} catch (Exception e) {
tx.rollback();
}finally {
session.close();
}
}
执行完成后,可以发现数据库中创建了对应的两张表
表中信息如下:
接下来我们看看控制台中的sql语句的输出
首先创建了两张表
然后为hibernate_Linkman添加了外键指向hibernate_Customer的cid
再为两张表添加数据
最后为hibernate_Linkman中的外键赋值
以上就是创建并保存的全过程。
这里我们首先看看数据库中修改前的数据
接下来我们将李四的联系人赵柳,改到张三的联系人中
测试代码如下
//级联修改
@Test
public void testUpdate(){
Session session = null;
Transaction tx = null;
try {
session = HibernateUtil.getSession();
tx = session.beginTransaction();
//根据id获取客户
Customer customer = session.get(Customer.class, 1);
//获取联系人
Linkman linkman = session.get(Linkman.class, 2);
//修改持久态对象实现数据库自动更新
customer.getLinkmans().add(linkman);
linkman.setLkm_customer(customer);
tx.commit();
} catch (Exception e) {
tx.rollback();
}finally {
session.close();
}
}
此时我们发现数据库中数据改变了
但此时我们看到控制台中的update语句执行了两次
Hibernate:
select
customer0_.cid as cid1_0_0_,
customer0_.cName as cName2_0_0_,
customer0_.cLevel as cLevel3_0_0_,
customer0_.cSource as cSource4_0_0_,
customer0_.cPhone as cPhone5_0_0_,
customer0_.cMail as cMail6_0_0_
from
hibernate_Customer customer0_
where
customer0_.cid=?
Hibernate:
select
linkman0_.lkm_id as lkm_id1_1_0_,
linkman0_.lkm_name as lkm_name2_1_0_,
linkman0_.lkm_gender as lkm_gend3_1_0_,
linkman0_.lkm_phone as lkm_phon4_1_0_,
linkman0_.clid as clid5_1_0_
from
hibernate_Linkman linkman0_
where
linkman0_.lkm_id=?
Hibernate:
select
linkmans0_.clid as clid5_1_0_,
linkmans0_.lkm_id as lkm_id1_1_0_,
linkmans0_.lkm_id as lkm_id1_1_1_,
linkmans0_.lkm_name as lkm_name2_1_1_,
linkmans0_.lkm_gender as lkm_gend3_1_1_,
linkmans0_.lkm_phone as lkm_phon4_1_1_,
linkmans0_.clid as clid5_1_1_
from
hibernate_Linkman linkmans0_
where
linkmans0_.clid=?
Hibernate:
update
hibernate_Linkman
set
lkm_name=?,
lkm_gender=?,
lkm_phone=?,
clid=?
where
lkm_id=?
Hibernate:
update
hibernate_Linkman
set
clid=?
where
lkm_id=?
这就说明效率并不高,同样的数据进行了两次update。
这里就要介绍一下
关系
在 一对多 关系中,将“多”的一方设为主控方将有助于性能改善
接下来我们进行配置,将Customer.hbm.xml中的
然后我们再把刚刚做的数据库修改再改回去。
@Test
public void testUpdate(){
Session session = null;
Transaction tx = null;
try {
session = HibernateUtil.getSession();
tx = session.beginTransaction();
//根据id获取客户
Customer customer = session.get(Customer.class, 2);
//获取联系人
Linkman linkman = session.get(Linkman.class, 2);
//修改持久态对象实现数据库自动更新
customer.getLinkmans().add(linkman);
linkman.setLkm_customer(customer);
tx.commit();
} catch (Exception e) {
tx.rollback();
}finally {
session.close();
}
}
可以看到数据已经修改回原来的值了
Hibernate:
select
customer0_.cid as cid1_0_0_,
customer0_.cName as cName2_0_0_,
customer0_.cLevel as cLevel3_0_0_,
customer0_.cSource as cSource4_0_0_,
customer0_.cPhone as cPhone5_0_0_,
customer0_.cMail as cMail6_0_0_
from
hibernate_Customer customer0_
where
customer0_.cid=?
Hibernate:
select
linkman0_.lkm_id as lkm_id1_1_0_,
linkman0_.lkm_name as lkm_name2_1_0_,
linkman0_.lkm_gender as lkm_gend3_1_0_,
linkman0_.lkm_phone as lkm_phon4_1_0_,
linkman0_.clid as clid5_1_0_
from
hibernate_Linkman linkman0_
where
linkman0_.lkm_id=?
Hibernate:
select
linkmans0_.clid as clid5_1_0_,
linkmans0_.lkm_id as lkm_id1_1_0_,
linkmans0_.lkm_id as lkm_id1_1_1_,
linkmans0_.lkm_name as lkm_name2_1_1_,
linkmans0_.lkm_gender as lkm_gend3_1_1_,
linkmans0_.lkm_phone as lkm_phon4_1_1_,
linkmans0_.clid as clid5_1_1_
from
hibernate_Linkman linkmans0_
where
linkmans0_.clid=?
Hibernate:
update
hibernate_Linkman
set
lkm_name=?,
lkm_gender=?,
lkm_phone=?,
clid=?
where
lkm_id=?
可以看到update语句只有一句了,则提高了效率。
先看目前数据库中数据的情况
接下来我们删除第二个客户李四,由于第二个客户李四的联系人是赵柳,那么删除李四的同时也会删除赵柳。
现在控制台中的输出如下:
//级联删除,在“一”的一方标签中配置cascade属性为delete(多个值用英文的“,”隔开)
@Test
public void testDelete(){
Session session = null;
Transaction tx = null;
try {
session = HibernateUtil.getSession();
tx = session.beginTransaction();
//根据id获取客户
Customer customer = session.get(Customer.class, 2);
//删除
session.delete(customer);
tx.commit();
} catch (Exception e) {
tx.rollback();
}finally {
session.close();
}
}
执行之后数据库的变化
发现两张表的第二个记录都被删除了。
接着我们看看控制台中的sql语句的输出
Hibernate:
select
customer0_.cid as cid1_0_0_,
customer0_.cName as cName2_0_0_,
customer0_.cLevel as cLevel3_0_0_,
customer0_.cSource as cSource4_0_0_,
customer0_.cPhone as cPhone5_0_0_,
customer0_.cMail as cMail6_0_0_
from
hibernate_Customer customer0_
where
customer0_.cid=?
Hibernate:
select
linkmans0_.clid as clid5_1_0_,
linkmans0_.lkm_id as lkm_id1_1_0_,
linkmans0_.lkm_id as lkm_id1_1_1_,
linkmans0_.lkm_name as lkm_name2_1_1_,
linkmans0_.lkm_gender as lkm_gend3_1_1_,
linkmans0_.lkm_phone as lkm_phon4_1_1_,
linkmans0_.clid as clid5_1_1_
from
hibernate_Linkman linkmans0_
where
linkmans0_.clid=?
Hibernate:
delete
from
hibernate_Linkman
where
lkm_id=?
Hibernate:
delete
from
hibernate_Customer
where
cid=?
我们发现一个细节,hibernate是先删除了linkman再删除的customer。这是因为linkman中含有一个外键,若先删除customer,则有可能linkman中原来所指向的customer就没有了,那么逻辑上的对应关系就不存在了,所以数据库不允许我们这样操作。