一滓鸠、Hibernate三種狀態(tài)的簡介
Hibernate中的實(shí)體對象有三種狀態(tài)但惶,下面讓我來介紹一下:
1.Transient(瞬時(shí)狀態(tài))
??既沒有ID阴汇,也沒有被session管理页响。也就是剛打開session非洲,創(chuàng)建了一個(gè)類,還沒執(zhí)行save()俯画,也就是數(shù)據(jù)庫中沒有析桥。在這種狀態(tài)中的對象,并沒有被session托管艰垂,在session緩存中還不存在這個(gè)對象泡仗。
User user = new User();
user.setUsername("ada");
user.setPassword("123");
user.setCreateDate(new Date());
2.Persistent(持久化狀態(tài))
??當(dāng)執(zhí)行了save、update等操作之后猜憎,這個(gè)對象就會被session所管理娩怎,被管理之后就稱為Persistent。如果對象處于持久態(tài)胰柑,那么在session的緩沖中會存著一份該對象的拷貝截亦,這份拷貝被session所管理爬泥。
//當(dāng)執(zhí)行完save之后會被session管理
session.save(u);
//當(dāng)被session所管理之后,只要該對象的值被修改并且在事務(wù)提交時(shí)會自動將值完成修改
u.setAge(18);
//此處沒有調(diào)用update但是user也會調(diào)用一條update語句完成修改
//這個(gè)時(shí)候用save崩瓤、update方法都是沒有意義的
session.getTransaction().commit();
所以我們要記住一點(diǎn):如果一個(gè)對象是持久態(tài)的話袍啡,那么此時(shí)對對象進(jìn)行各種修改,或者調(diào)用多次update谷遂、save方法葬馋,hibernate都不會發(fā)送sql語句。只有當(dāng)事務(wù)提交時(shí)肾扰,此時(shí)hibernate才會拿當(dāng)前這個(gè)對象與之前保存在session中的持久化對象進(jìn)行比較畴嘶,如果不相同就發(fā)送一條update的sql語句,否則就不會發(fā)送update語句集晚。
??當(dāng)session調(diào)用get窗悯、load方法時(shí),如果數(shù)據(jù)庫中有該對象偷拔,則該對象也會變成一個(gè)持久化對象蒋院,被session所托管。因此莲绰,這個(gè)時(shí)候如果對對象進(jìn)行操作欺旧,在提交事務(wù)時(shí)同樣會去與session中的持久化對象進(jìn)行比較。
session = HibernateUtil.openSession();
session.beginTransaction();
//此時(shí)u是Persistent
User u = (User)session.load(User.class, 4);
//由于u這個(gè)對象和session中的對象不一致蛤签,所以會發(fā)出sql完成更新
session.getTransaction().commit();
特別注意辞友,如果我們想要修改一個(gè)持久化對象的主鍵的話,就會報(bào)錯(cuò)震肮,下面看一個(gè)栗子:
session.beginTransaction();
User u = new User();
u.setId(5);
//完成update之后也會變成持久化狀態(tài)
session.update(u);
u.setPassword("lisi");
u.setUsername("lisi");
//會拋出異常
u.setId(333);
session.getTransaction().commit();
這個(gè)時(shí)候称龙,hibernate會報(bào)錯(cuò),因?yàn)槲覀兊膗當(dāng)前已經(jīng)是一個(gè)持久化對象戳晌,如果試圖修改一個(gè)持久化對象的主鍵的值的話鲫尊,就會拋出異常,這點(diǎn)要特別注意沦偎。
org.hibernate.HibernateException: identifier of an instance of com.xiaoluo.bean.User was altered from 5 to 333
3.Detached(離線狀態(tài))
??離線狀態(tài)指數(shù)據(jù)庫中存在但是沒有被session所管理疫向。
User user = new User();
user.setUsername("ada");
user.setPassword("123");
user.setCreateDate(new Date());
//當(dāng)我們進(jìn)行了user.setId(11)后,u就變成了離線狀態(tài)
//因?yàn)閿?shù)據(jù)庫中存在id=11的這個(gè)對象豪嚎,但是該對象又沒有被session所托管
user.setId(11);
session.save(u);
要注意鸿捧,對于離線狀態(tài)的對象執(zhí)行save時(shí)會忽略狀態(tài)而直接進(jìn)行insert操作,當(dāng)save一執(zhí)行的時(shí)候疙渣,此時(shí)hibernate會根據(jù)id的生成策略往數(shù)據(jù)庫中再插入一條數(shù)據(jù):
Hibernate: insert into t_user (username, password, createDate) values (?, ?, ?)
所以對于離線對象,如果要使其變成持久化對象的話堆巧,我們不能使用save方法妄荔,而應(yīng)該使用update方法泼菌。方法如下:
User user = new User();
user.setUsername("ada");
user.setPassword("123");
user.setCreateDate(new Date());
user.setId(11);
session.update(u);
當(dāng)調(diào)用了update方法以后,u已經(jīng)變成了一個(gè)持久化的對象啦租,那么如果此時(shí)對u對象進(jìn)行修改操作后哗伯,在事務(wù)提交的時(shí)候,則會拿該對象和session中剛保存的持久化對象進(jìn)行比較篷角,如果不同就發(fā)一條sql語句:
Hibernate: update t_user set username=?, password=?, createDate=? where id=?
還有另外一種方法就是使用saveOrUpdate()焊刹,這個(gè)方法會執(zhí)行這樣的操作:如果是離線對象就執(zhí)行update,如果是瞬時(shí)對象就執(zhí)行insert恳蹲。但是這個(gè)方法一般不會使用虐块。
二、狀態(tài)之間的轉(zhuǎn)換
1.當(dāng)我們調(diào)用session.delete()的操作之后嘉蕾,對象就會從持久態(tài)/離線態(tài)變成瞬時(shí)態(tài)贺奠,因?yàn)榇藭r(shí)數(shù)據(jù)庫不存在該對象了。此時(shí)我們再對該對象進(jìn)行各種修改操作的話错忱,hibernate也不會發(fā)送任何修改語句
session = HibernateUtil.openSession();
session.beginTransaction();
User u = new User();
//此時(shí)u為detached對象
u.setId(5);
//此時(shí)u為transient對象
session.delete(u);
//此時(shí)u已經(jīng)是瞬時(shí)對象儡率,不會被session和數(shù)據(jù)庫所管理
u.setPassword("wangwu");
session.getTransaction().commit();
2.當(dāng)我們調(diào)用close()方法后,會把一個(gè)持久態(tài)對象轉(zhuǎn)化為離線態(tài)對象以清。
session.save(u);
//目前u是持久態(tài)對象
session.getTransaction().commit();
}catch(HibernateException e){
session.getTransaction().rollback();
}finally{
//注意在這個(gè)步驟中儿普,session已經(jīng)被關(guān)閉了
if(session!=null)
session.close();
}
Session session2 = null;
session2 = HibernateUtil.openSession();
session2.beginTransaction();
//目前u就是一個(gè)離線態(tài)的對象
System.out.println(u.getId());
u.setAge(18);
//對于離線對象而言,當(dāng)調(diào)整了這個(gè)對象的狀態(tài)時(shí)只要不在同一個(gè)session中是不會完成更新的
session2.getTransaction().commit();
u.setAge(18);
//執(zhí)行完update之后又變成持久化狀態(tài)掷倔,又會被session所管理
session2.update(u);
session2.getTransaction().commit();
3.當(dāng)我們調(diào)用session.clear()方法眉孩,這個(gè)時(shí)候就會將session的緩存對象清空,那么session中就沒有了user這個(gè)對象今魔,此時(shí)對象就會從持久態(tài)變成離線態(tài)勺像。這個(gè)時(shí)候提交事務(wù),發(fā)現(xiàn)session中已經(jīng)沒有該對象错森,所以不會進(jìn)行任何操作吟宦。
session = HibernateUtil.openSession();
session.beginTransaction();
//此時(shí)u是Persistent
User u = (User)session.load(User.class, 4);
u.setUsername("123");
//清空session
session.clear();
//不會進(jìn)行update,因?yàn)閟ession已經(jīng)被清空
session.getTransaction().commit();
里只會發(fā)送一條select語句:
Hibernate: select user0_.id as id0_0_, user0_.createDate as createDate0_0_, user0_.password as password0_0_, user0_.username as username0_0_ from t_user user0_ where user0_.id=?
三涩维、merge方法
一個(gè)session不能管理兩個(gè)相同的對象殃姓,此時(shí)我們可以用merge合并對象。這個(gè)方法的作用就是解決一個(gè)持久化對象兩份拷貝的問題瓦阐,這個(gè)方法會將兩個(gè)對象合并在一起成為一個(gè)對象蜗侈。
session = HibernateUtil.openSession();
session.beginTransaction();
//u1已經(jīng)是持久化狀態(tài)
User u1 = (User)session.load(User.class, 3);
//u2是離線狀態(tài)
User u2 = new User();
u2.setId(3);
u2.setPassword("123456789");
//此時(shí)u2將會變成持久化狀態(tài),在session的緩存中就存在了兩份同樣的對象,在session中不能存在兩份拷貝睡蟋,否則會拋出異常
//session.saveOrUpdate(u2);
//merge方法會判斷session中是否已經(jīng)存在同一個(gè)對象踏幻,如果存在就將兩個(gè)對象合并
session.merge(u2);
//最佳實(shí)踐:merge一般不用
session.getTransaction().commit();
調(diào)用了merge方法以后,此時(shí)會將session中的兩個(gè)持久化對象合并為一個(gè)對象戳杀,但是merge方法不建議被使用