一、持久对象生命周期
应用程序在使用Hibernate框架后,创建的持久对象会经历一整套生命周期来完成数据库的操作,其中主要的三个状态分别是瞬态(Transient)、持久化(Persistent)、脱管(detached)。这三种状态的转换是能够在应用程序中控制的,如下图:
为了能清楚的了解这几种状态,这里使用一个实例来查看下这几种状态下对象的不同,下面状态内的代码,具体步骤如下:
(1)创建Hibernate_session程序集,并添加像相应的jar包;
(2)配置Hibernate,添加相应的实体User类,及它的映射文件,并配置好相应的数据库连接;
User类文件的映射文件User.hbm.xml代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<? xml version = "1.0" ?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2014-4-30 15:39:33 by Hibernate Tools 3.4.0.CR1 -->
< hibernate-mapping >
< class name = "com.hibernate.User" >
< id name = "id" >
< generator class = "uuid" />
</ id >
< property name = "name" />
< property name = "password" />
< property name = "createTime" />
< property name = "expireTime" />
</ class >
</ hibernate-mapping >
|
Hibernate数据库连接配置代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<? xml version = "1.0" encoding = "UTF-8" ?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
< hibernate-configuration >
< session-factory >
< property name = "hibernate.connection.driver_class" >com.mysql.jdbc.Driver</ property >
< property name = "hibernate.connection.url" >jdbc:mysql://localhost:3306/hibernate_session</ property >
< property name = "hibernate.connection.username" >root</ property >
< property name = "hibernate.connection.password" >ab12</ property >
<!-- dialect:方言,封装的底层API,类似于Runtime,将数据库转换为配置中的相应的语言 -->
< property name = "hibernate.dialect" >org.hibernate.dialect.MySQLDialect</ property >
< mapping resource = "com/hibernate/User.hbm.xml" />
</ session-factory >
</ hibernate-configuration >
|
(3)添加静态成员sessionfactory的公共类,用来创建一个SessionFactory及其Session对象;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
package com.hibernate;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class session {
private static SessionFactory factory; //声明静态局部变量SessionFactory,数据库镜像
static {
try {
//创建并获取配置数据库的配置文件,默认获取hibernate.cfg.xml
Configuration cfg= new Configuration().configure();
factory=cfg.buildSessionFactory(); //构建一个数据库镜像
} catch (Exception e){
e.printStackTrace(); //打印错误信息
}
}
public static Session getSession(){
return factory.openSession(); //返回创建的session对象
}
public static SessionFactory getSessionFactory(){
return factory; //返回相应的SessionFactory
}
//关闭session对象
public static void closeSession(Session session){
if (session != null ){
if (session.isOpen()){
session.close();
}
}
}
}
|
(4)添加一个Source Folder,并在该文件夹内添加名称为com.hibernate的package包,并在包中添加一个名称为SessionTest的类文件。
1
2
3
4
5
6
7
8
9
10
11
12
|
package com.hibernate;
import java.util.Date;
import junit.framework.TestCase;
import org.hibernate.Session;
import org.hibernate.Transaction;
public class SessionTest extends TestCase {
}
|
二、状态转化方法
1、对象直接进入Persistent状态
1.1 get方法
从数据库中获取一行信息,并将该信息同步到创建的对象中,该方法返回一个Object对象,如果没有查询到内容则返回null。下面的实例通过采用Session的get方法来获取一个对象,并将对象转换为实例。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
public void testGet1(){
Session session= null ;
Transaction tx = null ;
try {
session=HibernateUtils.getSession();
//开启事务
tx= session.beginTransaction();
//get加载上来的对象为持久对象
//执行get会马上发出查询语句,如果不存在会返回null
User user=(User)session.get(User. class , "ff80808145bc28cc0145bc28ce020002" );
System.out.println(user.getName());
//persistent状态
//persistent状态的对象,当对象的属性发生改变的时候
//Hibernate在清理缓存(脏数据检查)的时候,会和数据库同步
user.setName( "赵柳" );
session.getTransaction().commit();
} catch (Exception e){
e.printStackTrace();
if (tx != null ){
tx.rollback();
}
} finally {
HibernateUtils.closeSession(session);
}
}
|
设置断点,获取User对象。
获取到了该对象,通过强制转换后得到了一个user对象。程序中添加了setName方法,也就是说会更新数据库中的名称,执行完成后检查数据库,如下图更新结果。
1.2 load方法
功能类似于get方法,也是从数据库中获取数据并同步到对象中,该方法支持lazy是一种懒汉操作,它返回的是一个持久化的Object对象或者一个代理,所以需要进行转化。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
public void testLoad1(){
Session session= null ;
try {
session=HibernateUtils.getSession();
//不会马上查询语句,因为load支持lazy(延迟加载/懒加载)
//什么教lazy?只有真正使用这个对象的时候,再创建,对于Hibernate来说
//才真正发出查询语句,主要为了提高性能,lazy是Hibernate中非常重要的特性
//Hibernate的lazy是如何实现的?采用代理对象实现,代理对象主要采用的是CGLIB库生成的
//而不是JDK的动态代理,因为JDK的动态代理只能对实现了借口的类生成代理,CGLIB可以对类生成
//代理,它采用的是继承方式
User user=(User)session.load(User. class , "8a1b653745bcc7b50145bcc7b7140001" );
System.out.println(user.getName());
//persistent状态
//persistent状态的对象,当对象的属性发生改变的时候
//Hibernate在清理缓存(脏数据检查)的时候,会和数据库同步
user.setName( "zhaoliu" );
session.getTransaction().commit();
} catch (Exception e){
e.printStackTrace();
} finally {
HibernateUtils.closeSession(session);
}
}
|
查询获取该User对象如下图:
分析上图,获取的User对象并不完整,或者说并没有常见一个User对象,更是一种代理,它使用了CGLIB来预加载对象,只有在使用该对象时才真正创建。
1.3 Get Vs load
get和load方法很重要,在面试Hibernate时经常会考到,下面对比下两者。
相同点:
(1)功能相同,将关系数据转化为对象;
(2)使用方法相同,同样需要制定两个参数
不同点:
(1)load方法支持lazy操作,预加载对象,在使用时才创建,get是直接将关系数据转化为对象;
(2)load加载对象如果不存在会抛出objectNotFoundException异常,get如果没有获取数据会返回null。
2、手动构造detached对象
想要获取对象还有另外一种方法,它区别于get与load方法,是一种手动获取的方法,首先常见一个对象,然后通过制定id的方式获取该对象的数据,方法如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public void testUer(){
Session session= null ;
try {
session=HibernateUtils.getSession();
session.beginTransaction();
//手动构造detached对象
User user= new User();
user.setId( "8a1b653745bcc7b50145bcc7b7140001" );
//persistent状态
//persistent状态的对象,当对象的属性发生改变的时候
//Hibernate在清理缓存(脏数据检查)的时候,会和数据库同步
session.getTransaction().commit();
} catch (Exception e){
e.printStackTrace();
} finally {
HibernateUtils.closeSession(session);
}
}
|
查看获取的结果图:
分析结果图,代码中使用了setId方法为该对象制定了id号,在制定id号后就能够对该对象进行操作,在事务提交后同步到数据库中,采用了手动指定,手动指定了对象的信息。
2.1 Delete方法
删除数据库中指定的对象,在删除前必须将对象转化到Persistent状态,可以使用get、load或者手动的方法指定对象,使用方法如下代码:
1
2
3
4
5
|
session=HibernateUtils.getSession();
session.beginTransaction();
User user=(User)session.load(User. class , "8a1b653745bcc6d50145bcc6d67a0001" );
//建议采用此种方式删除,先加载再删除
session.delete(user);
|
2.2 Update
更新数据,该方法会修改数据库中的数据。在使用的时候会出现量中情况,更新数据库某个字段值或者更新数据库的整行值
2.2.1 更新某个字段值
如果只想要更新某个字段的值,在update前,需要使用load或者get方法使对象转化为persistent状态代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
//获取session对象
session=HibernateUtils.getSession();
//开启事务
session.beginTransaction();
//或者可以使用另外的方法开启
//session.getTransaction().begin();
//加载获取User对象
//方法一:使用load方法
//User user=(User)session.load(User.class, "8a1b653745bcc7b50145bcc7b7140001");
//方法二:手动获取
User user= new User();
user.setId( "8a1b653745bcc7b50145bcc7b7140001" );
//更新姓名
user.setName( "zhangsan" );
session.update(user);
session.getTransaction().commit();
|
2.2.2 更新整行
想要更新整行的数据,可以采用手动将状态转换到detached状态,手动指定对象的id值,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
//获取session对象
session=HibernateUtils.getSession();
//开启事务
session.beginTransaction();
//或者可以使用另外的方法开启
//session.getTransaction().begin();
//手动获取
User user= new User();
user.setId( "8a1b653745bcc7b50145bcc7b7140001" );
//更新姓名
user.setName( "zhangsan" );
session.update(user);
session.getTransaction().commit();
|
查看更新结果:
分析更新结果,它其实更新了数据库的整行数据,这种更新操作有太多的不确定因素,不建议使用。
2.3 save方法
插入数据。在执行save方法时会调用数据库的insert语句,向数据库中添加新的一行。save后的对象会转化为持久态,在此状态下的对象能够再次更新对象,在最后提交事务时会同更改更新到数据库。如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
public void testSave2(){
Session session= null ;
Transaction tx = null ;
try {
session=HibernateUtils.getSession();
//开启事务
tx= session.beginTransaction();
//Transient状态
User user= new User();
user.setName( "zhangsi" );
user.setPassword( "123" );
user.setCreateTime( new Date());
user.setExpireTime( new Date());
//persistent状态
//persistent状态的对象,当对象的属性发生改变的时候
//Hibernate在清理缓存(脏数据检查)的时候,会和数据库同步
session.save(user);
user.setName( "lisi" );
tx.commit();
} catch (Exception e){
e.printStackTrace();
if (tx != null ){
tx.rollback();
}
} finally {
HibernateUtils.closeSession(session);
}
//detached状态
}
|
查看上例运行结果视图:
分析结果:session在提交事务的时候其实做了两部的操作,结合代码中的更新过程,首先是新增了一个User对象,之后执行了save操作,它会调用insert语句,然后在代码中做了一个setName的操作,重新修改了名称,但这时还没有同步到数据库中而是在内存中,这时就会有两种状态,我们称此时的数据位脏数据,最后提交事务的时候更新到数据库中。