表的一对多映射配置
以客户和联系人表为例子:客户是一,联系人是多
1、创建实体类,客户和联系人
2、让两个实体类之间互相表示
(1)在客户实体类中要表示多个联系人
- 一个客户有多个联系人
//在客户实体类里面表示多个联系人,一个客户有多个联系人
//hibernate中要表示多的数据,使用set集合,不是list集合
private Set<LinkMan> setLinkMan = new HashSet<LinkMan>();
public Set<LinkMan> getSetLinkMan() {
return setLinkMan;
}
public void setSetLinkMan(Set<LinkMan> setLinkMan) {
this.setLinkMan = setLinkMan;
}
(2)在联系人类中要表示他所属的客户
- 一个联系人只能属于一个客户
//在联系人实体类里面表示所属客户,一个联系人只能属于一个客户
private Customer customer;
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
3、配置映射文件
(1)一个实体类对应一个映射文件
(2)完成最基本的配置
(3)在映射文件中,配置一对多的关系
- 在客户映射文件中,表示客户的所有联系人
<!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="com.demo.entity.Customer" table="t_customer">
<id name="cid" column="cid">
<generator class="native"></generator>
</id>
<property name="custName" column="custName"></property>
<property name="custLevel" column="custLevel"></property>
<property name="custSource" column="custSource"></property>
<property name="custPhone" column="custPhone"></property>
<property name="custMobile" column="custMobile"></property>
<!-- 在客户映射文件中,表示客户的所有联系人
使用set标签表示所有联系人,set标签中有name属性,属性值就是写在客户实体类里表示联系人的set集合的名称
-->
<set name="setLinkMan">
<!-- 一对多建表,有外键
hibernate中:双向维护外键,在一和多的那一方都要配置外键
column属性值:外键名称
-->
<key column="clid"></key>
<!-- 客户的所有联系人,在class中写联系人实体类的全路径 -->
<one-to-many class="com.demo.entity.LinkMan"/>
</set>
</class>
</hibernate-mapping>
- 在联系人映射文件中,表示所属客户
<!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="com.demo.entity.LinkMan" table="t_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>
<!-- 表示联系人所属的客户
name属性:在联系人实体类中用来连接客户实体类的customer对象
class属性:客户实体类的全路径
column属性:外键名称,和客户实体类映射文件中的外键对应:clid
-->
<many-to-one name="customer" class="com.demo.entity.Customer" column="clid"></many-to-one>
</class>
</hibernate-mapping>
4、创建核心配置文件,将映射配置文件引入到核心配置文件中
<mapping resource="com/demo/entity/Customer.hbm.xml"/>
<mapping resource="com/demo/entity/LinkMan.hbm.xml"/>
一对多级联操作
级联操作:
1、级联保存
- 添加一个客户,为这个客户添加联系人
(1)在客户映射文件中进行配置
-- 在客户映射文件里面set标签进行配置
<set name="setLinkMan" cascade="save-update">
(2)创建客户和联系人对象,只需要将联系人对象放到客户对象的set集合中去,最终保存客户对象就可以了
@Test
public void testAdd(){
Session session = null;
Transaction tx= null;
try{
session = HibernateUtils.getSession();
tx = session.beginTransaction();
Customer customer = new Customer();
customer.setCustName("company1");
customer.setCustLevel("level1");
customer.setCustSource("source1");
customer.setCustPhone("111");
customer.setCustMobile("135");
LinkMan linkman = new LinkMan();
linkman.setLkm_name("张三");
linkman.setLkm_gender("男");
linkman.setLkm_phone("1333");
customer.getSetLinkMan().add(linkman);
session.save(customer);
tx.commit();
}
catch(Exception e){
tx.rollback();
}
finally{
session.close();
}
}
2、级联删除
- 删除一个客户,这个客户的所有联系人也删除
正常情况下,若想要删除一个客户的所有联系人,必须先把所有联系人删除完才可以删除客户,因为客户表和联系人表之间存在外键约束,不能直接删除客户表的数据。
使用hibernate可以解决这一问题
在客户实体类的映射文件中的set标签进行设置
<set name="setLinkMan" cascade="save-update,delete">
删除代码:
@Test
public void testDelete(){
Session session = null;
Transaction tx = null;
try{
session = HibernateUtils.getSession();
tx = session.beginTransaction();
//查询customer对象
Customer customer = session.get(Customer.class, 1);
session.delete(customer);
tx.commit();
}
catch(Exception e){
tx.rollback();
}
finally{
session.close();
}
}
执行结果就是数据库中t_customer表中id为1且在t_linkman中以id=1为外键的记录全部被删除了
控制台输出sql语句:
Hibernate:
alter table t_linkman
add constraint FKjtgu0oocf35ij4fmulu123vwk
foreign key (clid)
references t_customer (cid)
Hibernate:
select
customer0_.cid as cid1_0_0_,
customer0_.custName as custName2_0_0_,
customer0_.custLevel as custLeve3_0_0_,
customer0_.custSource as custSour4_0_0_,
customer0_.custPhone as custPhon5_0_0_,
customer0_.custMobile as custMobi6_0_0_
from
t_customer customer0_
where
customer0_.cid=?
Hibernate:
select
setlinkman0_.clid as clid5_1_0_,
setlinkman0_.lkm_id as lkm_id1_1_0_,
setlinkman0_.lkm_id as lkm_id1_1_1_,
setlinkman0_.lkm_name as lkm_name2_1_1_,
setlinkman0_.lkm_gender as lkm_gend3_1_1_,
setlinkman0_.lkm_phone as lkm_phon4_1_1_,
setlinkman0_.clid as clid5_1_1_
from
t_linkman setlinkman0_
where
setlinkman0_.clid=?
Hibernate:
update
t_linkman
set
clid=null
where
clid=?
Hibernate:
delete
from
t_linkman
where
lkm_id=?
Hibernate:
delete
from
t_customer
where
cid=?
一对多修改操作
需求:更改联系人表中的一条数据,使其外键指向客户表中的另一条数据
//演示一对多修改
@Test
public void testUpdate(){
Session session = null;
Transaction tx = null;
try{
session = HibernateUtils.getSession();
tx = session.beginTransaction();
//查询联系人和客户对象
Customer customer = session.get(Customer.class, 2);
LinkMan linkman = session.get(LinkMan.class, 1);
//设置持久态对象值
customer.getSetLinkMan().add(linkman);
linkman.setCustomer(customer);
tx.commit();
}
catch(Exception e){
tx.rollback();
}
finally{
session.close();
}
}
通过控制台输出可发现一个问题:sql语句中发现对外键的修改发生了两次
Hibernate:
update
t_linkman
set
lkm_name=?,
lkm_gender=?,
lkm_phone=?,
clid=?
where
lkm_id=?
Hibernate:
update
t_linkman
set
clid=?
where
lkm_id=?
原因:在hibernate中,对外键的维护是双向的,在客户实体类和联系人实体类中都需要修改外键
解决方式:可以让其中的一方放弃对外键的维护,在放弃关系维护的那一方的配置文件进行配置
在set标签上添加inverse属性
//inverse:默认值是false,不放弃对关系的维护
<set name="setLinkMan" cascade="save-update,delete" inverse="true">
Hibernate:
update
t_linkman
set
lkm_name=?,
lkm_gender=?,
lkm_phone=?,
clid=?
where
lkm_id=?
结果对外键的维护只更新了一次