表与表之间关系
- 一对多:分类与商品之间的关系:一个分类中可以有多种商品,一种商品只能属于某一分类
- 多对多:订单和商品之间的关系:一个订单里面有多种商品,一种商品可以属于多个订单
- 一对一:在实际开发中使用的不是很多
Hibernate一对多操作
一对多关联关系的建立
第一步
:创建两个实体类
package com.ycom.hibernate.crm.po;
// 实体类:客户类
public class Customer {
private Integer id; // 客户id
private String name; // 客户名称
private String level; // 客户级别
private String source; // 客户来源
private String phone; // 联系电话
private String mobile; // 联系手机
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLevel() {
return level;
}
public void setLevel(String level) {
this.level = level;
}
public String getSource() {
return source;
}
public void setSource(String source) {
this.source = source;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
}
==========================================================================
package com.ycom.hibernate.crm.po;
// 实体类:联系人
public class LinkMan {
private Integer id; // 联系人id
private String name; // 联系人名称
private String gender; // 联系人性别
private String phone; // 联系人电话
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
第二步
:让两个实体类关联起来:对象关系
(1) 一方关联多方
(2) 多方关联一方
第三步
:配置映射文件:一般一个实体类对应一个映射文件
(1) 把映射成两个类自己的最基本配置
<?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="com.ycom.hibernate.crm.po.Customer" table="t_customer">
<!-- 第一部分: 配置实体类唯一属性与表主键的映射关系 -->
<id name="id" column="id">
<generator class="native"></generator>
</id>
<!-- 第二部分:配置实体类其他属性与表的字段的映射关系 -->
<property name="name" column="name"></property>
<property name="level" column="level"></property>
<property name="source" column="source"></property>
<property name="phone" column="phone"></property>
<property name="mobile" column="mobile"></property>
<!-- 第三部分:配置两个实体类之间的映射关系 -->
</class>
</hibernate-mapping>
----------------------------------------------------------------------------------
<?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="com.ycom.hibernate.crm.po.LinkMan" table="t_linkman">
<!-- 第一部分: 配置实体类唯一属性与表主键的映射关系 -->
<id name="id" column="id">
<generator class="native"></generator>
</id>
<!-- 第二部分:配置实体类其他属性与表的字段的映射关系 -->
<property name="name" column="name"></property>
<property name="gender" column="gender"></property>
<property name="phone" column="phone"></property>
<!-- 第三部分:配置两个实体类之间的映射关系 -->
</class>
</hibernate-mapping>
(2) 在映射配置文件中配置一对多的关系
<?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="com.ycom.hibernate.crm.po.Customer" table="t_customer">
<!-- 第一部分: 配置实体类唯一属性与表主键的映射关系 -->
<id name="id" column="id">
<generator class="native"></generator>
</id>
<!-- 第二部分:配置实体类其他属性与表的字段的映射关系 -->
<property name="name" column="name"></property>
<property name="level" column="level"></property>
<property name="source" column="source"></property>
<property name="phone" column="phone"></property>
<property name="mobile" column="mobile"></property>
<!-- 第三部分:配置两个实体类之间的映射关系
name属性:就是客户实体类中联系人Set集合的属性名称
-->
<set name="linkMans" >
<!-- 外键配置
一对多建表有外键:Hibernate机制:双向维护外键,即在一方与多方都要配置外键
column属性:外键名称
-->
<key column="fk_limlman_customer"></key>
<!-- 一方关联多方
class属性:一方关联多方的多方的实体类的全限定类名
-->
<one-to-many class="com.ycom.hibernate.crm.po.LinkMan"/>
</set>
</class>
</hibernate-mapping>
----------------------------------------------------------------------------------
<?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="com.ycom.hibernate.crm.po.LinkMan" table="t_linkman">
<!-- 第一部分: 配置实体类唯一属性与表主键的映射关系 -->
<id name="id" column="id">
<generator class="native"></generator>
</id>
<!-- 第二部分:配置实体类其他属性与表的字段的映射关系 -->
<property name="name" column="name"></property>
<property name="gender" column="gender"></property>
<property name="phone" column="phone"></property>
<!-- 第三部分:配置两个实体类之间的映射关系 -->
<!-- 多方关联一方
name属性:实体类中关联的一方的属性名称
class属性:多方关联的一方的全限定类名
column属性:外键名称,Hibernate要求关联双方必须双向维护外键
-->
<many-to-one name="customer"
class="com.ycom.hibernate.crm.po.Customer"
column="fk_limlman_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>
<!-- 访问数据库服务器的url -->
<property name="hibernate.connection.url">jdbc:mysql:///hibernate05</property>
<!-- 访问数据库服务器的用户名 -->
<property name="hibernate.connection.username">root</property>
<!-- 访问数据库服务器的密码 -->
<property name="hibernate.connection.password">root</property>
<!-- 第二部分:Hibernate自身的配置 -->
<!-- 让Hibernate输出SQL语句 -->
<property name="hibernate.show_sql">true</property>
<!-- 让Hibenate输出的SQL语句是格式化的SQL语句 -->
<property name="hibernate.format_sql">true</property>
<!-- 让Hibernate帮助我们创建数据表 -->
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- 配置MySQL数据库方言 -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- 获取本地线程Session的配置 -->
<property name="hibernate.current_session_context_class">thread</property>
<!-- 第三部分:加载映射配置文件 -->
<mapping resource="com/ycom/hibernate/crm/po/Customer.hbm.xml"/>
<mapping resource="com/ycom/hibernate/crm/po/LinkMan.hbm.xml"/>
</session-factory>
</hibernate-configuration>
第五步
:测试能否自动创建两张表
一对多操作:级联保存
复杂的操作
@Test
public void testSave() {
Customer customer = new Customer();
customer.setName("AAAAA");
customer.setLevel("Vip");
customer.setSource("网络");
customer.setPhone("110");
customer.setMobile("123456");
LinkMan man1 = new LinkMan();
man1.setName("M1");
man1.setGender("male");
man1.setPhone("123456");
LinkMan man2 = new LinkMan();
man2.setName("M2");
man2.setGender("female");
man2.setPhone("12345678");
// 建立对象的关联关系
// 一方关联多方
customer.getLinkMans().add(man1);
customer.getLinkMans().add(man2);
// 多方关联一方
man1.setCustomer(customer);
man2.setCustomer(customer);
SessionFactory sessionFactory = null;
Session session = null;
Transaction tx = null;
try {
sessionFactory = HibernateUtils.getSessionFactory();
session = sessionFactory.openSession();
tx = session.beginTransaction();
session.save(customer);
session.save(man1);
session.save(man2);
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
session.close();
sessionFactory.close();//在Web项目开发中不关闭
}
}
-------------------------------------------------------------------------------
结果:
Hibernate:
insert
into
t_customer
(name, level, source, phone, mobile)
values
(?, ?, ?, ?, ?)
Hibernate:
insert
into
t_linkman
(name, gender, phone, fk_limlman_customer)
values
(?, ?, ?, ?)
Hibernate:
insert
into
t_linkman
(name, gender, phone, fk_limlman_customer)
values
(?, ?, ?, ?)
Hibernate:
update
t_linkman
set
fk_limlman_customer=?
where
id=?
Hibernate:
update
t_linkman
set
fk_limlman_customer=?
where
id=?
(1) 需要一方关联多方,同样需要多方关联一方
(2) session.sqve()既需要保存一方,也需要报错多方:例如多方有100个,那么需要save一百次
简单的操作
:就是对上面的关联关系进行简化,同时对session.save()操作进行简化:前提:需要在一方的映射配置文件中进行相关属性的配置:在客户实体类的映射配置文件中的set标签中进行配置
(1) 映射配置文件如下
(2)程序代码如下
@Test
public void testSave2() {
Customer customer = new Customer();
customer.setName("AAAAA");
customer.setLevel("Vip");
customer.setSource("网络");
customer.setPhone("110");
customer.setMobile("123456");
LinkMan man1 = new LinkMan();
man1.setName("M1");
man1.setGender("male");
man1.setPhone("123456");
LinkMan man2 = new LinkMan();
man2.setName("M2");
man2.setGender("female");
man2.setPhone("12345678");
// 建立对象的关联关系:只需要关联一次即可
customer.getLinkMans().add(man1);
customer.getLinkMans().add(man2);
SessionFactory sessionFactory = null;
Session session = null;
Transaction tx = null;
try {
sessionFactory = HibernateUtils.getSessionFactory();
session = sessionFactory.openSession();
tx = session.beginTransaction();
// 只需要保存一方对象即可
session.save(customer);
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
session.close();
sessionFactory.close();//在Web项目开发中不关闭
}
}
------------------------------------------------------------------------------
Hibernate:
insert
into
t_customer
(name, level, source, phone, mobile)
values
(?, ?, ?, ?, ?)
Hibernate:
insert
into
t_linkman
(name, gender, phone, fk_limlman_customer)
values
(?, ?, ?, ?)
Hibernate:
insert
into
t_linkman
(name, gender, phone, fk_limlman_customer)
values
(?, ?, ?, ?)
Hibernate:
update
t_linkman
set
fk_limlman_customer=?
where
id=?
Hibernate:
update
t_linkman
set
fk_limlman_customer=?
where
id=?
(3)结果:
一对多操作:级联删除
- 级联删除:删除主表中的记录,子表中的所有关联的记录都被删除
- 先查询主表中的记录得到持久化的对象,执行删除操作
- 代码
@Test
public void testDeleteCascade() {
SessionFactory sessionFactory = null;
Session session = null;
Transaction tx = null;
try {
sessionFactory = HibernateUtils.getSessionFactory();
session = sessionFactory.openSession();
tx = session.beginTransaction();
Customer customer = session.get(Customer.class, 4);
session.delete(customer);
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
session.close();
sessionFactory.close();//在Web项目开发中不关闭
}
}
*结果:
- 删除主表中的记录,子表中关联的记录是逻辑删除,即并不是真正的删除子表中的关联记录,而是将子表中关联的记录的外键字段置为NULL:如果要达到级联删除的目的,需要在映射配置文件中进行相关的配置:在一方的映射配置文件中的set标签中的cascade属性添加delete值
- 执行过程
- 那么问题来了:既然是要删除子表中关联的记录,那么在删除之间为什么还需要进行外键字段的置空操作呢?实际上直接先删除子表中的关联记录再删除主表中的记录即可!不需要再搞什么update操作了:之所以有这个操作是因为:Hibernate双向维护外键,一方与多方都需要维护外键!解决办法:让其中一方放弃维护外键:在一对多的关系中让一方放弃维护外键:需要在一方的映射配置文件中的set标签的inverse属性值为true
注意
:不能在任何时候都放弃外键关系的维护:如果是这样那就不能做级联添加了:子表记录的外键字段为NULL,因为一方没有维护外键是不行的
一对多修改(inverse=true)
@Test
public void testUpdateCascade() {
SessionFactory sessionFactory = null;
Session session = null;
Transaction tx = null;
try {
sessionFactory = HibernateUtils.getSessionFactory();
session = sessionFactory.openSession();
tx = session.beginTransaction();
Customer customer = session.get(Customer.class, 12);
LinkMan man = session.get(LinkMan.class, 21);
customer.getLinkMans().add(man);
man.setCustomer(customer);
System.out.println(customer);
System.out.println(man);
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
session.close();
sessionFactory.close();//在Web项目开发中不关闭
}
}
----------------------------------------------------------------------------
Hibernate:
select
customer0_.id as id1_0_0_,
customer0_.name as name2_0_0_,
customer0_.level as level3_0_0_,
customer0_.source as source4_0_0_,
customer0_.phone as phone5_0_0_,
customer0_.mobile as mobile6_0_0_
from
t_customer customer0_
where
customer0_.id=?
Hibernate:
select
linkman0_.id as id1_1_0_,
linkman0_.name as name2_1_0_,
linkman0_.gender as gender3_1_0_,
linkman0_.phone as phone4_1_0_,
linkman0_.fk_limlman_customer as fk_limlm5_1_0_
from
t_linkman linkman0_
where
linkman0_.id=?
Hibernate:
select
linkmans0_.fk_limlman_customer as fk_limlm5_1_0_,
linkmans0_.id as id1_1_0_,
linkmans0_.id as id1_1_1_,
linkmans0_.name as name2_1_1_,
linkmans0_.gender as gender3_1_1_,
linkmans0_.phone as phone4_1_1_,
linkmans0_.fk_limlman_customer as fk_limlm5_1_1_
from
t_linkman linkmans0_
where
linkmans0_.fk_limlman_customer=?
Customer [id=12, name=BBBBB, level=null, source=null, phone=null, mobile=null]
LinkMan [id=21, name=M2, gender=female, phone=12345678, customer=Customer [id=12, name=BBBBB, level=null, source=null, phone=null, mobile=null]]
Hibernate:
update
t_linkman
set
name=?,
gender=?,
phone=?,
fk_limlman_customer=?
where
id=?
Hibernate:
update
t_linkman
set
fk_limlman_customer=?
where
id=?
- inverse属性:因为Hibernate双向维护外键,在客户和联系人里面都需要维护外键,修改客户时候修改一次外键,修改联系人时候也修改一次外键,造成效率问题
- 解决方式:让其中的一方不维护外键:一对多里面,让其中一方放弃外键维护
- 上面说错了 :