引入:
(借用了他人的例子)首先应该清楚多对一和一对多只是站在不同的角度看待问题,其本质是一样的。在思考这个问题的时候,不要把这两个概念混在一起,这样不容易理解,而要分开,站在不同的角度去解决同一个问题。
在数据库中,可以通过添加外主键的关联,表现一对多的关系
通过一方持有多方的集合实现,即在“一”的一端中使用元素表示持有“多”的一端的对象
就拿员工和部门的例子来说,我们站在不同的角度,可能会遇到如下的几种情况:
站在员工的角度看,是多对一的关系:
1、来了新员工,但是还不知道该分配到哪个部门,只有先把员工保存到员工表中,部门那一列设为空,待以后再更新其部门信息
2、来了新员工,并且确定了该员工分配到哪个部门,则将员工的完整信息保存到员工表中
站在部门的角度看,是一对多的关系:
1、成立了新部门,要向其中添加一些新员工
2、要撤销一个部门,这时,这个部门中的员工的部门信息,全部置为空
此外应注意以下几个问题:
1、若是保存员工,说明表中还没有这个员工的id,在保存的时候,设置其id属性的时候,就不要设置,用默认的就可以,这样系统会自动给这个员工分配一个id的
2、若是更新某个员工,说明员工表中已经有了这个员工的id,在更新的时候,一定要指明其id,才可以找到这个员工
3、无论是站在部门还是站在员工的角度,即无论是一对多还是多对一的关系,映射到数据库中的表的关系都是一样的,就相当于是两个表的关系的两种实现方式,但最终的结果是一样的。
下面,我们来看看如何具体的去实现上面提到的几种情况:
单向一对多关联关系
首先配置hibernate的基本环境:
1、创建hibernate.cfg.xml文件:
<?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.password">123456</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate?useUnicode=true&characterEncoding=UTF-8</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<!--
1、 分别为在控制台是否输出sql语句
2、控制台是否格式化输出sql语句
-->
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<property name="hbm2ddl.auto">update</property>
</session-factory>
</hibernate-configuration>
2、创建HibernateUtil工具类
package com.util;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
public class HibernateUtil {
private static Session session;
private static SessionFactory sessionFactory;
static{
//创建Configuration对象,读取Hibernate.cfg.xml文件,完成初始化
Configuration configuration=new Configuration().configure();
StandardServiceRegistryBuilder ssrd=new StandardServiceRegistryBuilder().applySettings(configuration.getProperties());
StandardServiceRegistry ssr=ssrd.build();
sessionFactory=configuration.buildSessionFactory(ssr);
}
//获取SessionFactory
public static SessionFactory GetSessionFactory(){
return sessionFactory;
}
//获取Session
public static Session GetSession(){
session=sessionFactory.openSession();
return session;
}
//关闭Session
public static void CloseSession(){
if(session!=null){
session.close();
}
}
}
建立两实体个类:Department和Employee
Employee类:
package com.entity;
public class Employee {
private int Eid; //员工的id
private String name;//员工的姓名
private String sex;//员工的性别
/**
* 1、属性一定要是私有的
* 2、public属性的无参构造方法
* 3、Get和Set方法
* 4、有参的构造方法
*/
public Employee(){
}
public Employee(String name, String sex) {
super();
this.name = name;
this.sex = sex;
}
public Employee(int eid, String name, String sex) {
super();
Eid = eid;
this.name = name;
this.sex = sex;
}
public int getEid() {
return Eid;
}
public void setEid(int eid) {
Eid = eid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
Department 类:
package com.entity;
import java.util.HashSet;
import java.util.Set;
public class Department {
private int Did;//部门id
private String Dname;//部门姓名
private Set<Employee> employees=new HashSet<Employee>();//员工集合
public Department() {
super();
}
public Department(int did, String dname, Set<Employee> employees) {
super();
Did = did;
Dname = dname;
this.employees = employees;
}
public int getDid() {
return Did;
}
public void setDid(int did) {
Did = did;
}
public String getDname() {
return Dname;
}
public void setDname(String dname) {
Dname = dname;
}
public Set<Employee> getEmployees() {
return employees;
}
public void setEmployees(Set<Employee> employees) {
this.employees = employees;
}
}
生成两个实体类的映射文件:
Department对应的hbm.xml映射文件:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2016-8-3 13:27:41 by Hibernate Tools 3.5.0.Final -->
<hibernate-mapping>
<class name="com.entity.Department" table="DEPARTMENT">
<id name="Did" type="java.lang.Integer">
<column name="DID" />
<generator class="increment" />
</id>
<property name="Dname" type="java.lang.String">
<column name="DNAME" />
</property>
<!-- 配置单向的一对多的关联关系 -->
<set name="employees" table="EMPLOYEE" >
<key>
<column name="DID" />
</key>
<one-to-many class="com.entity.Employee" />
</set>
</class>
</hibernate-mapping>
Set元素常用的属性:
1、name:映射类属性的名称
2、table:关联类的目标数据库
3、lazy:指关联对象是否使用延迟加载 默认:proxy
4、inverse:表示双向关联中被动的一方 默认:false
Employee实体类对象的hbm.xml映射文件内容:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2016-8-3 14:17:01 by Hibernate Tools 3.5.0.Final -->
<hibernate-mapping>
<class name="com.entity.Employee" table="EMPLOYEE">
<id name="Eid" type="int">
<column name="EID" />
<!-- increment 类型可以先查询数据库中对应的最大的,然后再在原来的基础上加一 -->
<generator class="increment" />
</id>
<property name="name" type="java.lang.String">
<column name="NAME" />
</property>
<property name="sex" type="java.lang.String">
<column name="SEX" />
</property>
</class>
</hibernate-mapping>
最后在配置完两个hbm文件以后,在hibernate核心配置文件当中指定映射文件的路径:
SessionFactory-mapping 下面:
<!-- 需要填写对应的物理地址 -->
<mapping resource="com/entity/Employee.hbm.xml"/>
<mapping resource="com/entity/Department.hbm.xml"/>
【最后】完成以上步骤就可以在test.java中进行测试:
一、单向的一对多关联关系
1、将员工信息存入数据库
//将员工添加到部门中
public static void save(){
Session session=HibernateUtil.GetSession();
Transaction transaction=session.beginTransaction();
Department department=new Department("技术部");
Employee employee=new Employee("张三","男");
Employee employee2=new Employee("李四","女");
department.getEmployees().add(employee);
department.getEmployees().add(employee2);
System.out.println(department.toString());
session.save(department);
session.save(employee);
session.save(employee2);
transaction.commit();
HibernateUtil.CloseSession(session);
}
数据库显示的结果:
2、根据单向的一对多的关系查询部门中包含的员工
//查询部门中中包含的员工信息
public static void FindEmployee(){
Session session=HibernateUtil.GetSession();
Department department=(Department) session.get(Department.class, 1);
System.out.println("Department:"+department.getDname());
Set<Employee> employees=department.getEmployees();
for (Employee employee : employees) {
System.out.println("EmployeeName:"+employee.getName()+" EmployeeSex:"+employee.getSex());
}
}
控制台输出结果:
PS:建立关联关系以后,可以方便的从一个对象导航到另外一个对象
3、修改学生信息
//修改学生信息
public static void update(){
Department department=new Department("公关部");
Session session=HibernateUtil.GetSession();
Transaction tx=session.beginTransaction();//由于需要进行更新的操作,所以需要进行的事务的开启和提交
Employee employee=(Employee) session.get(Employee.class, 1);//获取ID为1的学生对象
department.getEmployees().add(employee);
session.save(department);
tx.commit();
HibernateUtil.CloseSession(session);
}
控制台输出:
4、删除学生信息(session.delete( )方法)
//删除学生信息
public static void delete(){
Session session=HibernateUtil.GetSession();
Transaction tx=session.beginTransaction();
Employee employee=(Employee) session.get(Employee.class, 2);//获取员工id为2的员工
session.delete(employee);
tx.commit();
HibernateUtil.CloseSession(session);
}
工程源码地址以及所需要的jar包:工程源码
单向多对一关联关系
- 多对一的关系和关系数据库中的外键参照关系最匹配,即在已方的表中的一个外键参照另外一个表的主键
- 通过在多方持有一方的引用实现,需要在“多”的一端使用 配置
1、在多方的定义一个一方的引用:
在原有代码的基础之上修改Employee实体类添加一个多方定义一个一方的引用。
package com.entity;
public class Employee {
private int Eid; //员工的id
private String name;//员工的姓名
private String sex;//员工的性别
//在多方定义一个一方的引用
private Department department;
/**
* 1、属性一定要是私有的
* 2、public属性的无参构造方法
* 3、Get和Set方法
* 4、有参的构造方法
*/
public Employee(){
}
public Employee(String name, String sex) {
super();
this.name = name;
this.sex = sex;
}
public Employee(int eid, String name, String sex) {
super();
Eid = eid;
this.name = name;
this.sex = sex;
}
public int getEid() {
return Eid;
}
public void setEid(int eid) {
Eid = eid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = department;
}
}
对应的hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2016-8-3 14:17:01 by Hibernate Tools 3.5.0.Final -->
<hibernate-mapping>
<class name="com.entity.Employee" table="EMPLOYEE">
<id name="Eid" type="int">
<column name="EID" />
<!-- increment 类型可以先查询数据库中对应的最大的,然后再在原来的基础上加一 -->
<generator class="increment" />
</id>
<property name="name" type="java.lang.String">
<column name="NAME" />
</property>
<property name="sex" type="java.lang.String">
<column name="SEX" />
</property>
<!-- 配置多对一关联关系 -->
<many-to-one name="Department" class="com.entity.Department" column="DID"></many-to-one>
</class>
</hibernate-mapping>
在test下进行测试:
public static void save(){
Department department =new Department("技术部");
Employee employee=new Employee("张三","男");
Employee employee2=new Employee("李四","女");
//设置关联关系(多对一)
employee.setDepartment(department);
employee.setDepartment(department);
Session session=HibernateUtil.GetSession();
Transaction tx=session.beginTransaction();
session.save(department);
session.save(employee);
session.save(employee2);
tx.commit();
HibernateUtil.CloseSession(session);
}
控制台输出结果:
可以通过对一对多以及多对一的了解,在此基础上建立一个双向多对一关联关系。
双向多对一在测试类中增加关联关系。
public static void save(){
Department department =new Department("技术部");
Employee employee=new Employee("张三","男");
Employee employee2=new Employee("李四","女");
//设置关联关系(多对一及一对多)
department.getEmployees().add(employee);
department.getEmployees().add(employee2);
employee.setDepartment(department);
employee.setDepartment(department);
Session session=HibernateUtil.GetSession();
Transaction tx=session.beginTransaction();
session.save(department);
session.save(employee);
session.save(employee2);
tx.commit();
HibernateUtil.CloseSession(session);
}
控制台输出的结果如下图:
可以发现其中第四、第五指令为多余的,因为学生知道自己所处的班级,多余的代码对程序的性能有一定的影响,故:我们可以通过inverse属性进行设置。
Inverse属性:
- inerse表示反转,这个属性可以指定关联关系的方向。
set节点的inverse属性指定关联关系的控制方向,默认由one一方来维护
关联关系中,inverse=“false”则为主动方,由主动方负责维护关联关系。
- 在一对多关联中,只能设置one方的inverse为true,这将有助于性能的改善。
具体实施:
找到one方,在one方配置inverse属性,我们将属性该为true
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2016-8-3 13:27:41 by Hibernate Tools 3.5.0.Final -->
<hibernate-mapping>
<class name="com.entity.Department" table="DEPARTMENT">
<id name="Did" type="java.lang.Integer">
<column name="DID" />
<generator class="increment" />
</id>
<property name="Dname" type="java.lang.String">
<column name="DNAME" />
</property>
<!-- 配置单向的一对多的关联关系 设置inverse属性为true,由多方维护关联关系 -->
<set name="employees" table="EMPLOYEE" inverse="true">
<key>
<column name="DID" />
</key>
<one-to-many class="com.entity.Employee" />
</set>
</class>
</hibernate-mapping>
再次测试test,控制台输出:
原来的update语句已经没有了,这样子提高了系统的性能。
级联操作:
如果设置了级联操作,我们就可以不需要现示的保存多方,只需要保存one方就可以存入数据库。