俗話說,本身寫的代碼,6個月後也是別人的代碼……複習!複習!複習!涉及的知識點總結以下:html
1和2大概總結了Hibernate運行的技術原理,那麼如今總結一下它自身的編寫過程:以下:java
其中本質上主要就是使用了dom4j解析配置文件+反射技術來支撐了整個框架的運行。固然若是是註解的話,還有註解技術。而其中世界級的設計思想和編程技巧,又是另外一個方面的技術內容了。web
Hibernate是如何識別持久化類的?spring
在Hibernate的hibernate.cfg.xml配置文件中引入了實體關係的映射文件Xxx.hbm.xml,而在映射文件中指明瞭持久化類是哪些,因此Hibernate經過它識別持久化類,Hibernate容器——》hibernate.cfg.xml——》 *.hbm.xml——》class元素的name屬性加在持久化類,經過這種方式識別持久化類。數據庫
內部執行過程編程
Hibernate的CRUD方法代碼設計模式
1 /** 2 * UserDao 3 * 4 * @author Wang Yishuai. 5 * @date 2016/2/2 0002. 6 * @Copyright(c) 2016 Wang Yishuai,USTC,SSE. 7 */ 8 public class UserDao { 9 final Logger LOG = LoggerFactory.getLogger(UserDao.class); 10 11 private SessionFactory sessionFactory; 12 13 private UserDao() { 14 // 經過new一個Configuration實例,而後用該實例去調用configure返回一個配置實例 15 Configuration configuration = new Configuration().configure(); 16 // 經過 配置實例的buildSessionFactory方法 生成一個 sessionFactory 對象 17 // buildSessionFactory方法會默認的去尋找配置文件hibernate.cfg.xml並解析xml文件 18 // 解析完畢生成sessionFactory,負責鏈接數據庫 19 this.sessionFactory = configuration.buildSessionFactory(); 20 } 21 22 public static UserDao newInstance() { 23 return new UserDao(); 24 } 25 26 public void save(User user) { 27 // 經過 sessionFactory 得到一個數據庫鏈接 session,能夠操做數據庫 28 Session session = sessionFactory.openSession(); 29 // 把操做封裝到數據庫的事務,則須要開啓一個事務 30 Transaction transaction = session.beginTransaction(); 31 32 // 通常把對實體類和數據庫的操做,放到try-catch-finally塊 33 try { 34 // 把user對象插入到數據庫 35 session.save(user); 36 // 提交操做事務 37 transaction.commit(); 38 LOG.info("transaction.commit(); ok"); 39 } catch (Exception e) { 40 // 提交事務失敗,必需要回滾 41 transaction.rollback(); 42 // 打印日誌 43 LOG.error("save user error......", e); 44 } finally { 45 // 不能丟這一步,要釋放資源 46 session.close(); 47 LOG.info("session.close(); ok"); 48 } 49 } 50 51 public void retriveAll() { 52 Session session = sessionFactory.openSession(); 53 List<User> userList = session.createQuery("from User").list(); 54 session.close(); 55 56 for (User user : userList) { 57 LOG.info("username = {}", user.getUsername()); 58 } 59 } 60 61 public void delete(User user) { 62 Session session = sessionFactory.openSession(); 63 Transaction transaction = session.beginTransaction(); 64 65 user = (User) session.get(user.getClass(), user.getUserId()); 66 session.delete(user); 67 transaction.commit(); 68 session.close(); 69 } 70 71 public void update(User user) { 72 Session session = sessionFactory.openSession(); 73 Transaction transaction = session.beginTransaction(); 74 user = (User) session.get(user.getClass(), user.getUserId()); 75 user.setUsername("dadad"); 76 session.update(user); 77 transaction.commit(); 78 session.close(); 79 } 80 }
2 中咱們知道hibernate經過讀取配置文件和依靠反射去拼接對應的SQL語句,好比當hibernate執行session.get(xxx.class, xL)這個代碼的時候,在hibernate內部會拼接成一個SQL語句:緩存
select
user0_.userId as userId0_0_,
user0_.username as username0_0_,
user0_.password as password0_0_
from
user user0_
where
user0_.userId=?安全
要生成該SQL語句,必須找到數據庫對應的表,以及表中的字段,表中的主鍵。又由於session.get方法的第一個參數爲持久化類的class形式,去sessionFactory中查找該class對應的映射文件,找到該映射文件之後,映射文件中的class元素的namesession
屬性的值就是對應的持久化類,class元素的table屬性就是對應的表。這樣找到的。
Hibernate對象的三種狀態
下面是測試代碼:使用的JUnit4作單元測試
1 package test.java; 2 3 import dashuai.dao.UserDao; 4 import dashuai.vo.User; 5 import org.junit.After; 6 import org.junit.Before; 7 import org.slf4j.Logger; 8 import org.slf4j.LoggerFactory; 9 10 /** 11 * Test 12 * 13 * @author Wang Yishuai. 14 * @date 2016/3/10 0010. 15 * @Copyright(c) 2016 Wang Yishuai,USTC,SSE. 16 */ 17 public class Test { 18 private static final Logger LOG = LoggerFactory.getLogger(Test.class); 19 20 private UserDao userDao; 21 22 @Before 23 public void init() { 24 LOG.info("init"); 25 this.userDao = UserDao.newInstance(); 26 } 27 28 @After 29 public void clear() { 30 LOG.info("clear"); 31 } 32 33 @org.junit.Test 34 public void testHibernate1() { 35 User user = new User(); 36 user.setUsername("niubi"); 37 user.setPassword("1"); 38 // 此時的user對象是瞬時態的 39 40 userDao.save(user); 41 // save到數據庫以後,user變爲持久態 42 43 // 最後在save方法裏,最終執行 session.close();使得user變爲遊離態 44 user.setUsername("liuxiang");// 數據庫中的數據不會變 45 } 46 }
小結:
,對象脫離hibernate管理了,變爲遊離態。
1.new出來的對象,但沒有進行session.save();
2.持久化對象調用delete()方法,持久態對象也會變成瞬時對象;
1.在數據庫中經過get(),load(),find()查詢出來的對象數據;
2.瞬時的對象調用save();方法
3.遊離態的對象調用update()方法;
1.手動構建遊離態對象;
2.持久化對象調用evict()(evict方法能夠把一個對象從hibernate容器中去除掉),clear()(session.clear方法清空hibernate內部的全部對象),close()方法,可變爲遊離對象;
如圖:
使用getCurrentSession建立session
爲何不推薦使用openSession方法,看一個例子:
銀行的轉帳操做:一個數據庫的表account,以下:
如今把100塊從一個帳戶轉到另一個帳戶,代碼以下:
public void delete(String account) { Account acc = (Account) session.createQuery("from Account where account = '" + account + "'").uniqueResult(); Transaction transaction = session.beginTransaction(); acc.setMoney(acc.getMoney() - 100); transaction.commit(); session.close(); } public void addMoney(String account) { Account acc = (Account) session.createQuery("from Account where account = '" + account + "'").uniqueResult(); Transaction transaction = session.beginTransaction(); acc.setMoney(acc.getMoney() + 100); transaction.commit(); session.close(); }
測試
@org.junit.Test public void testAccount() { // 帳戶1 Account account1 = new Account(); account1.setAccount("1"); account1.setAid(1); account1.setMoney(100.0); // 帳戶2 Account account2 = new Account(); account2.setAccount("2"); account2.setAid(2); account2.setMoney(200.0); // accountDao.save(account1); // accountDao.save(account2); // // 把2的帳戶的錢轉100到1帳戶 // 先增長1帳戶100元 accountDao.addMoney("1"); // 刪除2帳戶100元 accountDao.delete("2"); }
這是轉帳以前的表,轉帳以後以下:
帳戶2的錢沒有少100,這確定不對。並且JUnit還報錯了:org.hibernate.SessionException: Session is closed!,說明當執行sessionFactory.openSession的時候,會建立一個新的session,再執行事務提交的時候,確定是一個新的事務提交了,說明上面的代碼中addMoney中的事務和delete的事務不是同一個事務,可是根據需求分析,這兩個方法必須在同一個事務中,若是在同一個事務中則必須在同一個session中!也就是說openSession每次都會建立一個新的session,這樣很是耗費內存,且有安全隱患。
使用getCurrentSession
先檢查當前線程中是否有session,若是當前線程中有session,則把session提取出來,直接使用,若是當前線程中沒有session,才用openSession方法建立session,而後把新建立的session放入到threadlocal中,當再次獲得session的時候就是從當前線程中獲取了。其實這很是相似享元設計模式的思想:
由於數據庫的crud操做必須在事務的條件下運行,而使用getCurrentSession建立session,當事務提交的時候,session自動關閉, 這種作法至關於把session和事務綁定在一塊兒了。
再補充一下ThreadLoacl類的做用
《Java併發編程實踐》上面說到:ThreadLoacl是一種線程封閉的實現策略。把變量封閉在線程中,使之變得安全。ThreadLocal並非一個Thread,而是Thread的局部變量,也許把它命名爲ThreadLocalVariable更容易讓人理解一些。使用ThreadLocal維護變量時,ThreadLocal爲每一個使用該變量的線程提供獨立的變量副本,因此每個線程均可以獨立地改變本身的副本,而不會影響其它線程所對應的副本。ThreadLocal是解決線程安全問題一個很好的思路,它經過爲每一個線程提供一個獨立的變量副本解決了變量併發訪問的衝突問題。在不少狀況下,ThreadLocal比直接使用synchronized同步機制解決線程安全問題更簡單,更方便,且結果程序擁有更高的併發性。
getCurrentSession用法
在hibernate.cfg.xml文件中增長配置:
說明session從當前線程中獲取。代碼中使用無需關閉session事務,很是方便,推薦使用。
this.session = sessionFactory.getCurrentSession();
下面比較二者的區別:
1 getCurrentSession建立的session會綁定到當前線程,而openSession不會。
2 getCurrentSession建立的線程會在事務回滾或事物提交後自動關閉,而openSession必須手動關閉
這裏getCurrentSession使用本地事務(本地事務:jdbc)時 要在配置文件裏進行以下設置:
<property name="hibernate.current_session_context_class">thread</property>
若是使用的是全局事務(jta事務),以下配置:
<property name="hibernate.current_session_context_class">jta</property>
使用getCurrentSession的優勢
this.session = sessionFactory.getCurrentSession(); Transaction transaction = session.beginTransaction(); transaction.commit();
就可讓該方法的原子性獲得保證。
可否不使用事務保存對象