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)


相关推荐

  • idea 配置SVN_idea的svn插件

    idea 配置SVN_idea的svn插件如何解决idea中配置svn不能用的问题Can’tuseSubversioncommandlineclient:svnThepathtotheSubversionexecutableisprobablywrong.Fixit.mac自带svnsvn–versionbrewlistsubversion然后idea配置文件更改如果默认这样有问题更改为…

  • OSChina 周六乱弹 ——在你玩的时候,妹子都在干这个[通俗易懂]

    OSChina 周六乱弹 ——在你玩的时候,妹子都在干这个

  • linux signal 处理

    linux signal 处理

  • gtest框架_软件测试框架

    gtest框架_软件测试框架gtest下载与安装gitclonehttps://github.com/google/googletest.gitcdgoogletestmkdirbuildcmake..makesudomakeinstall以上命令会将gtest编译好,并将动态链接库放在/usr/local/lib目录下:zhouhao@ubuntu:/usr/loc…

  • cdn加速以及前后分离SpringBoot+Vue 配置https及SSL证书「建议收藏」

    cdn加速以及前后分离SpringBoot+Vue 配置https及SSL证书「建议收藏」配置方法同上篇文章一样CDN加速配置,阿里云cdn内配置好以后即可剩下把前端服务器nginx内的证书文件上传以及配置写好即可。后端服务器也同上配置证书下载上传服务器配置好nginx即可。如果都配置好以后,不可以访问时,注意查看前端请求接口是否为https方式。如果为http时,检查是否更新服务器成功,如果成功后还不可以正常访问时,前端修改代码如下:vue.config.js内添加下行代码。index.html下添加代码:https:true完毕!…

  • pycharm添加anaconda解释器_anaconda找不到指定模块

    pycharm添加anaconda解释器_anaconda找不到指定模块Pycharmanaconda创建虚拟环境章节导航Pycharmanaconda创建虚拟环境背景知识Pycharm新建环境Pycharm使用已创建环境实操方案创建新项目选择已存在解释器在Pycharm添加已存在的解释器进入该项目的解释器设置页面查看解释器所带软件包背景知识Pycharm新建环境PyCharm使用anaconda新建环境是只包含一些基础包,后续如果想要如Scrapy.requests等库的话则需要自己在解释器页面添加了(ctrl+alt+s进入解释器设置页面)而且新环境中的

发表回复

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

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