前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >hibernate的关联与级联

hibernate的关联与级联

作者头像
全栈程序员站长
发布2022-08-04 17:02:53
1.3K0
发布2022-08-04 17:02:53
举报
文章被收录于专栏:全栈程序员必看

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

什么是关联(association)

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

关联是有方向的

关联的关键点都在外键上

如何建立一对多双向关联

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

代码语言:javascript
复制
initOrderItems = 0;//0代表懒加载	1代表立即加载

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

代码语言:javascript
复制
定义一对多的关系时需要采用接口方式

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

代码语言:javascript
复制
		<!-- 
			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中需要添加(建立订单项对订单多对一的关系)

代码语言:javascript
复制
<property name="oid" type="java.lang.Integer" column="oid" insert="false" update="false"></property>
代码语言:javascript
复制
<!-- 会报错 -->
		<many-to-one name="order" class="com.zking.four.entity.Order" column="oid"></many-to-one>

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

代码语言:javascript
复制
 <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>

方法:

代码语言:javascript
复制
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测试

代码语言:javascript
复制
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表的集合,所以配置如下

代码语言:javascript
复制
 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不需要任何配置 测试代码:

代码语言:javascript
复制
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、两个对象之间必须建立关联关系

查询数据测试

代码语言:javascript
复制
@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不关闭的情况下,

代码语言:javascript
复制
    @OneToMany(fetch=FetchType.EAGER)   //没有懒加载,
    @OneToMany(fetch=FetchType.LAZY)   //使用懒加载,

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

多对一

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

代码语言:javascript
复制
@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;

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

代码语言:javascript
复制
@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;
    }

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

代码语言:javascript
复制
@ManyToOne(fetch=FetchType.LAZY)

多对多

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

代码语言:javascript
复制
@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

代码语言:javascript
复制
@ManyToMany(cascade = {CascadeType.MERGE,CascadeType.PERSIST}, fetch = FetchType.EAGER,

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

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

代码语言:javascript
复制
@Test
 public void testDelete() {
  User user = userDao.findById(8);
  userDao.delete(user);
 }

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

代码语言:javascript
复制
@Test
 public void testDelete() {
  Role role = roleDao.findById(26);
  roleDao.delete(role); 

}

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

代码语言:javascript
复制
@Test
 public void testDelete() {
  Role role = roleDao.findById(26);
  roleDao.delete(role); 

}

会报错:

代码语言:javascript
复制
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的删除时进行级联操作

代码语言:javascript
复制
   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不会被删除,达到我们想要的效果

代码语言:javascript
复制
@Test
 public void testDelete() {
  Role role = roleDao.findById(26);
  roleDao.delete(role); 

}

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年4月2,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是关联(association)
  • 如何建立一对多双向关联
  • “一方” 保存示意图:
  • 关于 “一方” 多方 保存的简单总结:
  • 级联保存的简单总结:
  • 案例
    • 一对多
      • 多对一
        • 多对多
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档