1. Hibernate批量处理
a) 原因
i当用hibernate 做大批量处理时,比如将百万乃至更多条数据记录插入到数据库的时候,会出现内存溢出异常(OutOfMemoryException),这是因为当首次运作插入操作时,新产生的对象在session关闭之前都会自动装载在session级别的缓存区(一级缓存),如果配置使用了二级缓存,同样也会装载在二级缓存当中,所以当数据量过大时,会出现内存溢出异常。
b) 解决方式
i. 使用session定期清理缓存机制
利用flush()方法把session的方法提交以后及时调用clear()方法来清理session中的数据
ii 不使用session,使用stateless session(无状态session)接口
Stateless session 不实现第一级缓存,也不和第二级缓存,或者查询缓存交互。它不实现事务化写操作,也不实现脏数据(脏数据并不是废弃和无用的数据,而是指前后发生变化的数据)检查。用stateless session进行的操作甚至不级联到关联实例。Stateless session忽略集合类(Collections)。通过stateless session 进行的操作不触发hibernate的事件模型和拦截器。Stateless session 对数据的混淆现象免疫,因为它没有第一级缓存,stateless session是底层的抽象,和底层的JDBC相当接近。
(早期的版本使用factory.getStatelessSession(),hibernate4之后版本使用factory.withStatelessOptions().openStatelessSession()的方式操作)
iii. 不使用session,使用原生的JDBC方式
(bernate4之后采用如下的方式获取jdbc连接,早期版本使用:session.connection()方式获取)。这个方法性能上是比较好的。也是最快的
2. 事务
a)什么是事务
i. 事务,是工作中的基本逻辑单元。一个事务可能包括数据库的一系列操作,而一个完整的事务保证这些操作都被正确地同步到数据库中,不会发生数据不完整或者错误,或者受到其他干扰。事务具有ACID特性。
b) 并发控制
i. 并发控制是确保及时纠正由并发操作导致的错误的一种机制,并发控制的目的是保证一个用户的工作不会对另一个用户的工作产生不合理的影响。
并发机制不正确可能导致脏读、幻读、不可重复读和更新丢失等此类问题。
ii. 脏读(Dirty read)
如果第二个事务读取了第一个事务修改的但是还没有提交的数据,结果第一个事务最后发生回滚,数据更新没有被保存到数据库中,那么第二个事务读到的数据就是不正确的,是被第一个事务弄脏的数据,所以这种现象叫脏读。
iii. 不可重复读(Unrepeatable read)
两个事务都读取同一行数据,一个事务修改数据并提交,另一个事务也修改数据并提交(不可重复读重点在于update和delete)。
iv. 幻读(Phantom read)
一个事务执行两次查询,结果发现第二次查询的结果比第一次查询的结果数据多了,这可能是因为另一个事务在两次查询之间插入了新行,这就是所谓的“幻读”问题。(幻读的重点在于insert。)
v. 更新丢失(Lost Update)
两个事务都更新同一行数据,如果一个事务执行失败,导致事务回滚,会把另一个事务的更新结果也丢失。
3. 锁
a) 悲观锁
悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。
b) 乐观锁(大多数情况下都使用)
乐观锁hibernate的实现:
乐观锁,大多是基于数据版本( Version )记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个 “version” 字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。
1. 实体中添加整形version,配置问题配置version标签,注意verison标签必须放到id后边,所有property的前面:
2. 实体中添加时间戳,hbm文件配置中配置timestamp标签,该标签也是只能放到id的后边,所有property的前面。
3.对于一个实体来说,version和timestamp不能共存。
悲观锁在hibernate中需要在查询的时候指定,悲观锁的意思是:当有一个线程在进行查询的时候会锁定整个查询结果,其他线程无法查询这些结果,但是可以插入,可能会存在“幻读”的现象