大家好,又见面了,我是你们的朋友全栈君。
什么是关联(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账号...