hibernate的关联与级联

hibernate的关联与级联hibernate的关联与级联

大家好,又见面了,我是你们的朋友全栈君。

什么是关联(association)

1、关联指的是类之间的引用关系。如果类A与类B关联,那么被引用的类B将被定义为类A的属性。
2、关联的分类:关联可以分为一对一、一对多/多对一、多对多关联

关联是有方向的

关联的关键点都在外键上

如何建立一对多双向关联

以订单和订单项做案例
一个订单对多个订单项,多个订单项对一个订单
在订单实体类中需要添加两个属性 : Set<OrderItem> orderItems

initOrderItems = 0;//0代表懒加载	1代表立即加载

在订单项的实体类中需要添加一个属性:Order order

定义一对多的关系时需要采用接口方式

1、在Order.hbm.xml中需要添加(建立订单对订单项的一对多的关系)

		<!-- 
			cascade:用来配置维护实体类之间的关系所用
			inverse:关系交由反方控制(由OrderItem控制)
		 -->
		<set name="orderItems" cascade="save-update" inverse="true">
			<!-- 填外键 -->
			<key column="oid"></key>
			<one-to-many class="com.zking.four.entity.OrderItem"/>
		</set>

2、在OrderItem.hbm.xml中需要添加(建立订单项对订单多对一的关系)

<property name="oid" type="java.lang.Integer" column="oid" insert="false" update="false"></property>

<!-- 会报错 -->
		<many-to-one name="order" class="com.zking.four.entity.Order" column="oid"></many-to-one>

三、级联新增、级联查询,普通删除
pom.xml

 <dependencies>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
            <version>2.1.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>5.3.7.Final</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.querydsl/querydsl-jpa -->
        <dependency>
            <groupId>com.querydsl</groupId>
            <artifactId>querydsl-jpa</artifactId>
            <version>4.1.4</version>
        </dependency>

    </dependencies>

方法:

package com.four.dao;
import java.util.List;
import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.Transaction;
import com.four.entity.Order;
import com.four.entity.OrderItem;
import com.two.util.SessionFactoryUtil;
public class OrderDao {
	//订单项新增
	public Integer addOrderItem(OrderItem orderItem) {
		Session session = SessionFactoryUtil.getSession();
		Transaction transaction = session.beginTransaction();
		Integer otid = (Integer) session.save(orderItem);
		transaction.commit();
		session.close();
		return otid;
	}
	
	//订单新增
	public Integer addOrder(Order order) {
		Session session = SessionFactoryUtil.getSession();
		Transaction transaction = session.beginTransaction();
		Integer otid = (Integer) session.save(order);
		transaction.commit();
		session.close();
		return otid;
	}
	
	//查单个
	public Order getOrder(Order order) {
		Session session = SessionFactoryUtil.getSession();
		Transaction transaction = session.beginTransaction();
		Order o = session.get(Order.class, order.getOrderId());
		if(o != null && new Integer(1).equals(order.getInitOrderItems())) {
			Hibernate.initialize(o.getOrderItems());
		}
		transaction.commit();
		session.close();
		return o;
	}
	
	//查所有
	public List<Order> getOrderList(){
		Session session = SessionFactoryUtil.getSession();
		Transaction transaction = session.beginTransaction();
		List list = session.createQuery("from Order").list();
		transaction.commit();
		session.close();
		return list;
	}
	
	//删除(hibernate需要先删从表(订单项)再删主表(订单))
	public void delOrder(Order order) {
		Session session = SessionFactoryUtil.getSession();
		Transaction transaction = session.beginTransaction();
		Order o = session.get(Order.class, order.getOrderId());
		for (OrderItem ot : o.getOrderItems()) {
			session.delete(ot);
		}
		session.delete(o);
		transaction.commit();
		session.close();
	}
}

JUnit测试

package com.four.dao;

import java.util.Iterator;
import java.util.List;

import org.junit.Test;

import com.four.entity.Order;
import com.four.entity.OrderItem;

public class OrderDaoTest {
	
	private OrderDao orderDao = new OrderDao();
	
	/**
	 * Repeated column in mapping for entity: 
	 * com.zking.four.entity.OrderItem column: oid 
	 * (should be mapped with insert="false" update="false")
	 * 同一个字段被映射了两次
	 */

	@Test
	public void testAddOrderItem() {//订单项新增
		OrderItem orderItem = new OrderItem();
//		orderItem.setOid(7);//普通新增
		orderItem.setProductId(89);
		orderItem.setQuantity(78);
		Order order = new Order();
		order.setOrderId(5);
		orderItem.setOrder(order);
		this.orderDao.addOrderItem(orderItem);
	}
	
	//提交一个具有6个订单项的订单
//	addOrder		1
//	addOrderItem 	6
	
	@Test
	public void testAddOrder() {//订单新增
		Order order = new Order();
		order.setOrderNo("P8");
		OrderItem orderItem;
		for (int i = 1; i < 7; i++) {
			orderItem = new OrderItem();
			orderItem.setProductId(i);
			orderItem.setQuantity(i);
			
			order.getOrderItems().add(orderItem);
			orderItem.setOrder(order);
		}
		this.orderDao.addOrder(order);
	}
	
	@Test
	public void testGetOrder() {//查单个
		Order order = new Order();
		order.setOrderId(8);
		order.setInitOrderItems(1);
		Order o = this.orderDao.getOrder(order);
		System.out.println(o.getOrderNo());
		System.out.println(o.getOrderItems().size());
		for (OrderItem ot : o.getOrderItems()) {
			System.out.println(ot.getProductId());
		}
	}
	
	@Test
	public void testGetOrderList() {//查所有
		List<Order> list = this.orderDao.getOrderList();
		for (Order o : list) {
			System.out.println(o.getOrderNo());
			System.out.println(o.getOrderItems().size());
			for (OrderItem ot : o.getOrderItems()) {
				System.out.println(ot.getProductId());
			}
		}
	}
	
	@Test
	public void testDelOrder() {//删除
		Order order = new Order();
		order.setOrderId(8);
		this.orderDao.delOrder(order);
	}
}

“一方” 保存示意图:

在这里插入图片描述

关于 “一方” 多方 保存的简单总结:

在这里插入图片描述

级联保存的简单总结:

在这里插入图片描述

案例

一对多

首先我们先理解一对多的什么意思,在数据库A表上的一条数据,可以映射B表的多条数据库,也就是站在A表的角度,就被B表的都跳数据引用,
hiberante就认为A表拥有一个B表的集合,所以配置如下

 package com.xingxue.entity;

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

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;

//关联我们的表

@Entity  //该类被hibernate当做实体给管理
@Table(name="xx_plat_role")   //对应数据库的表
public class Role {

    @Id         //配置主键
    @GeneratedValue(strategy=GenerationType.IDENTITY)  //自增策略
    @Column(name="role_id")  //关联字段名和数据类型
    private int id;

    @Column(name="role_name")  //关联字段名和数据类型
    private String name;
    @Column(name="role_status")  //关联字段名和数据类型
    private String status;
    @Column(name="createdate", columnDefinition="DATETIME")  //关联字段名和数据类型
    private Date createdate;

    @OneToMany   //一对多配置,
    @JoinColumn(name="admin_role")  // 外键的名字
    private Set<Admin> admins = new HashSet<Admin>();

}

此处一对多配置,只配置1端,多端不用管理,所以Admin不需要任何配置
测试代码:

public boolean add(Admin param, int roleId) {
        Session session = this.sessionFactory.openSession();
        //开启事务
        Transaction t = session.beginTransaction();
        //get我们的role对象,让hibernate管理起来
        Role role = (Role) session.get(Role.class, roleId);
        //关联role和admin的关系
        role.getAdmins().add(param);

        int result = (Integer) session.save(param);
        //提交事务
        t.commit();
        //关闭回话
        session.close();
        return result > 0;
    }  

结果如下
在这里插入图片描述
根据测试结果我们发现,如果我们想要关联两张表的关系,:
1、hibernate配置了关联关系
2、当操作数据的时候,两个关联对象被hibernate管理起来,
3、两个对象之间必须建立关联关系

查询数据测试

@Override
    public List<Role> list() {
        Session session = this.sessionFactory.openSession();
        String SQL = "select * from xx_plat_role";
        List<Role> list = session.createSQLQuery(SQL).addEntity(Role.class).list();
        return list;
    }

在这里插入图片描述我们发现在查询role的时候,实际上hibernate自动帮我们查询了当前role下面的所有admin信息,并且封装到了set里面,也就是数据已经包装好了。省去了我们以前的链接查询的操作。
但是通过测试我们发现,在查admin的时候没有把admin相关的role给查询出来,那是因为admin没有配置映射关系,多对一,所以admin无效果,

懒加载设置
其实有的时候我们不需要查询admin信息,所以关联数据hiberante默认使用懒加载机制,所谓的懒加载就是我们需要使用这个数据他 才去查询,你不使用,H就不查询,但是必须建立在session不关闭的情况下,

    @OneToMany(fetch=FetchType.EAGER)   //没有懒加载,
    @OneToMany(fetch=FetchType.LAZY)   //使用懒加载,

由于不使用懒加载效率很低,所以我们默认都使用懒加载,如果在dao有需要进行关联数据加载,建议手动用代码访问一下关联数据

多对一

多对一实际上就是和一对多站的角度不一样,表之间的关系,如果是一对多,我们换个角度就是多对一,所以一般一对多和多对一都是双向关联配置,还是Admin和role为例
站在admin的角度多对一:

@Table(name="xx_plat_admin")
public class Admin {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="admin_id")
    private int id;

    @Column(name="admin_username",length=20)
    private String userName;

    @Column(name="admin_pwd",length=20)
    private String pwd;

    @ManyToOne
    @JoinColumn(name="admin_role")
    private Role role;

双向关联是如上配置, 同时需要注意的是,此种配置,我们的关系管理就交给了多端来进行管理了。
所以代码如下:

@Override
    public boolean add(Admin param, int roleId) {
        Session session = this.sessionFactory.openSession();
        //开启事务
        Transaction t = session.beginTransaction();
        //get我们的role对象,让hibernate管理起来
        Role role = (Role) session.get(Role.class, roleId);
        //关联role和admin的关系
//      role.getAdmins().add(param);
        param.setRole(role);

        int result = (Integer) session.save(param);
        //提交事务
        t.commit();
        //关闭回话
        session.close();
        return result > 0;
    }

多对一默认不是用懒加载,如果需要使用。需要手动开启

@ManyToOne(fetch=FetchType.LAZY)

多对多

hibernate多对多当中,我们常常希望只删除一方已及对应的关系,但不想删除另一方
表user和表role多对多,中间表user_role(userId,roleId),user是主控方,role是从方,
在spring+hibernate的环境下,使用的是Annotation配置
User.java

@ManyToMany(cascade = {CascadeType.MERGE,CascadeType.PERSIST}, fetch = FetchType.EAGER)
@JoinTable(name = "user_role",joinColumns = { @JoinColumn(name = "userId",referencedColumnName="userId")},inverseJoinColumns = { @JoinColumn(name = "roleId",referencedColumnName="roleId") })
 public Set<Role> getRoles() {
  return this.roles;
 }

Role.java

@ManyToMany(cascade = {CascadeType.MERGE,CascadeType.PERSIST}, fetch = FetchType.EAGER,

 mappedBy = "roles")
 public Set<User> getUsers() {
  return this.users;
 }

测试:主控方User删除,user会被删除,user_role的中的关系也会被删除了,但对应的role不会被删除

@Test
 public void testDelete() {
  User user = userDao.findById(8);
  userDao.delete(user);
 }

测试:从方Role删除,如果user_role里面没有对应的roleId,role可以删除,user不会被删除

@Test
 public void testDelete() {
  Role role = roleDao.findById(26);
  roleDao.delete(role); 

}

测试:从方Role删除,如果user_role里面有对应的roleId和别的userId关联,role不能被删除

@Test
 public void testDelete() {
  Role role = roleDao.findById(26);
  roleDao.delete(role); 

}

会报错:

org.springframework.dao.DataIntegrityViolationException: Could not execute JDBC batch update; nested exception is org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
Caused by: org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
Caused by: java.sql.BatchUpdateException: Cannot delete or update a parent row: a foreign key constraint fails (`hrms`.`user_role`, CONSTRAINT `roleId` FOREIGN KEY (`roleId`) REFERENCES `role` (`roleId`) ON DELETE NO ACTION ON UPDATE NO ACTION)

解决方案:在数据库里更改user_role表结构,就是添加约束,就添加roleId的删除时进行级联操作

   ALTER TABLE `hrms`.`user_role`
      ADD CONSTRAINT `roleId`
      FOREIGN KEY (`roleId` )
      REFERENCES `hrms`.`role` (`roleId` )
      ON DELETE CASCADE;

测试:从方Role删除,如果user_role里面有对应的roleId,现在role可以删除,user_role里面对应的关系也会被删除,但对应的user不会被删除,达到我们想要的效果


@Test
 public void testDelete() {
  Role role = roleDao.findById(26);
  roleDao.delete(role); 

}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/106605.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • pycharm一键调整代码格式_vs代码整理快捷键

    pycharm一键调整代码格式_vs代码整理快捷键1、代码自动填充空格2、自动对齐代码3、符合PEP8规范

  • 人脸识别系统如何建模_3dmax人脸建模

    人脸识别系统如何建模_3dmax人脸建模本发明涉及生物特征识别,特别是涉及人脸识别中的特征建模方法。背景技术:人脸识别技术一般包括四个组成部分,分别为人脸图像采集、人脸图像预处理、人脸图像特征提取以及匹配与识别,具体来说:人脸图像采集及检测是指通过摄像镜头等视频图像采集装置采集包括有人脸的视频或图像数据,可以是采集对象的静态图像、动态图像、不同的位置、不同表情等。人脸图像预处理是指从采集的图像数据中确定人脸的部分,并进行灰度校正、噪声过…

  • yarn 安装依赖(ubuntu16.04安装教程)

    Yarn是由Facebook开发的开源的JavaScript包管理工具,它在现在流行的npm基础上进行了升级改进。Facebook开发团队创造yarn来克服npm的缺陷。并声明它比npm更快,更可靠,更安全。Yarn能够向npm一样根据全局注册信息,自动的管理包的安装,更新,配置,删除过程。Yarn的优点是:它比npm的速度更快,因为它会缓存所有下载下来的包,因此它不需要下载第二遍。最…

  • volatile关键字到底有什么作用

    volatile关键字到底有什么作用提示:更多优秀博文请移步博主的GitHub仓库:GitHub学习笔记、Gitee学习笔记volatile是Java提供的一种轻量级的同步机制。Java语言包含两种内在的同步机制:同步块(或方法)和volatile变量,相比于synchronized(synchronized通常称为重量级锁),volatile更轻量级,因为它不会引起线程上下文的切换和调度。但是volatile变量的同…

  • dell T420热插拔安装过程

    dell T420热插拔安装过程

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号