Hibernate之对象的三种状态

来源:互联网 发布:一分洗车软件 编辑:程序博客网 时间:2024/05/16 18:18
hibernate有三种状态,transient(瞬时状态),persistent(持久化状态)以及detached(离线状态)。

瞬时状态就是刚new出来一个对象,还没有被保存到数据库中;

持久化状态就是已经被保存到数据库中;

离线状态就是数据库中有,但是session中不存在该对象。

后续将有大量测试用例,操作hibernate对象要用到session,每次都要加载hibernate.cfg.xml文件、创建SessionFactory对象、创建Session对象、关闭session,步骤都是一样的,这里就新建一个HibernateUtil工具类来提高代码的复用性。

HibernateUtil.java

此版本适用于Hibernate5的getCurrentSession,如果要用Hibernate3或Hibernate4或者将getCurrentSession换成openSession,可参考:http://blog.csdn.net/qinshijangshan/article/details/53314729进行配置。

package com.ack.core;import org.hibernate.Session;import org.hibernate.SessionFactory;import org.hibernate.boot.MetadataSources;import org.hibernate.boot.registry.StandardServiceRegistry;import org.hibernate.boot.registry.StandardServiceRegistryBuilder;public class HibernateUtil {private static SessionFactory sessionFactory;static{try{StandardServiceRegistry  serviceRegistry = new StandardServiceRegistryBuilder().configure().build();        sessionFactory=new MetadataSources(serviceRegistry).buildMetadata().buildSessionFactory();}catch(Exception e){    e.printStackTrace();    }}//获得开启着的Sessionpublic static Session getCurrentSession(){    return sessionFactory.getCurrentSession();    }    public static SessionFactory getSessionFactory(){    return sessionFactory;    }}

hibernate配置、实体类和类的映射文件就不再给出,可参考:hibernate环境和简单的测试用例http://blog.csdn.net/qinshijangshan/article/details/53312213

此篇实例参考自:http://www.cnblogs.com/xiaoluo501395377/p/3380270.html

1.TestTransient

public void TestTransient(){Session session = HibernateUtil.getCurrentSession();session.beginTransaction();User user = new User();user.setUserName("陈大大");user.setStatus("0");user.setBirthday(new Date());//此时user就是一个Transient(瞬时状态),此时user并没有被session进行托管,即在session的缓存中还不存在user这个对象session.save(user);//当执行完save方法后,此时user被session托管,并且数据库中存在了该对象    //user就变成了一个Persistent(持久化对象)    session.getTransaction().commit();}

save方法后立即进行insert操作插入一条数据到数据库,随即瞬时状态对象变成持久化状态对象,数据在事务提交之后才真正存在于数据库(事务不提交只是执行了sql语句,数据库存在该对象,数据还未写入,这是数据库的事务管理,暂且不管);

2.TestPersistent01

public void TestPersistent01(){Session session = HibernateUtil.getCurrentSession();session.beginTransaction();User user = new User();user.setUserName("陈大大");user.setStatus("0");user.setBirthday(new Date());//以上u就是Transient(瞬时状态),表示没有被session管理,并且数据库中没有session.save(user);//执行save之后,执行insert sql语句,数据库中已经存在user对象;//瞬时状态对象变为持久化对象,被session所管理;System.out.println("================= User SAVE =================");user.setUserName("陈大牙");System.out.println("================= User UPDATE =================");//比较set值与当前session中user对象的值userName已经改变,发送update sql语句    session.getTransaction().commit();    System.out.println("================= Transaction COMMIT =================");}

从方法内容执行顺序以及 insert sql语句在User SAVE之前,User SAVE和User UPDATE上下行紧挨着,update sql语句没在User SAVE和User UPDATE之间执行,在User UPDATE之后(或者说是事务提交时)才执行的update sql语句,为什么不在改变userName值之后。

3.TestPersistent02

public void TestPersistent02(){Session session = HibernateUtil.getCurrentSession();session.beginTransaction();User user = new User();user.setUserName("陈大大");user.setStatus("0");user.setBirthday(new Date());//以上u就是Transient(瞬时状态),表示没有被session管理,并且数据库中没有session.save(user);//执行save之后,执行insert sql语句,数据库中已经存在user对象;//瞬时状态对象变为持久化对象,被session所管理;System.out.println("================= User SAVE =================");user.setUserName("陈大大");System.out.println("================= User UPDATE =================");//如果参照TestPersistent01那么这里也就会执行相应的update sql语句,实际没有,因为userName的值未改变    session.getTransaction().commit();    System.out.println("================= Transaction COMMIT =================");}

TestPersistent02区别于TestPersistent01为save()后的set值相对于session中的user对象未改变;

TestPersistent02持久化状态时set的值与session中对象的值相同(未改变),所以这就是TestPersistent01在User UPDATE和Transaction之间执行了update sql语句,而TestPersistent02未执行的原因;

到这里我们还未明白为什么update语句为什么不是在User SAVE和User UPDATE之间执行。

猜测:持久化态的对象后面操作会在事务提交时判断session中对象的值是否有改变,决定是否进行后续数据库操作。

4.TestPersistent03

public void TestPersistent03(){Session session = HibernateUtil.getCurrentSession();session.beginTransaction();User user = new User();user.setUserName("陈大大");user.setStatus("0");user.setBirthday(new Date());//以上u就是Transient(瞬时状态),表示没有被session管理,并且数据库中没有    //执行save之后,被session所管理,而且,数据库中已经存在,此时就是Persistent状态session.save(user);System.out.println("================= User SAVE =================");//此时u是持久化状态,已经被session所管理,当在提交时,会把session中的对象和目前的对象进行比较    //如果两个对象中的值不一致就会继续发出相应的sql语句user.setUserName("陈大大");session.update(user);System.out.println("================= User UPDATE1 =================");user.setLoginName("chensan");session.update(user);System.out.println("================= User UPDATE2 =================");session.getTransaction().commit();    System.out.println("================= Transaction COMMIT =================");}

TestPersistent03在TestPersistent02的基础上增加了User UPDATE1到事务提交前的语句(session中的user对象的loginName值相对于之前改变了)。

结果insert sql语句后,User SAVE、User UPDATE1和User UPDATE2上下行挨着,在User UPDATE2和Transaction COMMIT之间才执行了update sql语句。看来是比较了session中user对象的值前后是否有改变来决定是否需要执行相应sql语句的;

那么对应有修改是不是次次都会执行sql语句呢?继续看下一个例子

5.TestPersistent04

public void TestPersistent04(){Session session = HibernateUtil.getCurrentSession();session.beginTransaction();User user = new User();user.setUserName("陈大大");user.setStatus("0");user.setBirthday(new Date());//以上u就是Transient(瞬时状态),表示没有被session管理,并且数据库中没有session.save(user);//执行save之后,执行insert sql语句,数据库中已经存在user对象;//瞬时状态对象变为持久化对象,被session所管理;System.out.println("================= User SAVE =================");user.setUserName("陈大牙");session.update(user);System.out.println("================= User UPDATE1 =================");user.setLoginName("chensan");session.update(user);System.out.println("================= User UPDATE2 =================");session.getTransaction().commit();    System.out.println("================= Transaction COMMIT =================");}

insert之后userName和loginName分别修改提交,可之后只在User UPDATE2和Transaction COMMIT之间发送了一条update sql语句;

我们也就知道了:在对象成了持久化状态对象后,后续操作会在事务提交时根据session中持久化对象的值是否改变来确定是否进行数据库操作,不是只要update一次就会进行数据库操作;

6.TestPersistent05

public void TestPersistent05(){Session session = HibernateUtil.getCurrentSession();session.beginTransaction();User user = new User();user.setUserName("陈大大");user.setStatus("0");user.setBirthday(new Date());//以上u就是Transient(瞬时状态),表示没有被session管理,并且数据库中没有session.save(user);//执行save之后,执行insert sql语句,数据库中已经存在user对象;//瞬时状态对象变为持久化对象,被session所管理;System.out.println("================= User SAVE =================");user.setUserName("陈大牙");session.update(user);System.out.println("================= User UPDATE1 =================");user.setUserName("陈大大");session.update(user);System.out.println("================= User UPDATE2 =================");session.getTransaction().commit();    System.out.println("================= Transaction COMMIT =================");}

userName修改后update,再修改再update,实际userName到最后值并未改变,所以可以预见:User UPDATE2和Transaction COMMIT之间也不会去有什么update sql语句了。

结论:如果一个对象是持久化状态了,这个持久化状态的对象将由session托管。此后对该对象进行各种修改,或者调用多次update、save方法 都不会进行数据库操作。只有当事物提交的时候,hibernate才会比较session中的持久化对象的属性最终的值是否有改变来确定是否发送相应的sql语句来进行数据库操作。

瞬时状态对象遇到save、saveOrUpdate、update就会变成持久化对象。

注:

1.持久化对象由session管理,都是同一个对象它是怎么比的。

2.看前面例子有的set属性值并没有update()却实现了update()的作用。

7.TestPersistent06

public void TestPersistent06(){Session session = HibernateUtil.getCurrentSession();session.beginTransaction();User user = session.get(User.class, 1);System.out.println("================= User GET =================");user.setLoginName("chensan");System.out.println("================= User UPDATE =================");session.getTransaction().commit();System.out.println("================= Transaction COMMIT =================");}

get()方法获取数据库中id=1的user对象,在User GET之前执行了select sql语句;user对象的属性loginName的值有修改,在User UPDATE和Transaction COMMIT之间执行了update语句;说明get()方法使得离线对象变成了持久化对象;
执行第二次,那么也就不会发送update sql语句了,因为值未变;

当然id=1的数据记录不存在,那么get()就会报错,Done entity load : com.ack.hibernate.User#2,空指针异常;

8.TestPersistent07

public void TestPersistent07(){Session session = HibernateUtil.getCurrentSession();session.beginTransaction();User user = session.load(User.class, 1);System.out.println("================= User GET =================");user.setLoginName("cs");System.out.println("================= User UPDATE =================");session.getTransaction().commit();System.out.println("================= Transaction COMMIT =================");}

load()后select sql语句,之后打印User GET,在User UPDATE和Transaction COMMIT之间执行update sql语句。

执行第二次,值未修改,也是未执行update sql语句。

9.TestPersistent08

public void TestPersistent08(){Session session = HibernateUtil.getCurrentSession();session.beginTransaction();User user = session.get(User.class, 1);session.clear();System.out.println("================= User GET =================");user.setLoginName("chensan");System.out.println("================= User UPDATE =================");session.getTransaction().commit();System.out.println("================= Transaction COMMIT =================");}
clear()清空session后,user对象变成了瞬时状态对象;后面loginName改变值,在User UPDATE和Transaction COMMIT之间不发送update sql语句。

结论:get、load方法之后获取的数据库离线对象变成持久化对象。

因从数据库获取的对象,未set值的字段将不会修改;下面示例离线状态setId获取对象,对未set值的字段是采取清空方式。

TestDetached01

public void TestDetached01(){Session session = HibernateUtil.getCurrentSession();session.beginTransaction();User user = new User();user.setId(1);user.setStatus("1");//session.save(user);session.update(user);System.out.println("================= User UPDATE1 =================");user.setUserName("陈三");session.update(user);System.out.println("================= User UPDATE2 =================");session.getTransaction().commit();System.out.println("================= Transaction COMMIT =================");}

id=1的user对象在数据库中已存在,但此处是new一个对象在session中不存在,所以user对象为离线状态;
save方法根据hibernate主键生成策略,在数据库insert一条语句,而我们希望的是对id=1的user对象进行更新操作。所以离线对象不应该用save()而是update();

第一次update和第二次update都修改了user对象的值,然而只在User UPDATE2和Transaction COMMIT之间执行了一次update sql语句。那么到底什么时候离线状态的user对象持久化了,是在setId之后,还是update(user)的作用?

但值得注意的是,这种方式创建的离线状态对象,因为新建的对象没法获取数据库中的字段属性值,set值的时候需要对需要字段全部赋值,否则值为空。根据数据库默认值来对数值字段赋值为0,hibernate和数据库设置默认值哪个优先级会更高,未测试。

TestDetached02

public void TestDetached02(){Session session = HibernateUtil.getCurrentSession();session.beginTransaction();User user = new User();user.setId(1);user.setStatus("1");session.update(user);System.out.println("================= User UPDATE =================");user.setId(2);//session.update(user);session.getTransaction().commit();System.out.println("================= Transaction COMMIT =================");}

报异常:identifier of an instance of com.ack.hibernate.User was altered from 1 to 2

User UPDATE处离线对象已转为持久化对象,为持久化对象set主键为另一个值报异常。

TestDetached03

public void TestDetached03(){Session session = HibernateUtil.getCurrentSession();session.beginTransaction();User user = new User();user.setId(1);session.delete(user);//user变成了transient对象System.out.println("================= Transaction DELETE =================");user.setId(1);session.getTransaction().commit();System.out.println("================= Transaction COMMIT =================");}

delete操作后,离线状态的user变为瞬时对象,后续的操作将不进行数据库操作,只发送一条delete的sql语句;

TestDetached04

public void TestDetached04(){Session session = HibernateUtil.getCurrentSession();session.beginTransaction();User user = new User();user.setId(1);session.delete(user);//user变成了transient对象System.out.println("================= Transaction DELETE =================");user.setId(1);session.update(user);session.getTransaction().commit();System.out.println("================= Transaction COMMIT =================");}

报错:deleted instance passed to update(): [<null entity name>#<null>];

此处delete后离线状态变为瞬时态,不是set id的问题报错,set任何属性都一样会报错;

update用来更新detached对象,更新完成后转为Persistence对象;

update用来更新transient对象则会报错;

TestDetached05

public void TestDetached05(){Session session = HibernateUtil.getCurrentSession();session.beginTransaction();User user = new User();user.setId(1);session.update(user);session.delete(user);//user变成了transient对象System.out.println("================= Transaction DELETE =================");user.setId(1);session.getTransaction().commit();System.out.println("================= Transaction COMMIT =================");}

先将瞬时态user对象update更新为持久化对象,再对持久化user对象delete;
发送了两条语句,一条update语句,一条delete语句;并且两条语句在Transaction DELETE打印内容后两条sql语句紧挨着执行的;

很意外,应该是update语句是要在事务提交时才执行,后期需要考虑hibernate对象三种状态与语句执行情况的关系;

离线状态的user对象已不再session中,set属性值自然也就没有意义,不会去检测;

TestDetached06

public void TestDetached06(){Session session = HibernateUtil.getCurrentSession();session.beginTransaction();User user = new User();user.setId(1);user.setUserName("chensan");session.saveOrUpdate(user);session.getTransaction().commit();System.out.println("================= Transaction COMMIT =================");}

id=1的user对象存在于数据库,则对象为离线状态saveOrUpdate同update执行update操作;发送一条update的sql语句;

id=1的user对象不存在数据库,则会执行save操作,报错: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1; 这个异常是由于主键设置为自增长,而在我们插入记录的时候设置了ID的值导致的。

对于设置了自增长主键生成策略的,指定id就不适用了。猜想:应该改变主键生成策略,不让自增长,没有相应记录自然就能save了;

TestDetached07

public void TestDetached07(){Session session = HibernateUtil.getCurrentSession();session.beginTransaction();User user1 = session.load(User.class, 1);System.out.println("userName = "+user1.getUserName());user1.setUserName("chensan");User user2 = new User();user2.setId(1);user2.setUserName("cs");session.saveOrUpdate(user2);session.getTransaction().commit();System.out.println("================= Transaction COMMIT =================");}

报错:A different object with the same identifier value was already associated with the session : [com.ack.hibernate.User#1];

只知道是两个对象最终为表示同一个对象,具体的错误先不讨论,继续看看下一个例子:

public void TestDetached08(){Session session = HibernateUtil.getCurrentSession();session.beginTransaction();User user1 = session.load(User.class, 1);System.out.println("userName = "+user1.getUserName());user1.setUserName("chensan");User user2 = new User();user2.setId(1);user2.setUserName("cs");user2.setStatus("1");//session.saveOrUpdate(user2);//merge方法会判断session中是否已经存在同一个对象,如果存在就将两个对象合并session.merge(user2);session.getTransaction().commit();System.out.println("================= Transaction COMMIT =================");}

merge()将两个对象合并为一个对象,最后发送一条update的sql语句;

报错:A different object with the same identifier value was already associated with the session : [com.ack.hibernate.User#1];参考:http://www.blogjava.net/hrcdg/articles/157724.html基本可以解决问题;


0 0
原创粉丝点击