Hibernate级联操作和加载机制(一) cascade and fetch

本篇介绍Hibernate的级联操作和加载机制


首先来看级联,级联在Hibernate中用cascade这一属性表示,首先看什么是级联。比如在关系映射中,无论是一对一还是一对多还是其他:对象A和对象B是关联的,那么在对A进行CRUD操作的时候,比如将A持久化到数据库中时,也把B一起持久化进去,或者只是持久化A而不关联B,这种就称之为级联操作。


下面来看一个具体的实例,映射关系就用一对多的双向关系,用之前的例子,问卷和问题,一份问卷中包含多个问题,但是一个问题只能属于一份问卷。我们先来回顾一下实体类的书写。

@Entity
@Table(name="t_questionnaire")
public class Questionnaire {

    private Integer id;
    private String name;
    private String answers;
    private List<Question> questions = new ArrayList<Question>();

    public Questionnaire() {
        super();
    }

    public Questionnaire(Integer id, String name, List<Question> questions) {
        super();
        this.id = id;
        this.name = name;
        this.questions = questions;
    }

    @Id
    @GeneratedValue
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }



    public String getAnswers() {
        return answers;
    }

    public void setAnswers(String answers) {
        this.answers = answers;
    }

    @OneToMany(mappedBy="questionnaire")
    public List<Question> getQuestions() {
        return questions;
    }

    public void setQuestions(List<Question> questions) {
        this.questions = questions;
    }

}

以上代码是问卷这个实体类。

@Entity
@Table(name="t_question")
public class Question {

    private Integer id;
    private String name;
    private Questionnaire questionnaire;

    public Question() {
        super();
    }

    public Question(Integer id, String name, Questionnaire questionnaire) {
        super();
        this.id = id;
        this.name = name;
        this.questionnaire = questionnaire;
    }

    @Id
    @GeneratedValue
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @ManyToOne
    @JoinColumn(name="questionnaireId")
    public Questionnaire getQuestionnaire() {
        return questionnaire;
    }

    public void setQuestionnaire(Questionnaire questionnaire) {
        this.questionnaire = questionnaire;
    }

}

以上代码是问题这个实体类。
这是一个双向关联,所以设置了mappedBy属性,并且外键设置在了多的一方,也就是问题这一方。


那么我们现在来看测试用例:

     @Test
     public void testCreateQuestionnaire(){
      Session session = sessionFactory.getCurrentSession();
      session.beginTransaction();

      Questionnaire qn = new Questionnaire();
      qn.setName("XXX公司3月转正考试");

      Question q1 = new Question();
      q1.setName("1、Java是一门OOP语言吗?A、是  B、不是");
      q1.setQuestionnaire(qn);

      Question q2 = new Question();
      q2.setName("2、Java是哪个公司的作品?A、SUN  B、MicroSoft");
      q2.setQuestionnaire(qn);

      qn.getQuestions().add(q1);
      qn.getQuestions().add(q2);

      session.saveOrUpdate(qn);

      session.getTransaction().commit();
     }

new一个问卷qn,设置了名字,new两个问题q1 q2,分别设置问题的题干内容,然后建立他们之间的关联,将q1 q1所属的问卷设置为qn,将qn的下属问题列表添加q1 q2进来。那么问题来了,我在保存实体的时候,我只保存qn,我希望Hibernate裙带关系帮我把连带的q1 q2也持久化进数据库,这就需要级联操作,我们看现在不设置级联会出现什么情况。

Hibernate: 
    insert 
    into
        t_questionnaire
        (name, answers) 
    values
        (?, ?)

没报错,但是只insert into了问卷,没有帮我把连带的问题也持久化进去。那么要解决此问题,我们必须设置级联关系。实际情况是,在保存问卷的时候,希望连带保存问题,那么级联属性设置理当应该在问卷实体中的问题列表属性上。代码如下:

    @OneToMany(mappedBy="questionnaire",cascade={CascadeType.ALL})
    public List<Question> getQuestions() {
        return questions;
    }

用cascade属性设置了级联关系,cascade属性的取值是一个数组,用大括号阔起来,CascadeType是一个枚举型,取值有5种:
ALL:全部操作都会级联,包括增,删,改
PERSIST:在持久化也就是保存的时候产生级联
DELETE:在删除时候产生级联删除
最常用的就是这三个,另外还有两种取值:
MERGE:在合并时产生级联
REFRESH:刷新时候级联
这里我们用到ALL。下面看结果:

Hibernate: 
    insert 
    into
        t_questionnaire
        (name, answers) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        t_question
        (name, questionnaireId) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        t_question
        (name, questionnaireId) 
    values
        (?, ?)

保存了问卷和两个问题,并且设置上了问题所属的问卷。


刚刚是保存问卷,我希望连带保存问题,那么反过来呢?

     @Test
     public void testCreateQuestion(){
      Session session = sessionFactory.getCurrentSession();
      session.beginTransaction();

      Questionnaire qn = new Questionnaire();
      qn.setName("XXX公司3月转正考试");

      Question q1 = new Question();
      q1.setName("1、Java是一门OOP语言吗?A、是  B、不是");
      q1.setQuestionnaire(qn);

      Question q2 = new Question();
      q2.setName("2、Java是哪个公司的作品?A、SUN  B、MicroSoft");
      q2.setQuestionnaire(qn);

      session.saveOrUpdate(q1);
      session.saveOrUpdate(q2);

      session.getTransaction().commit();

new了一份问卷qn,new了两个问题q1 q2,设置上了q1 q2所属的问卷为qn,我保存q1 q1的时候希望连带把qn保存进去,我们看结果:

org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing

报错了,TransientObjectException是说透明的对象异常:对象引用了一个未保存的透明的对象,也就是说,我的问卷没有保存,而问题设置了所属问卷,保存时候就会报错,关于Hibernate的三种状态在这里顺便提一句:
第一种:transient,透明的状态,也就是new出来的对象,在Hibernate看来称之为transient。
第二种:persist,持久化的,也就是save进数据库了就称之为该状态
第三种:detached,托管的,也就是session关闭后,称之为托管态。
那么要解决该异常,就需要在问题这一方添加级联,代码如下:

@ManyToOne(cascade={CascadeType.ALL})
    @JoinColumn(name="questionnaireId")
    public Questionnaire getQuestionnaire() {
        return questionnaire;
    }

下面我们看结果:

Hibernate: 
    insert 
    into
        t_questionnaire
        (name, answers) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        t_question
        (name, questionnaireId) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        t_question
        (name, questionnaireId) 
    values
        (?, ?)

OK,没问题,那么下一篇介绍加载时候的机制。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值