Hibernate:一对多级联操作

MySQL数据库中char和vchar区别

比如存储"name"字符串

char(5):['n','a','m','e',''];

vchar(5):['n','a','m','e'];可变长度

数据库中表关系:一对一;一对多、多对一;多对多

如何实现数据库中的表关系?

一对多的表关系在数据库中如何实现?使用外键约束。我们习惯把一的一方称为主表,把多的一方称为从表。

什么是外键?从表中有一列,该列的取值除了null之外,只能来源于主表的主键。(默认情况下,外键字段的值是可以重复的)

多对多的表关系在数据库中如何实现?使用中间表。

中间表中只有两个外键,引用两个多对多表的主键,不能有其他字段信息,至于中间表的主键,应该采用联合主键。

任何一个多方的表和中间表去比较,都是一对多的关系。

一对一的表关系在数据库中如何实现?有两种:

第一种,建立外键。使用外键约束,唯一约束、非空约束,从而实现了一对一。

第二种,使用主键。让其中一张表既是主键,又是外键。

如何确立数据库中的两张表关系?找外键

学习多表映射配置要遵循的步骤

第一步:确立两张表之间的关系

第二步:在数据库中实现两张表之间的关系建立,一是主表,多是从表,在从表配置外键

第三步:在实体类中描述出两个实体之间的关系,主表的实体类应该包含从表实体类的集合引用,从表的实体类应该包含主表实体类的对象引用(构造方法和toString方法不要写关联表的集合或对象的引用)

第四步:在映射配置文件中建立两个实体和两张表之间的关系

Cla.java

private Integer claId;
private String claName;
private Set<Stu> stu = new HashSet<Stu>(0);

Cla.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="org.haiwen.entity.Cla" table="cla">
		<id name="claId" column="cla_id" >
			<generator class="native" />
		</id>
		<property name="claName" type="java.lang.String" column="cla_name" />
		<!-- 一对多关系映射:主表实体的映射配置
              标签:set
			作用:用于配置set集合属性
			属性:name,指定实体类中引用从表set集合的属性名称.
                       table,指定从表的名称,在一对多配置时可以不写.
              标签:key
			作用:用于映射外键关系
			属性:column,指定外键字段名称
			标签:one-to-maney
			作用:用于建立一对多的映射配置
			属性:class,用于指定从表实体的名称 -->
		<set name="stu" table="stu" inverse="true" cascade="save-update">
			<key column="cla_id"></key>
			<one-to-many class="org.haiwen.entity.Stu"/>
		</set>
	</class>
</hibernate-mapping>

Stu.java

private Integer stuId;
private String stuName;
private Cla cla;

Stu.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="org.haiwen.entity.Stu" table="stu">
		<id name="stuId" column="stu_id" >
			<generator class="native" />
		</id>
		<property name="stuName" type="java.lang.String" column="stu_name" />
		<!-- 多对一关系映射:从表实体的映射配置
                标签:many-to-one
			作用:建立多对一的映射
			属性:name,从表实体中引用主表实体对象引用的名称
                        class,指定属性所对应的实体类名称
                        column,指定从表中外键字段的名称 -->
		<many-to-one name="cla" class="org.haiwen.entity.Cla" column="cla_id"></many-to-one>
	</class>
</hibernate-mapping>

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="connection.driver_class">com.mysql.jdbc.Driver</property>
		<property name="connection.url">jdbc:mysql://localhost:3306/hibernate</property>
		<property name="connection.username">root</property>
		<property name="connection.password">root</property>

		<property name="show_sql">true</property>
		<property name="format_sql">true</property>

		<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
		<property name="hibernate.hbm2dll.auto">update</property>
		<property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
		<property name="hibernate.current_session_context_class">thread</property>

		<mapping resource="org/haiwen/entity/Cla.hbm.xml" />
		<mapping resource="org/haiwen/entity/Stu.hbm.xml" />
	</session-factory>
</hibernate-configuration>

HibernateUtils.java

public class HibernateUtils {
	static Configuration cfg;
	static SessionFactory factory = null;
	static {
		try {
			cfg = new Configuration();
			cfg.configure();
			factory = cfg.buildSessionFactory();
		} catch (ExceptionInInitializerError e) {
			throw new ExceptionInInitializerError("初始化SessionFactory失败,请检查配置文件");
		}
	}

	public static Session getCurrentSession() {
		return factory.getCurrentSession();// 每次使用该方法从当前线程上获取一个session,只有配置了session和线程绑定之后才能使用此方法,否则返回null
	}
}

一对多保存

/**
  *创建一个班级和一个学生,建立班级和学生的双向关联关系
  *使用符合原则的保存:先保存主表实体,再保存从表实体
  *此时保存会有问题:我们保存两个实体,应该只有两条insert语句,执行结果却是多了一条update语句
  *解决方法:在班级执行操作的时候,放弃维护关联关系的权利
  *配置的方式:在Cla的映射配置文件中的set标签上使用inverse属性
  *inverse含义:是否放弃维护关联关系的权利
  *true:放弃
  *false:不放弃(默认值)
 */
public void save() {
	// 1.创建一个班级
	Cla cla = new Cla();
	cla.setClaName("武将");
	// 2.创建一个学生
	Stu stu = new Stu();
	stu.setStuName("关羽");
	// 3.建立班级和学生的关联关系(双向)
	stu.setCla(cla);
	cla.getStu().add(stu);
	Session session = HibernateUtils.getCurrentSession();
	session.getTransaction().begin();
	// 4.保存规则,先保存主表实体,在保存从表实体
	session.save(cla);
	session.save(stu);
	session.getTransaction().commit();
}

级联保存

/**
 * 级联保存:只写一个方法同时保存主表实体和从表实体
 * 配置方式:在Cla的映射配置文件中的set标签上使用cascade属性,此时使用session.save(c);
 * 也可以写在Stu的映射配置文件中的many-to-one标签上,此时使用session.save(s); 
 * cascade属性:配置级联操作
 * save-update:级联保存更新
 */
public void save() {
	Cla cla = new Cla();
	cla.setClaName("武将");
	Stu stu = new Stu();
	stu.setStuName("关羽");
	stu.setCla(cla);
	cla.getStu().add(stu);
	Session session = HibernateUtils.getCurrentSession();
	session.getTransaction().begin();
	session.save(cla);
	session.getTransaction().commit();
}

级联更新

/**
 * 同时做两件事,先将stu实体保存,再更新cla数据库
 */
public void update() {
	Session session = HibernateUtils.getCurrentSession();
	session.getTransaction().begin();
	Cla cla = (Cla) session.get(Cla.class, 1);
	cla.setClaName("武将");
	Stu stu = new Stu();
	stu.setStuName("张飞");
	stu.setCla(cla);
	cla.getStu().add(stu);
	session.update(cla);
	session.getTransaction().commit();
}

级联删除

/**
 * 删除主表时,同时删除关联从表
 * 当cascade="delete"属性设置在主表时
 * 	删除从表数据就是单表删除
 * 	删除主表数据时,判断
 * 		没有从表数据引用,单表删除
 * 		有从表数据引用,hibernate会把从表中的外键设置为null,然后删除从表数据,再删除主表数据
 * 		如果外键字段有非空约束,则hibernate不能跟新外键字段为null,会报错
 * 	当cascade="delete"属性设置在从表时,反之亦然,可同时设置cascade="save-update,delete"
 * 级联删除要慎用
 */
public void delete() {
	Session session = HibernateUtils.getCurrentSession();
	session.getTransaction().begin();
	Cla cla = (Cla) session.get(Cla.class, 1);
	session.delete(cla);
	session.getTransaction().commit();
}

对象导航查询

一对多

/**
 * 一对多,根据一的一方查询多的一方时,需要使用延迟加载(默认配置即可)
 */
public void oneToMany() {
	Session session = HibernateUtils.getCurrentSession();
	session.getTransaction().begin();
	Cla cla = (Cla) session.get(Cla.class, 1);
	System.out.println(cla.getStu());
	session.getTransaction().commit();
}

多对一

/**
 * 多对一,根据多的一方查询一的一方时,不需要使用延迟加载,而是使用立即加载,需要配置
 * 配置:从表的映射配置文件,在many-to-one标签上使用lazy属性.
 *	取值:false,使用立即加载
 *        proxy,看load方法是延迟加载还是立即加载
 */
public void manyToOne() {
	Session session = HibernateUtils.getCurrentSession();
	session.getTransaction().begin();
	Stu stu = (Stu) session.get(Stu.class, 1);
	System.out.println(stu.getCla());
	session.getTransaction().commit();
}

class标签的lazy:负责load方法是否是延迟加载。

set标签的lazy:负责查询关联的集合对象是否是延迟加载。

many-to-one的lazy:负责查询关联的主表实体是否是立即加载。

<many-to-one> 属性fetch:抓取策略,表示以什么方式进行关联关系对象的加载

  • select:以单独一条select语句查询关联关系对象,支持lazy
  • join:以连接查询的方式查询关联关系对象,不支持lazy

<many-to-one> 属性lazy:关联级别的延迟加载

  • false:表示不延迟
  • proxy:表示延迟加载(关联级别最好使用延迟加载 )

<set> 属性inverse: 表示是否放弃对于关联关系的控制权

  • true:放弃控制权
  • false:不放弃控制权

<set> 属性cascade:级联策略,表示当操作当前对象时,以什么方式级联操作关联关系对象

  • save:当保存当前对象时,级联保存关联关系对象
  • save-update:当保存或更新当前对象时,级联保存或更新关联关系对象
  • delete:当删除先前对象时,级联删除关联关系对象
  • all:当操作当前对象时,级联操作关联关系对象

Hibernate关联关系映射

  1. 不管关系多复杂,在数据库端只有一个字段:外键
  2. 不管关系多复杂,在java程序中无非就是一个对象和一个集合的事
  • XXX-to-one,只要关联一个关系对象,在本类中就是一个对象
  • XXX-to-many,只要关联多个关系对象,在本类中就是一个集合
发布了202 篇原创文章 · 获赞 37 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/lovecuidong/article/details/99681118