Hibernate中为什么要使用双向一对多,以及关系详解(配置文件)

本文探讨了在Hibernate中使用双向一对多关系的原因,主要是为了提高执行效率。详细解释了一对多、多对一关系的原理,并介绍了如何配置双向一对多关联。通过示例代码展示了配置文件的细节,包括Department和Employee实体类及其映射文件。最后提供了测试代码,强调保存“一”的一方以提高效率,并提供资源链接供读者下载实践。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    在我们的日常生活中,用到最多的就是双向一对多的关系,最普遍的映射关系是一对多的关系,比如部门和职工,这是从部门的角度来说。从职工的角度来说,就是多对一的关系,职工和部门

    但一对多和多对一的执行效率太慢了,所以使用双向一对多的原因就是:提高执行效率。

  • 多对一的运行原理:在多的一端加入一个外键指向一的一端,它维护的关系多指向一的一方,不需要维护关系,多的一方,需要维护双方关系,所以里面配置有一的一方的引用。
  • 单向一对多的运行原理:实体之间关系由”一”的一端加载”多”的一端,关系由”一”的一端来维护。也就是说在”一”的一端持有”多”的一端的集合我们在加载”一”的时候把”多”也加载进来了,也就是在部门中维护职工的集合。(我们可以根据部门找到属于这些部门的所有职工。但是不能根据职工找到它所属的部门)。
  • 双向一对多:所谓的双向一对多关联,同时配置单向多对一和单向一对多就可以构成双向一对多关联关系。它在解决单向一对多关系存在的缺陷起到了一定的修补作用,提高效率

我已经把我的项目上传了大家可以下载看看https://download.youkuaiyun.com/download/amazing_banana/11014465

现在我们来进行双向一对多的代码测试:

首先我们要有jia包:

jar包大家就自己找一下

  1. 接下来我们配置hibernate.cfg.xml,在src下创建一个folder,名字为config

1.接下来为大家展示代码:

<?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 元素用于配置Hibernate中的属性
            键:值 
          -->
          <!-- hibernate.connection.driver_class : 连接数据库的驱动  -->
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>


          <!-- hibernate.connection.username : 连接数据库的用户名 -->
        <property name="hibernate.connection.username">root</property>


          <!-- hibernate.connection.password : 连接数据库的密码 -->
        <property name="hibernate.connection.password">root</property>


          <!-- hibernate.connection.url : 连接数据库的地址,路径 -->
        <property name="hibernate.connection.url">
        jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8
        </property>


        <!-- show_sql: 操作数据库时,会 向控制台打印sql语句 -->
        <property name="show_sql">true</property>

        <!-- format_sql: 打印sql语句前,会将sql语句先格式化  -->
        <property name="format_sql">true</property>

        <!-- hbm2ddl.auto: 生成表结构的策略配置
             update(最常用的取值): 如果当前数据库中不存在表结构,那么自动创建表结构. 
                     如果存在表结构,并且表结构与实体一致,那么不做修改
                     如果存在表结构,并且表结构与实体不一致,那么会修改表结构.会保留原有列.
             create(很少):无论是否存在表结构.每次启动Hibernate都会重新创建表结构.(数据会丢失)
             create-drop(极少): 无论是否存在表结构.每次启动Hibernate都会重新创建表结构.每次Hibernate运行结束时,删除表结构.
             validate(很少):不会自动创建表结构.也不会自动维护表结构.Hibernate只校验表结构. 如果表结构不一致将会抛出异常.
          -->
        <property name="hbm2ddl.auto">update</property>

        <!-- 数据库方言配置 
         org.hibernate.dialect.MySQLDialect (选择最短的)
         -->
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>

        <!-- hibernate.connection.autocommit: 事务自动提交  -->
        <property name="hibernate.connection.autocommit">true</property>

        <!-- 将Session与线程绑定=> 只有配置了该配置,才能使用getCurrentSession -->
        <property name="hibernate.current_session_context_class">thread</property>


        <!-- 引入ORM 映射文件 
            填写src之后的路径
         -->
        <mapping resource="com/hp/bean/Department.hbm.xml"/>
        <mapping resource="com/hp/bean/Employee.hbm.xml"/>
        
    </session-factory>
</hibernate-configuration>

代码里面都有详细的注解

2.接着建两个实体类 Department.java(部门),Employee.java(员工)

package com.hp.bean;

import java.util.HashSet;
import java.util.Set;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

/**
 * 部门
 * @author Administrator
 *
 */
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Department {
	private Integer did;
	private String departname;
	private Set<Employee> employeeSet = new HashSet<Employee>();

}
package com.hp.bean;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

/**
 * 员工
 * @author Administrator
 *
 */
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Employee {
	 private Integer eid;
	 private String ename;
	 private String esex;
	 private Department department;

}

里面的get,set方法用到了这个lombok这个jar包个人觉得比较方便,大家可以下载试试。

3.创建Department.java 的映射文件Department.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.hp.bean.Department" table="department">
	        <id name="did" column="did">
            <!-- 设置成increment之后,会从数据库中查找主键id最大的值,然后+1进行存储 -->
            <generator class="increment"></generator>
	        </id>
	        <property name="departname" column="departname"></property>      
        <!-- 
            inverse:默认为false, 指关联关系的控制权由自己来维护
            inverse设置为true的时候,就说明关联在多的那一方被维护 (一般设置在多的一方去维护,效率会跟高一点)
        -->
	        <set name="employeeSet" inverse="true" cascade="all" lazy="false">
	            <key column="did"></key>
	            <one-to-many class="com.hp.bean.Employee"/>
        	</set>
	        
    	</class>
	</hibernate-mapping>

	

这里面需要特别注意inverse和lazy.  inverse默认为false,意思是关联关系由自己维护。如果为true的话,就是把控制权交出去由多的一方去维护,效率会更高一些,只能在set里面设置inverse属性。lazy的意思是延迟,为false就是不延迟就是快。

4.创建Employee.java 的映射文件Employee.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.hp.bean.Employee" table="employee">
	        <!-- 设置主键 -->
	        <id name="eid" column="eid">
	            <!-- 设置为native之后,主键会按照本来的顺序进行增长(比如之前23但是删除了,现在就从24开始) -->
	            <generator class="native"></generator>
	        </id>
	
	        <!-- 设置属性 -->
	        <property name="ename" column="ename"></property>
	        <property name="esex" column="esex"></property>
	        <many-to-one name="department"  cascade="all" column="did" 
	            class="com.hp.bean.Department"></many-to-one>
	       
	    </class>
	</hibernate-mapping>

many-to-one的常用属性:

name:映射类属性的名称(必须)

class:关联类的完全限定名

column:关联的字段

not-null:设置关联的字段的值是否可以为空。默认值false

lazy:指定关联对象是否使用延迟加载以及延迟加载的策略。lazy属性有三个取值:false、proxy、no-proxy。默认值proxy

fetch:设置抓取数据的策略。默认值是select

many-to-one没有inverse属性,因为关系的维护是多的一方,不可能放弃对关系的维护。

5.创建个工具类SessionFactoryUtils.java,用来创建Session工厂,测试代码会用到这个工具类

package com.hp.util;

import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class SessionFactoryUtils {
	private SessionFactory factory;
	private static SessionFactoryUtils factoryUtils;
	
	 // 单例模式:把构造方法设置为私有的,说明不可以new这个类的实例。
	private SessionFactoryUtils() {
		
	}
	 // 通过定义这个类的静态方法,并且返回类型与这个类的类型一样,来实现对这个类的访问
	public static SessionFactoryUtils getInstance() {
		if(factoryUtils == null) {
			factoryUtils = new SessionFactoryUtils();
		}
		
		return factoryUtils;
	}
	
	public SessionFactory openSessionFactory() {
		if(factory ==null) {
			//加载主配置文件
			Configuration configuration = new Configuration().configure();
			//建立工厂
			factory = configuration.buildSessionFactory();
		}
		return factory;
	}

}

6.测试代码:

package com.hp.test;

import org.hibernate.HibernateException;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.classic.Session;
import org.junit.Before;
import org.junit.Test;

import com.hp.bean.Department;
import com.hp.bean.Employee;
import com.hp.util.SessionFactoryUtils;

public class OneToManyTest {
	private SessionFactory factory;
	
	@Before
	public void init() {
		//调用前面的工具类来打开工厂
		factory = SessionFactoryUtils.getInstance().openSessionFactory();
	}
	
	@Test
	public void add() {
		//打开Session
		Session session = factory.openSession();
		//打开事务
		Transaction tx = session.beginTransaction();
		
		
		Department d = new Department();
		d.setDepartname("财务部");
		
		
		Employee e1 = new Employee();
		e1.setEname("硕硕");
		e1.setEsex("女");
		
		Employee e2 = new Employee();
		e2.setEname("海燕");
		e2.setEsex("女");
		
		
		d.getEmployeeSet().add(e1);
		d.getEmployeeSet().add(e2);
		
		
		try {
			session.save(d);
			tx.commit();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			tx.rollback();
			e.printStackTrace();
		}
		
	}
}

因为是双向操作,所以保存任何一方都能成功,但是推荐保存”一”的一方,并在”一”的一方交出控制权,这样会提高效。hibernate仅仅会格局主控方对象的状态来同步更新数据库。

如果操作成功的话数据库中会有两个这样的表和表里面的值

 

 

这样双向一对多的简单测试就完成了,大家赶快试试吧!

我的资源包里面已经上传了完整的项目,大家可以下载试试看!

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值