继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Hibernate【映射】知识要点(2)

Java3y
关注TA
已关注
手记 297
粉丝 1.5万
获赞 6488
解除关联关系

部门和员工是存在外键的联系的,我们接下来看看inverse属性对于解除关联关系有没有影响

  • 当部门一方有权限时,即inverse属性为false

        //查找部门id为1的信息
        Dept dept1 = (Dept) session.get(Dept.class, 1);

        //清除关联信息
        dept1.getSet().clear();

可以解除关联关系,employee的外键字段被设置为NULL了

这里写图片描述

  • 当部门一方没有权限时,即inverse属性为true

        //查找部门id为2的信息
        Dept dept1 = (Dept) session.get(Dept.class, 2);

        //清除关联信息
        dept1.getSet().clear();

不能解除关联关系

这里写图片描述


删除数据对关联关系的影响
  • 当部门一方有权限时,即inverse属性为false
        //查找部门id为2的信息
        Dept dept1 = (Dept) session.get(Dept.class, 2);

        //删除部门2
        session.delete(dept1);

当有控制权的时候可以删除数据,先把外键设置为NULL,再删除数据!

这里写图片描述

  • 当部门一方没有权限时,即inverse属性为true
        //查找部门id为1的信息
        Dept dept1 = (Dept) session.get(Dept.class, 1);

        //删除部门1
        session.delete(dept1);

直接抛出异常,说该部门拥有外键,不能删除数据!

这里写图片描述


cascade属性

cascade表示级联的意思,简单来说就是操作某一属性时,对其他关联字段的影响

casecade属性不像inverse属性只能在“一”的一方设置,它可以在“一”的一方也可以在“多”的一方设置

cascade有这么几个值

  • none 不级联操作, 默认值
  • save-update 级联保存或更新
  • delete 级联删除
  • save-update,delete 级联保存、更新、删除
  • all 同上。级联保存、更新、删除
级联保存

级联保存有什么用呢???我们来看个例子:

  • 如果在保存对象的时候,没有把相关的对象也一并保存进数据库,会出现错误
  • 因为它会发现dept是有外键的,而外键又是一个对象来保存着,这个对象在数据库表中并没有存在,因此会抛出异常

        //创建对象
        Dept dept = new Dept();
        dept.setDeptName("开发部");

        Employee zs = new Employee();
        zs.setEmpName("张珊");
        zs.setSalary(1111);
        Employee ls = new Employee();
        ls.setEmpName("李四");
        ls.setSalary(2222);

        //维护关系
        dept.getSet().add(zs);
        dept.getSet().add(ls);

        //保存dept对象
        session.save(dept);

这里写图片描述

如果我们在dept中设置了级联保存,那么Hibernate就会知道:保存dept的数据时,发现dept了外键,也把dept外键的对象保存在数据库之中


    <set name="set" table="employee" cascade="save-update" >

这里写图片描述


级联删除

级联删除,这个对于我们来说风险太大了,如果删除了某些数据,会把另外有关联的数据也删除...在实际中我们一般不使用!

  • 在没有设置级联删除的时候,我们试试删除dept
        //删除部门为3的记录
        Dept dept1 = (Dept) session.get(dept.getClass(), 3);
        session.delete(dept1);

它会先把3的外键对应的记录删除,再删除部门的数据

这里写图片描述

  • 在设置级联删除的时候,我们试试删除dept

        <set name="set" table="employee" cascade="save-update,delete"  >
  • 级联删除数据

        //删除部门为4的记录
        Dept dept1 = (Dept) session.get(dept.getClass(), 4);
        session.delete(dept1);

我们发现数据相关联的数据都没有了

这里写图片描述


cascade和inverse同时使用

上面我们已经把cascade和inverse都介绍一遍了,那么cascade和inverse同时使用会怎么样呢???我们来测试一下

  • 设置dept没有控制权,但设置级联保存、删除
        <set name="set" table="employee" cascade="save-update,delete" inverse="true"  >
  • 添加一个dept对象
        //添加一个dept对象
        session.save(dept);

如果我们单单设置了inverse属性为true,那么数据库中肯定是不能维护关联关系的【这里我们已经测试了】

但是呢,现在也设置了级联保存,级联保存是否可以让该对象相关的关联关系一并保存在数据库中的

我们来看看结果:

这里写图片描述

inverse的优先级是比cascade的优先级要高的,因此设置了inverse属性为true,那么cascade就无效了。

参考详细博文:

http://www.cnblogs.com/whgk/p/6135591.html

ascade和inverse总结

上面的测试都是通过几个方面的,看起来有点多,因此我们总结一下

inverse属性

inverse属性只能在“一”的一方中设置。inverse=false表示有控制权,inverse=ture表示没有控制权

  • 在保存关联信息时
    • 有控制权--->可以保存相对应的关联数据
    • 没有控制权--->数据会保存,但是关联关系没有维护,也就是外键列为NULL
  • 在查询数据时
    • 有无控制权对查询数据没有任何影响
  • 在解除关联关系时
    • 有控制权--->可以解除关联关系
    • 没有控制权--->不能解除关联关系,不会生成update语句,也不会报错
  • 在删除数据时对关联关系的影响
    • 有控制权--->将外键的值设置为NULL,随后删除数据
    • 没有控制权--->如果删除的记录有被外键引用,会报错,违反主外键引用约束,如果删除的记录没有被引用,可以直接删除

多对多关系的时候也是一样的,只不过多对多的关联关系是在中间表中

cascade属性

cascade有这么几个值:

  • none 不级联操作, 默认值
  • save-update 级联保存或更新
  • delete 级联删除
  • save-update,delete 级联保存、更新、删除
  • all 同上。级联保存、更新、删除

我们可能使用到的往往是:save-update这个值,因为级联删除的风险太大了

  • 级联保存
    • 没有设置级联保存-->如果单单保存一个对象,而对象又存在外键时,那么就会抛出异常
    • 设置了级联保存-->那么就可以将对象以及有关联关系的对象一并保存
  • 级联删除
    • 没有设置级联删除-->在删除数据的时候,会把外键的字段设置为NULL,再删除当前一方的记录
    • 设置了级联删除-->把对象有关联关系的记录都删除了

如果cascade和inverse同时设置时:

  • inverse属性优先级是要比cascade要高的,如果inverse属性设置了true,那么cascade就无效了!

多对多映射

需求:一个项目由多个员工开发,一个员工开发多个项目

设计数据库表

一般地,如果是多对多的映射,我们都会使用一张中间表来保存它们的关联关系....

这里写图片描述

设计实体

我们在设计实体的时候,一般是核心数据表对应一个JavaBean实体【中间表并不是核心数据表】,那么我们将会设计两个JavaBean对象

这里写图片描述

project.java


package zhongfucheng.many2many;

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

/**
 * Created by ozc on 2017/5/7.
 */

public class Project {

    private int projectId;
    private String projectName;

    //使用Set集合与developer实体维护关系
    private Set<Developer> developers = new HashSet<>();

    public int getProjectId() {
        return projectId;
    }

    public void setProjectId(int projectId) {
        this.projectId = projectId;
    }

    public String getProjectName() {
        return projectName;
    }

    public void setProjectName(String projectName) {
        this.projectName = projectName;
    }

    public Set<Developer> getDevelopers() {
        return developers;
    }

    public void setDevelopers(Set<Developer> developers) {
        this.developers = developers;
    }
}

developer.java


package zhongfucheng.many2many;

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

/**
 * Created by ozc on 2017/5/7.
 */
public class Developer {

    private int developerId;
    private String developerName;

    //使用Set集合来维护与Project关系
    private Set<Project> projects = new HashSet<>();

    public int getDeveloperId() {
        return developerId;
    }

    public void setDeveloperId(int developerId) {
        this.developerId = developerId;
    }

    public String getDeveloperName() {
        return developerName;
    }

    public void setDeveloperName(String developerName) {
        this.developerName = developerName;
    }

    public Set<Project> getProjects() {
        return projects;
    }

    public void setProjects(Set<Project> projects) {
        this.projects = projects;
    }
}
映射配置文件

以项目映射文件为例:我们不急着写,首先来分析一下关键点......想要在多对多映射中产生正确的关联关系,下面几步必不可少:

  • 配置映射集合的属性(developers)
  • 映射集合对应的中间表(developer_project)
  • 中间表的外键字段(project_id)
  • 集合元素的类型(Developer)
  • 中间表另外的外键字段(developer_id)

Project和Developer的映射文件都需要这几个关键步骤

Project映射文件

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<!--在domain包下-->
<hibernate-mapping package="zhongfucheng.many2many">

    <class name="Project" table="Project">

        <!--映射主键-->
        <id name="projectId" column="projectId">
            <generator class="native"></generator>
        </id>

        <!--映射普通字段-->
        <property name="projectName" column="projectName"></property>

        <!--映射多对多的关系-->
        <!--Set的属性名称为developers,对应developer_project表-->
        <set name="developers" table="developer_project">
            <!--对应developer_project表的外键列-->
            <key column="project_id"></key>

            <!--集合的类型和developer_project表的另一个外键列-->
            <many-to-many column="developer_id" class="Developer"></many-to-many>
        </set>

    </class>

</hibernate-mapping>

Developer映射文件


<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<!--在domain包下-->
<hibernate-mapping package="zhongfucheng.many2many">

    <class name="Developer" table="Developer">

        <!--映射主键-->
        <id name="developerId" column="developerId">
            <generator class="native"></generator>
        </id>

        <!--映射普通字段-->
        <property name="developerName" column="developerName"></property>

        <!--映射多对多的关系-->
        <!--Set的属性名称为developers,对应developer_project表-->
        <set name="projects" table="developer_project">
            <!--对应developer_project表的外键列-->
            <key column="developer_id"></key>

            <!--集合的类型和developer_project表的另一个外键列-->
            <many-to-many column="project_id" class="Project"></many-to-many>
        </set>

    </class>

</hibernate-mapping>

测试

package zhongfucheng.many2many;

import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.classic.Session;

/**
 * Created by ozc on 2017/5/6.
 */
public class App2 {
    public static void main(String[] args) {

        /*
        *
        *
        *     Project   Developer
              电商系统
                             曹吉
                             王春
               OA系统
                             王春
                             老张

            */
        //创建对象
        Developer cj = new Developer();
        Developer wc = new Developer();
        Developer lz = new Developer();
        Project ds = new Project();
        Project oa = new Project();

        //设置对象的数据
        cj.setDeveloperName("曹吉");
        wc.setDeveloperName("王春");
        lz.setDeveloperName("老张");

        oa.setProjectName("OA系统");
        ds.setProjectName("电商系统");

        //使用Project来关联数据【在多对多中,一样的】
        oa.getDevelopers().add(wc);
        oa.getDevelopers().add(lz);

        ds.getDevelopers().add(cj);
        ds.getDevelopers().add(wc);

        //获取加载配置管理类
        Configuration configuration = new Configuration();

        //加载User的映射文件!
        configuration.configure().addClass(Developer.class).addClass(Project.class);

        //创建Session工厂对象
        SessionFactory factory = configuration.buildSessionFactory();

        //得到Session对象
        Session session = factory.openSession();

        //使用Hibernate操作数据库,都要开启事务,得到事务对象
        Transaction transaction = session.getTransaction();

        //开启事务
        transaction.begin();

        //在Project映射文件中设置级联保存了
        session.save(oa);
        session.save(ds);

        //提交事务
        transaction.commit();

        //关闭Session
        session.close();
    }
}

执行了9条SQL语句,数据库中的记录也是正确的。

这里写图片描述


一对一的映射

需求:用户与身份证信息..一个用户对应一个身份证

数据库表设计

对于数据库表设计我们有两种方式

  • 第一种:在身份证的数据表中设置一个外键来维护用户的关系,这个外键也应该是唯一的【一个用户对应一张身份证】
  • 第二种:在身份证的数据表中使用主键+外键的方式来维护用户的关系。

这里写图片描述

设计实体

idCard.java

package zhongfucheng.one2one;

/**
 * Created by ozc on 2017/5/7.
 */
public class IdCard {

    private int idCardId;
    private String idCardName;

    //维护与用户之间的关系
    private User user ;

    public int getIdCardId() {
        return idCardId;
    }

    public void setIdCardId(int idCardId) {
        this.idCardId = idCardId;
    }

    public String getIdCardName() {
        return idCardName;
    }

    public void setIdCardName(String idCardName) {
        this.idCardName = idCardName;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
}

User.java

package zhongfucheng.one2one;

/**
 * Created by ozc on 2017/5/7.
 */
public class User {

    private int userId;
    private String userName;

    //维护与身份证一对一的关系
    private IdCard idCard ;

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public IdCard getIdCard() {
        return idCard;
    }

    public void setIdCard(IdCard idCard) {
        this.idCard = idCard;
    }
}

第一种方式映射文件

我们有两种方式来设计数据库中的表实现一对一的关系,首先我们来挑比较熟悉的外键方式来写映射文件

user映射文件


<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="zhongfucheng.one2one">

    <class name="User" table="User">

        <!--映射主键-->
        <id name="userId" column="userId">
            <generator class="native"></generator>
        </id>

        <!--映射普通字段-->
        <property name="userName" column="userName"></property>

        <!--
            User是没有外键字段的表
                一对一的关系的属性名称name是idCard
                类型是IdCard
        -->
        <one-to-one name="idCard" class="IdCard"></one-to-one>

    </class>

</hibernate-mapping>

idCard映射文件


<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="zhongfucheng.one2one">

    <class name="IdCard" table="IdCard">

        <!--映射主键-->
        <id name="idCardId" column="idCardId">
            <generator class="native"></generator>
        </id>

        <!--映射普通字段-->
        <property name="idCardName" column="idCardName"></property>

        <!--idCart是有外键的表,要把字段映射成外键,用的是manyToOne-->

        <!--
            外键的属性name是user
            对应表的字段是userId
            属性的类型是User
            该字段需要唯一性 unique
        -->
        <many-to-one name="user" column="user_id" class="User" unique="true" cascade="save-update"></many-to-one>

    </class>

</hibernate-mapping>
测试

要使用IdCart来维护User的关联关系。

  • 如果使用User来维护idCart的关联关系,idCart的外键列是为NULL的,因为重头到尾我们都没有给它赋值
  • 而使用IdCart来维护User,是外键值是根据User的主键id来生成的

package zhongfucheng.one2one;

import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.classic.Session;

/**
 * Created by ozc on 2017/5/6.
 */
public class App3 {
    public static void main(String[] args) {

        //创建对象
        User user = new User();
        IdCard idCard = new IdCard();

        //设置对象的数据
        user.setUserName("你好");
        idCard.setIdCardName("身份证001");

        //一对一关联数据
        idCard.setUser(user);

        //获取加载配置管理类
        Configuration configuration = new Configuration();

        //加载User的映射文件!
        configuration.configure().addClass(User.class).addClass(IdCard.class);

        //创建Session工厂对象
        SessionFactory factory = configuration.buildSessionFactory();

        //得到Session对象
        Session session = factory.openSession();

        //使用Hibernate操作数据库,都要开启事务,得到事务对象
        Transaction transaction = session.getTransaction();

        //开启事务
        transaction.begin();

        //保存对象的数据,idCard配置文件使用级联保存
        session.save(idCard);

        //提交事务
        transaction.commit();

        //关闭Session
        session.close();
    }
}

这里写图片描述


第二种方式映射文件

因为IdCart使用userId作为了主键,因此需要在JavaBean中配置多一个属性UserId...其他的都不用变


    private int userId;
    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }
打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP