hibernate级联更新外键问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了hibernate级联更新外键问题相关的知识,希望对你有一定的参考价值。

使用级联更新时,通过子表更新始终取不到主表的主键值,导致子表外键值始终是空的,而通过主表更新,子表能够取到所有值,小弟不解到底是怎么回事,请编程达人指教,代码如下:
/*添加订单到数据库*/
Order_ order=new Order_();
User user=(User)session.get(Constants.LOGIN_USER);
order.setUserId(user.getUserId());
order=this.orderService.saveOrder(order);

/*添加订单项到数据库*/
List orderitems=(List)session.get(Constants.CART);

//一对多级联更新
Set set=new HashSet(orderitems);
order.setOrderitems(set);
this.orderService.updateOrder(order);

Orderitem orderitem=new Orderitem();
Hotel hotel=new Hotel();
for(int i=0;i < orderitems.size();i++)
orderitem=(Orderitem)orderitems.get(i);
// orderitem.setOrder(order);使用多对一更新的时候始终取不到外键值,而一对多可行
// this.orderitemService.saveOrderitem(orderitem);

/*预定后更新对应酒店的预定次数*/
hotel=this.hotelService.findByHotelId(String.valueOf(orderitem.getHotelId()));
hotel.setTimes(hotel.getTimes()+1);
this.hotelService.updateHotel(hotel);


hbm.xml文件
<hibernate-mapping>
<class name="com.myssh.po.Order_" table="order_">
<id name="orderId" type="integer">
<column name="oid" />
<generator class="native" />
</id>
<!-- cascade="all"级联保存,更新,删除附属物。inverse="true"设置关联关系由对方维护。为set属性 -->
<set name="orderitems" cascade="all" inverse="false" lazy="false">
<!-- 相关联主键 -->
<key column="oid"/>
<!-- 设置一对多关联类 -->
<one-to-many class="com.myssh.po.Orderitem"/>
</set>
<property name="userId" type="integer">
<column name="uid" />
</property>
</class>
</hibernate-mapping>

<hibernate-mapping>
<class name="com.myssh.po.Orderitem" table="orderitem">
<id name="orderitemId" type="integer">
<column name="oiid"/>
<generator class="native"/>
</id>
<many-to-one name="order"
column="oid"
class="com.myssh.po.Order_"
cascade="none"
update="false"
insert="false"
not-null="true"
/>
<property name="orderId" type="integer">
<column name="oid"/>
</property>
<property name="hotelId" type="integer">
<column name="hid"/>
</property>
<property name="beginDate" type="date">
<!-- date是否应该配置格式? -->
<column name="begin"/>
</property>
<property name="days" type="integer">
<column name="days"/>
</property>
<property name="type" type="integer">
<column name="type"/>
</property>
</class>
</hibernate-mapping>

<set name="orderitems" cascade="all" inverse="false" lazy="false">
inverse=true
试试
级联操作 cascade="all" 你的子表配置里是cascade="none"
参考技术A <many-to-one name="order"
column="oid"
class="com.myssh.po.Order_"
cascade="none"
update="false"
insert="false"
not-null="true"
/>
子表建立的none的级联
子表更新等操作影响不到父表

三大框架 之 Hibernate查询(一对多多对多查询关系)

一对多

表之间关系

​ 一对多

  • 一个部门有多个员工,一个员工只能属于某一个部门
  • 一个班级有多个学生,一个学生只能属于一个班级

​ 多对多

  • 一个老师教多个学生,一个学生可以被多个老师教
  • 一个学生可以先择多门课程,一门课程可以被多个学生选择
  • 一个用户可以选择多个角色,一个角色也可以被多个用户选择

​ 一对一

  • 一个公司只能对应一个注册地址

表之间关系建表原则

​ 一对多
​ 在多的一方创建一个外键,指向一的一方的主键
​ 多对多
创建一个中间表,中间表至少有两个字段,分别作为外键指向多对多双方的主键
​ 一对一
​ 唯一外键对应
​ 主键对应

一对多关系配置

建立表

创建表的 hbm.xml文件时,有外键可不创建列的映射

主表为客户(Customer),从表为联系人(Linkman)

销售联系人(linkman),一个联系人只能属于某一个客户

CREATE TABLE `linkman` (
  `link_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT \'联系人编号(主键)\',
  `link_name` varchar(16) DEFAULT NULL COMMENT \'联系人姓名\',
  `link_cust_id` bigint(32) NOT NULL COMMENT \'客户id\',
  `link_gender` char(1) DEFAULT NULL COMMENT \'联系人性别\',
  `link_phone` varchar(16) DEFAULT NULL COMMENT \'联系人办公电话\',
  `link_mobile` varchar(16) DEFAULT NULL COMMENT \'联系人手机\',
  `link_email` varchar(64) DEFAULT NULL COMMENT \'联系人邮箱\',
  `link_qq` varchar(16) DEFAULT NULL COMMENT \'联系人qq\',
  `link_position` varchar(16) DEFAULT NULL COMMENT \'联系人职位\',
  `link_memo` varchar(512) DEFAULT NULL COMMENT \'联系人备注\',
  PRIMARY KEY (`link_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

客户(customer),一个客户可以有多个联系人

CREATE TABLE `customer` (
  `cust_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT \'客户编号(主键)\',
  `cust_name` varchar(32) NOT NULL COMMENT \'客户名称(公司名称)\',
  `cust_source` varchar(32) DEFAULT NULL COMMENT \'客户信息来源\',
  `cust_industry` varchar(32) DEFAULT NULL COMMENT \'客户所属行业\',
  `cust_level` varchar(32) DEFAULT NULL COMMENT \'客户级别\',
  `cust_phone` varchar(64) DEFAULT NULL COMMENT \'固定电话\',
  `cust_mobile` varchar(16) DEFAULT NULL COMMENT \'移动电话\',
  PRIMARY KEY (`cust_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

关系图

建立ORM

实体类与数据库中建立字段关系

Customer 实体类(一个客户可以有多个联系人)
package com.myxq.domain;
import lombok.Getter;
import lombok.Setter;
import java.util.HashSet;
import java.util.Set;
@Getter@Setter
public class Customer {
    private Long cust_id;
    private String cust_name;
    private String cust_source;
    private String cust_industry;
    private String cust_level;
    private String cust_phone;
    private String cust_mobile;
    //建立一个客户可以有多个联系人
    //放置多的一方的集合,hibernate默认使用的是Set集合
    //如果使用List的话,它要对List进行排列,在表中要多建这一列,用来排序
    //一般使用的都是Set集合
    //现在是双向关联,从客户能查联系人,从联系人也能查客户
    private Set<Linkman> linkmens = new HashSet<>();
    @Override
    public String toString() {
        return "Customer{" +
                "cust_id=" + cust_id +
                ", cust_name=\'" + cust_name + \'\\\'\' +
                ", cust_source=\'" + cust_source + \'\\\'\' +
                ", cust_industry=\'" + cust_industry + \'\\\'\' +
                ", cust_level=\'" + cust_level + \'\\\'\' +
                ", cust_phone=\'" + cust_phone + \'\\\'\' +
                ", cust_mobile=\'" + cust_mobile + \'\\\'\' +
                \'}\';
    }
}
Linkman 实体类(一个联系人只能属于一个客户)
package com.myxq.domain;
import lombok.Getter;
import lombok.Setter;
@Getter@Setter
public class Linkman {
    private Long   link_id;
    private String link_name;
    private String link_gender;
    private String link_phone;
    private String link_mobile;
    private String link_email;
    private String link_qq;
    private String link_position;
    private String link_memo;
    private String link_cust_id;
    //一个联系人只对应一个客户
    private Customer customer;
    @Override
    public String toString() {
        return "Linkman{" +
                "link_id=" + link_id +
                ", link_name=\'" + link_name + \'\\\'\' +
                ", link_gender=\'" + link_gender + \'\\\'\' +
                ", link_phone=\'" + link_phone + \'\\\'\' +
                ", link_mobile=\'" + link_mobile + \'\\\'\' +
                ", link_email=\'" + link_email + \'\\\'\' +
                ", link_qq=\'" + link_qq + \'\\\'\' +
                ", link_position=\'" + link_position + \'\\\'\' +
                ", link_memo=\'" + link_memo + \'\\\'\' +
                ", link_cust_id=\'" + link_cust_id + \'\\\'\' +
                ", customer=" + customer +
                \'}\';
    }
}

添加配置文件

1.客户(Customer)实体类的配置文件

customer.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="com.myxq.domain.Customer" table="customer" >
        <!--建立类属性哪一个是主键  还要跟数据库当中主键进行对象-->
        <id name="cust_id" column="cust_id" >
            <generator class="native"/>
        </id>
        <!--建立类中的普通属性与数据库当中的表字段的映射,关联-->
        <property name="cust_name" column="cust_name" />
        <property name="cust_source" column="cust_source"/>
        <property name="cust_industry" column="cust_industry"/>
        <property name="cust_level" column="cust_level"/>
        <property name="cust_phone" column="cust_phone"/>
        <property name="cust_mobile" column="cust_mobile"/>
        <!--一对多-->
        <set name="linkmens" cascade="save-update,delete" inverse="true"><!--set属性名称-->
            <key column="link_cust_id"></key><!--外键-->
            <one-to-many class="com.myxq.domain.Linkman"></one-to-many>
        </set>

    </class>
</hibernate-mapping>

2.联系人(LinkMan)实体类配置文件
linkman.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="com.myxq.domain.Linkman" table="linkman" >
        <!--建立类属性哪一个是主键  还要跟数据库当中主键进行对象-->
        <!-- 设置主键与OID的对应关系 -->
        <id name="link_id" column="link_id" >
            <generator class="native"/>
        </id>
        <!--建立类中的普通属性与数据库当中的表字段的映射  注意:外键不用设置-->
        <property name="link_name" column="link_name" />
        <property name="link_gender" column="link_gender"/>
        <property name="link_phone" column="link_phone"/>
        <property name="link_mobile" column="link_mobile"/>
        <property name="link_email" column="link_email"/>
        <property name="link_qq" column=" link_qq"/>
        <property name="link_position" column=" link_position"/>
        <property name="link_memo" column=" link_memo"/>

        <!--
                many-to-one:配置多对一
                name:一的一方对象属性名称
                class:一的一方类的全路径
                column:多的一方表的外键名称
        -->
        <many-to-one name="customer" cascade="save-update" class="com.myxq.domain.Customer" column="link_cust_id"/>
    </class>
</hibernate-mapping>

在hibernate.cfg.xml中的标签里,添加核心配置文件

		<!--加载映射文件-->
		<mapping resource="com/myxq/domain/customer.hbm.xml" />
		<mapping resource="com/myxq/domain/linkman.hbm.xml" />
		<mapping resource="com/myxq/domain/role.hbm.xml" />
		<mapping resource="com/myxq/domain/user.hbm.xml" />

引入工具类

HibernateUtil.java

package com.myxq.utils;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {
    public static final SessionFactory sessionFactory;
    static {
        //1.加载配置文件
        Configuration configure = new Configuration().configure();
        //configure.addResource("com/myxq/domain/Customer.hbm.xml");
        //2.创建sessionFactory   --JDBC 连接池
        sessionFactory = configure.buildSessionFactory();
    }
    public static Session openSession(){
        Session session = sessionFactory.openSession();
        return  session;
    }
    public static Session getCurrentSession(){
        Session session = sessionFactory.getCurrentSession();
        return  session;
    }
}

编写测试类

级联操作

问题
​ 在两张表建立一对多关系时,如果只保存一边的对象,就会发异常
示例

什么是级联

​ 在操作一个对象的时候,是否会操作其关联的对象。

级联分类

​ 级联保存或更新
​ 级联删除

级联是有方向性

​ 在操作一的一方,是否会操作多的一方
​ 操作多的一方时, 是否会操作一的一方

级联保存或更新

级联保存

​ 操作的主体是谁,就要在谁的映射配置文件当中进行配置
​ 在开始配置的set当中添加一个新的属性cascade="save-update"

​ 在多的一方添加级联

​ 再去运行,就不会报异常,两条记录都会被添加
​ 在一的一方添加级联

对象导航

​ 两方如果都加了级联,这种我们也称为双向导航
​ 设置双向导航时,当对象存在关系时, 就会做出对应的操作

级联更新

级联删除

删除一边数据时,同时将另一边的数据一并删除

不设置级联删除

​ 默认:先把外键改为空,然后再删除

​ 发送的SQL语句

设置级联删除

示例代码

配置文件

在双向级联的过程当中,会产生一些多余的sql语句

原因
​ 当双向维护时,两都都维护了外键,当做更新操作时, 两边的外键都要去修改

解决办法

1.使用单向维护
​ 有些地方还是会有问题
2.一方放弃维护权
​ 在一的一方放弃外键维护权
​ 在配置文件当中添加一个inverse="false/true"
​ true为放弃外键维护权,false为不放弃外键维护权

cascade与inverse

​ cascade控制有没有关联对象
​ inverse控制有没有外键
​ 示例

lazy懒加载(默认值是proxy,不自动获取外键对象)

改成false,获取外键对象

在linkman.hbm.xml

<many-to-one  name="customer"   class="com.myxq.domain.Customer"   column="link_cust_id"    lazy="false"/>

级联保存或更新(解决 瞬时对象异常,只保存一边)

在customer.hbm.xml

<set name="linkmens" cascade="save-update,delete" inverse="true">
	<!--set属性名称-->
    <key column="link_cust_id"></key><!--外键-->
    <one-to-many class="com.myxq.domain.Linkman">
    </one-to-many>
</set>

多对多

多对多关系配置

建立表

用户表,一个用户可以有多个角色

CREATE TABLE `user` (
				  `user_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT \'用户id\',
				  `user_code` varchar(32) NOT NULL COMMENT \'用户账号\',
				  `user_name` varchar(64) NOT NULL COMMENT \'用户名称\',
				  `user_password` varchar(32) NOT NULL COMMENT \'用户密码\',
				  `user_state` char(1) NOT NULL COMMENT \'1:正常,0:暂停\',
				  PRIMARY KEY (`user_id`)
				) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;

角色表,一个角色可以被多个用户选择

CREATE TABLE `role` (
				  `role_id` bigint(32) NOT NULL AUTO_INCREMENT,
				  `role_name` varchar(32) NOT NULL COMMENT \'角色名称\',
				  `role_memo` varchar(128) DEFAULT NULL COMMENT \'备注\',
				  PRIMARY KEY (`role_id`)
				) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

关系图

建立ORM

  • 用户
  • 角色


Role.java

import lombok.Getter;
import lombok.Setter;
import java.util.HashSet;
import java.util.Set;
@Setter@Getter
public class Role {
    private Long role_id;
    private String role_name;
    private String role_memo;
    //角色下面的所有用户
    private Set<User> users = new HashSet<>();
}

添加配置文件

用户

角色

在核心配置文件当中添加两个新配置

编写测试类

单向维护

双向维护

双向维护时,必须要有一方放弃外键维护
如果两边都有维护的话, 就会有重复的的记录,由于关系表是两个字段作为共同主键,不能有相同的记录
解决办法
​ 通常都是让被动方放弃,用户选角色,角色为被动方

多对多的级联操作和一对多的级联操作是一样的

多对多的操作

关系的操作,只需要操作集合,就可以操作它们之间的关系
给用户添加一个新的角色

修改一个用户的角色

删除角色

查询方式

OID查询

什么是OID查询

  • 根据对象的OID主键进行检索

OID查询方式

  • get方法
  • load方法

对象导航查询

什么是对象导航检索

  • Hibernate根据一个已经查询到的对象,获得其关联的对象的一种查询方式
  • 先查询到联系人,就可以通过联系人获取联系人所关联的客户对象

有点像级联查询

HQL

什么是HQL

  • HQL查询:Hibernate Query Language,Hibernate的查询语言
  • 是一种面向对象的方式的查询语言,语法类似SQL。
  • 通过session.createQuery(),用于接收一个HQL进行查询方式。
  • 注意:使用时,不能用*,对于表,要采用别名查询

查询

简单查询

别名查询

排序查询

条件查询

​ 位置绑定:根据参数的位置进行绑定条件

​ 名称绑定:把参数对应的值起一个名称 再去设置名称

投影查询

​ 查询对象的某个或某些属性
​ 单个属性

​ 多个属性

​ 查询多个属性,封装到对象当中
​ 要在类中,提供构造方法

分页查询

统计查询

​ 查询的结构只有一个

分组查询

多表查询

​ 普通内连接

​ 迫切内连接

​ 通过hibernate将另一个对象的数据,封装该对象中

​ 在普通内连接inner join 后添加一个关键字fetch

QBC

什么是QBC

​ Query By Criteria,条件查询。是一种更加面向对象化的查询的方式。

查询

简单查询

排序查询

分页查询

条件查询

条件
=   eq
> gt
> =  ge
<  lt
<=  le
<> ne
like
in
and
or
单个条件

多个条件

统计查询

离线条件查询
  • 脱离Session,添加条件
  • 可以在外部提前使用DetachedCriteria对象提交设置好条件
  • 最后再绑定到session当中

以上是关于hibernate级联更新外键问题的主要内容,如果未能解决你的问题,请参考以下文章

请问hibernate级联更新时并不删除子表数据,只是将其外键置为null,是怎么回事?

如何在两个外键上设置 Hibernate @ManyToMany 与级联的关联?

三大框架 之 Hibernate查询(一对多多对多查询关系)

hibernate之一对多,多对一

Java Spring boot hibernate删除级联数据

关于Hibernate级联更新的一个问题