俗话说,本身写的代码,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();
就可让该方法的原子性获得保证。
可否不使用事务保存对象