之前只是简单的了解像flush、commit、evict这些方法,而且对实际的执行过程也缺乏了解,在做一些项目的时候也因此导致出现一些错误。通过查阅一些资料后才知道原来是怎么一回事,以下是自己整理的一点东西,给有需要的人看看。
之前使用hibernate框架的时候经常会忘记了在我们的应用程序与数据库之间还隔了一Hibernate,只是简单的认为通过session对数据库操作就是高一层JDBC的封装而已,但Hibernate在为我们提供持久化服务的同时,其实它改变了我们对数据库的操作方式,这种方式使我们不再像是JDBC一样直接对数据库操作,而是有着很多的不同,如果没有正视这一点,在应用Hibernate的时候往往会出现一些不好的结果。
有什么不同?
最大的不同就是Hibernate在我们的应用程序与数据库之间增加了一个缓存区。每Session是通过一些映射和集合来维护所有与该Session建立了关联的对象实体以及对这些对象实体所进行的操作
主要映射与集合:
entityEntries:保存与Session建立了关联的对象实体的映射,包含了对象实体的状态信
息。
insertions:所有的插入操作集合,在一次事务中所有的插入操作都会被记录进来。
deletions:所有删除操作集合,在一次事务中所有删除操作都会被记录进来。
updates:所有更新操作集合,在一次事务中所有更新操作都会被记录进来。
我们平时使用session中的方法对数据库进行操作的时候,实际的执行过程与上面的的缓存的映射、集合有很大关系。
刚学Hibernate的时候我错误的认为:
session.save(obj);
错误认为上面这句代码执行的时候就会将对象实体进行缓存并保存到数据库,但Hibernate并未将这个对象实际的写入数据库中,而仅仅是将obj这个对象放入entityEntries(因为此时obj已经与缓存建立了关联了),由于现在是要对obj对象进行插入操作,Session还要在insertions中登记这个插入行为,在真正需要将缓存中的数据flush(事务提交的时候会调用此方法)入数据库时才执行先前登记的所有行为(有多个数据库操作,会分别一起先登记到对应的操作集合)。
理解了上面说实际执行过程之后,看一个简单的例子:
Session s = HibernateSessionFactory.openSession();
Obj obj = new Obj();
Transaction tx = s.beginTransaction();
s.save(obj);
s.evict(obj);
tx.commit();
s.close();
如果觉得上面的代码没有错误那么说明也没能理解好hibernate对数据库操作的实际过程。
以上代码中的s.evict(obj)将obj对象实体从session缓存中清除,会从entityEntries中将obj这个
对象移出。
事务提交时,需要将所有缓存中的对象实体flush入数据库,这个时候obj不在entityEntries中是没有什么关系的,因为在执行insert的行为时只需要访问insertions集合就足够了,所以此时不会有任何的异常。但执行完插入后异常就出现了,在插入完成后需要entityEntries中obj的标记为已存在数据库中,但obj已经不存在于entityEntries中,此时就会出现异常。
学习中还有一个比较容易模糊的是:flush
session中的flush主要做两件事:
1、清理缓存
2、强制数据库与缓存中的对象实体进行同步,保证数据的一致性
要注意的是在进行flush的时候,是有顺序的:hibernate会将之前已经登记到insertions这些操作集合的所有行为按照insert、update、delete的顺序提交操作的,但它也并不会向数据库提交事务(未持久化到数据库,还可以rollback),也就是数据库中的数据还没有变动。
看个例子:
Session s = HibernateSessionFactory.openSession();
User user1 = new User();
user1.setU_id("1");//这里u_id是主键
s.save(user1); //保存对象
user1.setU_id("2");//更改主键
s.update(User1);//更新对象
User user2 = new User();
user2.setU_id("1");
s.save(user2);
s.flush();
上面的代码会产生主键冲突,这就跟上面说的flush提交操作的顺序有关。
虽然s.save(user2)写在了s.update(user1)后面,但flush提交操作的时候会按顺序先将登记在insertions集合里的操作行为都提交执行后再提交updates集合里的。所以在执行s.save(user1)之后就执行了s.save(user2),从而出现主键冲突。
在使用hibernate的时候,单纯简单的了解的话有可能会带来一些不确定的情况,通过了解多一点的实际操作过程与内部机制,会帮助我们写出更好的代码,所以我还是强调:做一个较真的技术人。