Hibernate数据关系映射
**
1 * 一对多/多对一**
① 场景
一个人可能有多个地址,在数据库中就可以建立两张表,一个是用户表,一个是地址表,为了反映这种关系,我们需要在地址表中保存该对应用的ID,从用户来讲一个用户就对应多个地址,这就是一对多的关系,从地址来讲,多个地址对应一个人,这就是多对一的关系。
② 数据库表的设计
在多方要保存一方的外键
③ Java实体类的设计
在多方保存一方的对象,(如果是双向的关联在一方保存多方的对象的Set集合)
注:一般情况下一张数据库表对应一个VO类
UserVo.java
public class UserVo {
private int id;
private String userName;
private String password;
private String email;
//存放多个地址的集合
private Set<AddressVo> adds;
//get/set方法
}
AddressVo.java
public class AddressVo {
private int id;
private String city;
private String street;
//存放用户的对象
private UserVo user;
//get/set方法
}
④ Vo类相对应的映射文件的配置
UserVo.hbm.xml
<?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.xixw.vo.UserVo" table="user_table1">
<!-- 映射主键 -->
<id name="id" column="id">
<!-- 设置主键的生成策略/用序列生成主键 -->
<generator class="sequence">
<!--指定生成主键的序列hibernate_sequence -->
<param name="sequence">hibernate_sequence</param>
</generator>
</id>
<property name="userName" type="java.lang.String"></property>
<property name="password"></property>
<property name="email"></property>
<!-- 配置地址集合 -->
<set name="adds">
<!-- 多的一方存贮的外键的列名 -->
<key column="userid"></key>
<!-- 多的一方对应的类的完全限定名 -->
<one-to-many class="com.xixw.vo.AddressVo"/>
</set>
</class>
</hibernate-mapping>
AddressVo.hbm.xml
<?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.xixw.vo.AddressVo" table="address_table1">
<id name="id" column="id">
<generator class="sequence">
<param name="sequence">hibernate_sequence</param>
</generator>
</id>
<property name="city" />
<property name="street"/>
<!-- name:指定地址类中的用户对象名称,class:指定用户类对象的完全限定名,column:指定数据库中外键名称 -->
<many-to-one name="user" class="com.xixw.vo.UserVo" column="userid"></many-to-one>
</class>
</hibernate-mapping>
⑤ 测试类进行测试
package com.xixw.test;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import com.xixw.vo.AddressVo;
import com.xixw.vo.UserVo;
public class ManyToOneTest {
public static void main(String[] args) {
Configuration cfg=new Configuration();
cfg.configure();
ServiceRegistry registry=new ServiceRegistryBuilder().applySettings(cfg.getProperties()).buildServiceRegistry();
SessionFactory factory=cfg.buildSessionFactory(registry);
Session session=factory.openSession();
//4.获取session对象
Session session=factory.openSession();
Transaction tr=session.beginTransaction();
UserVo user=new UserVo();
user.setUserName("小军");
user.setPassword("888888");
user.setEmail("123@qq.com");
Set<AddressVo> set=new HashSet<AddressVo>();
AddressVo address=new AddressVo();
address.setCity("西安");
address.setStreet("郭杜街道");
address.setUser(user);
set.add(address);
user.setAdds(set);
session.save(user);
session.save(address);
tr.commit();
session.close();
}
}
⑥ 数据库表的创建
如果要自动生成数据库表,需要在hibernate的主配置文件中添加如下配置:
<!-- 设置是否自动创建表 -->
<property name="hibernate.hbm2ddl.auto">update</property>
update:代表如果数据库中没有表则进行创建,如果有,并且表的结构改变了,则进行更新,否则不变。
create:代表每次执行都会删除原来的表,创建新的表
none:不进行数据库表的自动创建
<!--inverse:设置维护关系,false:不放弃维护 true:代表放弃维护
默认双方都维护,一般我们会设置让“多”的一方去维护,“一”的一方放弃维护
-->
<set name="adds" inverse="true">
<!-- 多的一方存贮的外键的列名 -->
<key column="userid"></key>
<!-- 多的一方对应的类的完全限定名 -->
<one-to-many class="com.xixw.vo.AddressVo"/>
</set>
2. 一对一
场景
一个人对应一张身份证,一张身份证只能对应一个人
一对一映射关系分两种:主键关联和唯一外键关联
主键关联:两张表共用一个主键
唯一外键关联:特殊的多对一的关系
**
主键关联
**
① 主键关联数据库表的设计方案
从表的主键的生成关联的是主表的主键,从表的主键即是主键又是外键。
② 实体类设计方案
两个实体类中互存对象
PersonVo.java
public class PersonVo {
private int id;
private String name;
//存放唯一对应的CardVo对象
private CardVo card;
//get/set方法
}
CardVo.java
public class CardVo {
private int id;
private String cardNo;
//存放唯一对应的PersonVo对象
private PersonVo person;
//get/set方法
}
③ Vo类相对应的映射文件的配置
PersonVo.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.xixw.OneToOne">
<!-- 映射类和表 -->
<class name="Person" table="person_t">
<!-- 映射主键 -->
<id name="id">
<!-- 设置主键的生成策略 -->
<generator class="increment"></generator>
</id>
<!-- 映射普通属性 -->
<property name="name"></property>
<!-- 配置映射关系
cascade:设置对象的级联操作
delete:在执行删除操作的时候进行级联删除
save-update:在执行保存或者更新的情况下进行级联操作
all:在所有的情况下都会进行级联操作
none:不进行级联操作(默认)。
注意:尽量避免使用all进行级联,因为会降低程序的执行效率。
-->
<!-- 设置一对一关系,name:对象中存放的CardVo对象的属性名 -->
<one-to-one name="card" class="Card" cascade="all"></one-to-one>
</class>
</hibernate-mapping>
CardVo.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.bxit.OneToOne">
<!-- 映射类和表 -->
<class name="Card" table="card_t">
<!-- 映射主键 -->
<id name="id">
<!-- 设置主键的生成策略,从表主键的生成方式必须要依赖于主表
foreign:以外键的形式生成主键 -->
<generator class="foreign">
<!-- 指定外键的依赖的对象属性 -->
<param name="property">person</param>
</generator>
</id>
<!-- 映射普通属性 -->
<property name="cardNo"></property>
<!-- 映射对象 -->
<one-to-one name="person" class="Person" cascade="all">
</one-to-one>
</class>
</hibernate-mapping>
设置级联操作:cascade:save-update/delete/all/none
<!-- 设置级联策略cascade
all:在所有的情况下都进行级联操作
save-update:只是在保存和更新的时候进行级联操作
delete:只是在删除的时候进行级联操作
none:不进行级联操作(默认)
级联操作会降低程序的执行效率,在需要的时候进行添加,尽量不要设置级联操作为all
-->
<one-to-one name="card" class="com.xixw.vo.CardVo" cascade="delete">
</one-to-one>
④ 元数据加入到主配置文件中
<mapping resource="com/xixw/OneToOne/Person.hbm.xml"/>
<mapping resource="com/xixw/OneToOne/Card.hbm.xml"/>
⑤ 测试
package com.xixw.OneToOne;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
public class Test {
public static void main(String[] args) {
Configuration cfg=new Configuration();
//加载主配置文件
cfg.configure();
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(cfg.getProperties()).buildServiceRegistry();
SessionFactory factory = cfg.buildSessionFactory(serviceRegistry);
//获取session对象
Session session=factory.openSession();
Transaction tr=session.beginTransaction();
Person person=new Person();
person.setName("张三");
Card card=new Card();
card.setCardNo("999999999");
person.setCard(card);
card.setPerson(person);
//保存数据
session.save(person);
//session.save(card);
/*Person person=(Person) session.get(Person.class, 1);
session.delete(person);*/
//System.out.println(person.getName());
//System.out.println(person.getCard().getCardNo());
tr.commit();
session.close();
}
}
唯一外键关联
① 唯一外键关联数据库表设计方案
两张表中互存外键
② 实体类的设计方案(参考主键关联的设计方案)
两个实体类中互存对象
③ Vo类映射文件配置
PersonVo.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.xixw.OneToOne1">
<!-- 映射表和类 -->
<class name="Person1" table="person1_t">
<!-- 映射主键 -->
<id name="id">
<generator class="increment"></generator>
</id>
<!-- 映射普通属性 -->
<property name="name"></property>
<!-- 映射对象
特殊的多对一:所以使用many-to-one
-->
<many-to-one name="card" class="Card1" column="cid" cascade="delete"></many-to-one>
</class>
</hibernate-mapping>
CardVo.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.xixw.OneToOne1">
<!-- 映射类和表 -->
<class name="Card1" table="card1_t">
<!-- 映射主键 -->
<id name="id">
<generator class="increment"></generator>
</id>
<!-- 映射普通属性 -->
<property name="cardNo"></property>
<!-- 映射对象 -->
<many-to-one name="person" class="Person1" column="pid" cascade="delete"></many-to-one>
</class>
</hibernate-mapping>
④ 元数据加入到主配置文件中
<mapping resource="com/xixw/OneToOne1/Person.hbm.xml"/>
<mapping resource="com/xixw/OneToOne1/Card.hbm.xml"/>
*3. 多对多
① 场景
学生老师的对应关系
② 数据库表设计方案
产生一个中间表来表明两张表之间的关系,中间表最少有两个字段,两个字段对应的是两张表的主键,两个字段共同组成了中间表的主键,叫做复合主键。
③ Vo类设计
两个对象互存集合
TeacherVo.java
public class TeacherVo {
private int id;
private String name;
private Set<StudentVo> stuSet;
//get()和set()方法
}
StudentVo.java
public class StudentVo {
private int id;
private String name;
private Set< TeacherVo> teaSet;
//get()和set()方法
}
④ Vo类的映射文件配置
TeacherVo.hbm.xml
<?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 package=”com.xixw.vo”>
<class name="TeacherVo" table="tea_table">
<id name="id" >
<generator class="increment"></generator>
</id>
<property name="name" column="t_name"/>
<!--name:对应TeacherVo类中set集合的名称
table:对应多对多关系的中间表
<key column>:中间表中teacher表的对应的外键
-->
<set name="stuSet" table="table_s_t" fetch="join">
<key column="t_id"></key>
<!--column:中间表中student表的对应的外键
class:对应StudentVo类的完全限定名
-->
<many-to-many column="s_id" class="StudentVo">
</many-to-many>
</set>
</class>
</hibernate-mapping>
StudentVo.hbm.xml
<?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 package=”com.xixw.vo”>
<class name="StudentVo" table="stu_table">
<id name="id" >
<generator class="increment"></generator>
</id>
<property name="name" column="s_name"/>
<!--name:对应StudentVo类中set集合的名称
table:对应多对多关系的中间表
<key column>:中间表中student表的对应的外键
-->
<!--fetch:设置hibernate查询的抓取策略(查询方式)
值:join:使用外连接进行查询
select:使用select进行查询
subselect:使用子查询进行查询
-->
<!--lazy:设置hibernate延时加载功能
值:true:打开延时加载的功能(默认)
False:关闭延时加载的功能
-->
<set name="teaSet" table="table_s_t" lazy="true" fetch="join">
<key column="s_id"></key>
<!--column:中间表中teacher表的对应的外键
class:对应TeacherVo类的完全限定名
-->
<many-to-many column="t_id" class="TeacherVo"></many-to-many>
</set>
</class>
</hibernate-mapping>
将元数据加载到主配置文件
<mapping resource="com/xixw/Vo/StudentVo.hbm.xml"/>
<mapping resource="com/xixw/Vo/TeacherVo.hbm.xml"/>
⑤ 测试
package com.xixw.test;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import com.xixw.vo.StudentVo;
import com.xixw.vo.TeacherVo;
public class ManyToManyTest {
public static void main(String[] args) {
//1.创建Configuration对象
Configuration cfg=new Configuration();
//2.加载主配置文件
cfg.configure();
//3.获取SessionFactory工厂
SessionFactory factory=cfg.buildSessionFactory();
//4.获取session对象
Session session=factory.openSession();
//Transaction tr=session.beginTransaction();
//保存对象
/*StudentVo stu1=new StudentVo();
stu1.setName("李四");
TeacherVo tea1=new TeacherVo();
tea1.setName("小明1");
TeacherVo tea2=new TeacherVo();
tea2.setName("小红1");
Set<TeacherVo> teaSet=new HashSet<TeacherVo>();
teaSet.add(tea1);
teaSet.add(tea2);
stu1.setTeaSet(teaSet);*/
/*Set<StudentVo> stuSet1=new HashSet<StudentVo>();
stuSet1.add(stu1);
tea1.setStuSet(stuSet1);
Set<StudentVo> stuSet2=new HashSet<StudentVo>();
stuSet2.add(stu1);
tea2.setStuSet(stuSet2);*/
StudentVo stu=(StudentVo) session.get(StudentVo.class, 1);
/*解决: org.hibernate.LazyInitializationException
* 1.使用hibernate预加载功能,将关联的对象事先加载完成
Hibernate.initialize(stu.getTeaSet());
* 2.将对象延时加载的功能关闭:lazy="false",(默认是开启的)
关闭会影响程序的性能
* 3.修改抓取策略为join:fetch=”join”
Join:使用外连接的形式进行查询
Select:使用普通查询,会报懒加载异常
subSelect:使用子查询的方式进行查询
* */
// Hibernate.initialize(stu.getTeaSet());
session.close();
Set<TeacherVo> set=stu.getTeaSet();
for (TeacherVo teacherVo : set) {
System.out.println(teacherVo.getName());
}
/*session.save(tea1);
session.save(tea2);
session.save(stu1);
*/
//tr.commit();
//session.close();
}
}
总结
Inverse(设置维护关系) | 一般在多对一的时候让一方放弃维护,由多方去维护他们之间的关系 true:设置放弃维护关系 false:(默认)设置不放弃维护关系 |
---|---|
Cascade(设置级联操作) | delete:在删除的时候进行级联操作。save-update:在保存或者更新的时候进行级联操作。all:在所有的情况下都会进行级联操作。none:(默认)不进行级联操作 |
Lazy(设置延时加载) | 注意:在使用延时加载的时候要注意避免懒加载异常 |
Fetch(设置抓取策略) | join:使用外连接进行查询;select:使用普通select进行查询;subselect:使用子查询进行查询 |
order-by:进行排序,值是需要排序的属性名 | 注意:只能定义在set集合中,然后对set集合中的对象进行排序 |
下一篇:Hibernate注解
Hibernate注解