Hibernate 对象三种状态详解

本节内容:
大纲:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190407151628755.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDA3MTQwOA==,size_16,color_FFFFFF,t_7Hibernate 定义并支持下列对象状态:
瞬时(Transient) — 由 new 操作符创建,且尚未与Hibernate Session 关联的对象被认定为瞬时(Transient)的。瞬时(Transient)对象不会被持久化到数据库中,也不会被赋予持久化标识(identifier)。 如果瞬时(Transient)对象在程序中没有被引用,它会被垃圾回收器(garbage collector)销毁。 使用 Hibernate Session可以将其变为持久(Persistent)状态。(Hibernate会自动执行必要的SQL语句)

持久(Persistent) — 持久(Persistent)的实例在数据库中有对应的记录,并拥有一个持久化标识(identifier)。 持久(Persistent)的实例可能是刚被保存的,或刚被加载的,无论哪一种,按定义,它存在于相关联的Session作用范围内。 Hibernate会检测到处于持久(Persistent)状态的对象的任何改动,在当前操作单元(unit of work)执行完毕时将对象数据(state)与数据库同步(synchronize)。 开发者不需要手动执行UPDATE。将对象从持久(Persistent)状态变成瞬时(Transient)状态同样也不需要手动执行 DELETE 语句。

脱管(Detached) — 与持久(Persistent)对象关联的Session被关闭后,对象就变为脱管(Detached)的。对脱管(Detached)对象的引用依然有效,对象可继续被修改。脱管(Detached)对象如果重新关联到某个新的 Session 上, 会再次转变为持久(Persistent)的(在Detached其间的改动将被持久化到数据库)。 这个功能使得一种编程模型,即中间会给用户思考时间(user think-time)的长时间运行的操作单元(unit of work)的编程模型成为可能。我们称之为应用程序事务,即从用户观点看是一个操作单元(unit of work)。

Hibernate对象三种状态转换图:
在这里插入图片描述
接下来我们来细致地讨论下状态(states)及状态间的转换(state transitions)(以及触发状态转换的 Hibernate 方法)。
1.Transient

public void test1() {
		Author author = new Author();
		author.setAname("张三");
		//以上的author就是Transient瞬时状态,在调用save方法后就变为Persistent(持久化对象)。
		session.save(author);
		tx.commit();
		session.close();
	}

sql:

Hibernate:
select
seq_author.nextval
from
dual
Hibernate:
insert
into
t_author
(aname, aid)
values
(?, ?)

特点:
1.不和 Session 实例关联
2. 在数据库中没有和瞬时对象关联的记录
但是只要保存后就会转换为Persistent状态;

2.Persistent
例子:

	public void test2() {
		//创建一个瞬时状态的对象book
		Book book = new Book();
		book.setBookname("book1");
		session.save(book);
		//执行save之后,被session所管理,而且,数据库中已经存在,此时就是Persistent状态
		book.setBookname("book");
		//此时book是持久化状态,已经被session所管理,当在提交时,会把session中的对象和目前的对象进行比较
        //如果两个对象中的值不一致就会继续发出相应的sql语句,一条做插入,一条更新
		tx.commit();
	}

sql:

Hibernate:
select
seq_book.nextval
from
dual
Hibernate:
insert
into
t_book
(bookname, bid)
values
(?, ?)
Hibernate:
update
t_book
set
bookname=?
where
bid=?

在调用了save方法后,此时book已经是持久化对象了,被保存在了session缓存当中,这时book又重新修改了属性值,那么在提交事务时,此时hibernate就会拿当前这个book对象和保存在session缓存中的user对象进行比较,如果两个对象相同,则不会发送update语句,否则,如果两个对象不同,则会发出update语句。

再来看下个例子:

public void test3() {
		//创建一个瞬时状态的对象book
		Book book = new Book();
		book.setBookname("book2");
		session.save(book);
		//执行save之后,被session所管理,而且,数据库中已经存在,此时就是Persistent状态
		book.setBookname("book3");
		//没有意义的语句
		session.save(book);
		book.setBookname("Book");
		//没有意义的语句
		session.save(book);
		tx.commit();
	}

sql:

Hibernate:
select
seq_book.nextval
from
dual
Hibernate:
insert
into
t_book
(bookname, bid)
values
(?, ?)
Hibernate:
update
t_book
set
bookname=?
where
bid=?

从sql中可以看出,无论你保存多少次,其实他都只是在提交的过成中与session中的对象进行对比,如果有变化,就执行一条插入语句和一条更新语句,如果没有变化,那么只执行插入语句。

再看下个例子:

public void test4() {
		//此时book就是持久化对象
		Book book = (Book) session.load(Book.class, 22);
		book.setBookname("newbook");
		tx.commit();
	}

sql:

Hibernate:
select
book0_.bid as bid1_1_,
book0_.bookname as bookname1_1_,
author1_.aid as aid0_0_,
author1_.aname as aname0_0_
from
t_book book0_,
t_author author1_
where
book0_.bid=author1_.aid(+)
and book0_.bid=?
Hibernate:
update
t_book
set
bookname=?
where
bid=?

我们来看看此时会发出多少sql语句呢?记住:当session调用load、get方法时,此时如果数据库中有该对象,则该对象也变成了一个持久化对象,被session所托管。因此,这个时候如果对对象进行操作,在提交事务时同样会去与session中的持久化对象进行比较,因此这里会发送两条sql语句。

再看下个例子:

public void test5() {
		//此时book就是持久化对象
		Book book = (Book) session.load(Book.class, 22);
		book.setBookname("newbook2");
		session.clear();//清除session
		tx.commit();
	}

sql:

Hibernate:
select
book0_.bid as bid1_1_,
book0_.bookname as bookname1_1_,
author1_.aid as aid0_0_,
author1_.aname as aname0_0_
from
t_book book0_,
t_author author1_
where
book0_.bid=author1_.aid(+)
and book0_.bid=?

调用session.clear()方法,这个时候就会将session的缓存对象清空,那么session中就没有了book这个对象,这个时候在提交事务的时候,发现已经session中已经没有该对象了,所以就不会进行任何操作,因此这里只会发送一条查询语句。

3.Detached
例子:

public void test6() {
		//此时book就是持久化对象
		Book book = new Book();
		book.setBookname("book6");
		book.setBid(1);
		session.save(book);
		tx.commit();
		System.out.println("此时book的id为:"+book.getBid());
	}

sql:

Hibernate:
select
seq_book.nextval
from
dual
Hibernate:
insert
into
t_book
(bookname, bid)
values
(?, ?)

控制台:

此时book的id为:25

当调用了book.setBid(1);时,此时book是一个瞬时的对象,因为数据库中存在bid=1的这个对象,但是该对象又没有被session所托管,所以这个对象就是瞬时的对象,要使瞬时对象变成一个持久化的对象,应该调用什么方法呢?我们知道调用save方法,可以将一个对象变成一个持久化对象,但是,当save一执行的时候,此时hibernate会根据id的生成策略往数据库中再插入一条数据,所以如果调用save方法,此时hibernate会发送一条插入语句。而且,把book的的bid改成了和数据库一样的bid。

public void test7() {
		//此时book就是托管化对象,在数据库中有这个对象,只是sesion中没有
		Book book = new Book();
		book.setBookname("book6");
		book.setBid(21);
		//会发送一个sql语句,执行完成后,book对象转换成持久状态
		session.update(book);
		tx.commit();
	}

sql:

Hibernate:
update
t_book
set
bookname=?
where
bid=?

我们想将一个对象,从游离状态转换成持久状态,要调用session.update(obj)方法。

再看下一个例子:

public void test8() {
		// 此时book就是托管化对象,在数据库中有这个对象,只是sesion中没有
		Book book = new Book();
		book.setBookname("bookx");
		book.setBid(21);
		/*
		 * 会发送一个sql语句,如果,book设置了id那么将会执行一个update的方法,
		 * 如果没有设置id,那么此时book就是一个瞬时的对象,将会执行save方法, 将book保存到数据库中,book在执行完之后将会变成一个持久状态
		 */
		session.saveOrUpdate(book);
		tx.commit();
		System.out.println(book.getBookname());
	}

sql:

Hibernate:
update
t_book
set
bookname=?
where
bid=?

我们将book.setBid(21);去除,查看sql:

Hibernate:
select
seq_book.nextval
from
dual
Hibernate:
insert
into
t_book
(bookname, bid)
values
(?, ?)

这里将book直接insert进数据库中。

如果对象设置了ID值,例如u.setId(4),那么该对象会被假设当作一个离线对象,此时就会执行update操作。

再看下个例子:

public void test9() {
		// 此时book就是托管化对象,在数据库中有这个对象,只是sesion中没有
		Book book = (Book) session.load(Book.class, 41);
		System.out.println("书名:"+book.getBookname());
		Book book2 = new Book();
		book2.setBid(41);
		book2.setBookname("bookx");
		session.saveOrUpdate(book2);
		tx.commit();
	}

sql:

Hibernate:
select
book0_.bid as bid1_1_,
book0_.bookname as bookname1_1_,
author1_.aid as aid0_0_,
author1_.aname as aname0_0_
from
t_book book0_,
t_author author1_
where
book0_.bid=author1_.aid(+)
and book0_.bid=?

控制台:

org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [hibernatesql.demo.eg1entity.Book#41]

此时我们的book已经是持久化的对象了,保存在session缓存中,book2通过调用saveOrUpdate方法后也变成了一个持久化的对象,此时也会保存在session缓存中,这个时候session缓存中就存在了一个持久化对象有两个引用拷贝了,这个时候hibernate就会报错。一个session中不能存在对一个持久化对象的双重引用的,要解决这个方法,我们这里又要介绍session的另一个方法 merge方法,这个方法的作用就是解决一个持久化对象两分拷贝的问题,这个方法会将两个对象合并在一起成为一个对象。我们将session.saveOrUpdate(book2);改为session.merge(book2);就可以了;

总结:
在这里插入图片描述


您可以通过点击 文章下方的输入框 来对文章内容作出评价, 也可以通过左上方的 关注按钮 来关注我的博客的最新动态。

如果文章内容对您有帮助, 不要忘记点击右上角的 喜欢按钮 来支持一下哦 !

如果您对文章内容有任何疑问, 可以通过评论方式联系我;

如果需要转载,请注明出处,谢谢!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值