心得16-hibernate的优化3-事务处理
1.事务概述
事务就是指作为单个逻辑工作单元执行的一组数据操作,这些操作要么必须全部成功,要么必须全部失败,以保证数据的一致性和完整性。事务具有ACID属性:
原子性:自然界最小的,事务的内容要么都做要么都不做
一致性: 事务结束后状态一致;系统状态和业务的逻辑规则一致
隔离性: 就是加锁机制,多个事务访问同一个数据分别被隔开
持久性: 一旦事务结束,事务的结果被永久的保存下来
数据库事务管理
数据库事务的ACID特性是由关系数据库管理系统(RDBMS)来实现的。数据库管理系统采用日志来保证事务的原子性、一致性和持久性。
数据库管理系统采用锁机制来实现事务的隔离性。
2.Hibernate应用程序中的事务管理
Hibernate对JDBC进行轻量级的对象封装,Hibernate本身在设计时并不具备事务处理功能,平时所用的Hibernate的事务,只是将底层的JDBCTransaction或者JTATransaction进行一下封装,在外面套上Transaction和Session的外壳,其实底层都是通过委托底层的JDBC或JTA来实现事务的调度功能。
1)Hibernate中使用JDBC事务
要在Hibernate中使用JDBC事务,可以在hibernate.cfg.xml中指定Hibernate事务为JDBCTransaction。注意:如果不配置,默认使用JDBC事务。
<property name=“hibernate.transaction.factory_class”>
org.hibernate.transaction.JDBCTransactionFactory
</property>
Transaction tx=null;
try{
tx=session.beginTransaction();
//执行持久化操作
tx.commit();
}catch(Exception e){
if(tx!=null) tx.rollback();
throw e;
} finally{
session.close();
}
2) Hibernate中使用JTA事务
JTA(Java Transaction API) 可以简单的理解成跨数据库的事务,由应用JTA 容器实现。使用JTATransaction需要配置hibernate.transaction.factory_class参数,该参数缺省值是org.hibernate.transaction. JDBCTransactionFactory,当使用JTATransaction时需要将该参数改成org.hibernate.transaction.JTATransactionFactory,并配置jta.UserTransaction参数JNDI名(Hibernate在启动JTATransaction时要用该值到JNDI的上下文Context中去找javax.transaction.UserTransaction)。
javax.transaction.UserTransactin tx = context.lookup(“jndiName”);
try{
tx.begin();
//多个数据库的session操作;
//session1….
//session2….
tx.commit();
}catch(Exception e){
tx.rollback(); throw e;
}
3. 并发访问控制
数据库事务并发引起的问题:
丢失更新:你改的时候我也改,我改的内容覆盖你修改的或者改的内容回滚了,没有保存进去。
脏读:读到脏数据,你读取之后我才修改的,你读取的内容无效或者也是回滚,我修改的一半你就读取了最后我给回滚了。
不可重复读:两次查询的内容不一样;第二次读取的时候被修改
幻读:两次查询的内容不一样;在第二次查询的时候发现了与第一次不一样的内容;可能原因是中间有人进行更改或者删除。
事务隔离级别:为了解决多个事务并发引发的问题,让用户根据需要在事务的隔离性和并发性之间做合理的权衡。数据库系统提供了4种事务隔离级别。
读未提交(Read Uncommitted):隔离级别最低
读已提交(Read committed):一般情况下用这个
可重复读(Repeatable Read)
串行化(Serializable):隔离级别最高,所以并发问题都没有了
数据库系统采用不同的锁类型来实现这4中隔离级别。具体实现过程对用户是透明的,用户只要选择合适的隔离级别就可以了。
隔离级别越高,越能保证数据的完整性和一致性,但对并发性能的影响也越大。对于大多数应用程序,可以优先考虑隔离级别为Read Committed,它能够避免藏读,而且具有较好的并发性能。
Hibernate的配置文件可以显示的设置隔离级别。每种隔离级别对于一个正整数。
Read Uncommitted:1
Read Committed: 2
Repeatable Read: 4
Serializable:8
<property name=“hibernate.connection.isolation”>2</property>
4. 乐观并发控制
当数据库系统采用Read Committed隔离级别时,仍不能防止不可重复读和幻读。在可能出现这种问题的场合,可以在应用程序中采用乐观锁和悲观锁方式来解决。
乐观锁是假定当前事务操作数据库资源时,不会有其他事务同时访问,因此不做数据库层次上的锁定。为了维护正确数据,hibernate用version和timestamp来实现。
1)使用版本号进行版本控制
在持久化类中定义一个version属性。类型只能是整型的。
在映射文件中添加<version>标签,注意一定要放在<id>元素后面。
5.悲观并发控制
悲观锁是假定当前事务操作数据资源时,一定有其他事务同时访问该数据资源,所以先锁定资源。
一般实现方式是由数据库来实现,采用独占锁来锁定资源。使用get(),load(),是可以显示指定锁定模式:LockMode.UPGRADE
Session.get(Student.class,1,LockMode.UPGRADE);
6. 配置C3p0连接池
<!-- 启用c3p0连接池 设置连接池提供的供应商 -->
<property name="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
<!-- 最大连接数 -->
<property name="c3p0.max_size">20</property>
<!-- 最小连接数 -->
<property name="c3p0.min_size">5</property>
<!-- 每次请求连接的数目 -->
<property name="c3p0.acquire_increment">5</property>
<!-- 设置过期时间,以秒为单位,如果连接池中 -处于空闲状态的连接超过了这个时间,该连接就会从连接池中删除-->
<property name="c3p0.timeout">120</property>
<!-- 每个3000秒检查连接池中的空闲连接 -->
<property name="c3p0.idle_test_period">3000</property>
7. Hibernate不适合的场景
不适合OLAP(On-Line Analytical Processing联机分析处理),以查询分析数据为主的系统;适合OLTP(on-line transaction processing联机事务处理)。
对于些关系模型设计不合理的老系统,也不能发挥hibernate优势。数据量巨大,性能要求苛刻的系统,hibernate也很难达到要求, 批量操作数据的效率也不高。
1).Hibernate 整合Struts
Hibernate框架主要使用在持久层中,完成实体类的CRUD操作。
2). 泛型DAO模式Hibenate实现
3).OpenSessionInView模式
8. 案例分析
Hibernate.cfg.xml中要配置如下数据:
<property name="hibernate.connection.isolation">2</property>
映射文件中配置:
<version name="version"></version>
@Test
//测试乐观锁;添加数据,版本号自动生成不用手动添加
publicvoid testAdd(){
Session session = HibernateUtil.getSession();
session.beginTransaction();
User u = new User();
u.setName("李四");
u.setBirthday(new Date());
session.save(u);
session.getTransaction().commit();
HibernateUtil.close();
}
@Test
//测试乐观锁;更改数据,版本号会自动加1
publicvoid testupdate(){
Session session = HibernateUtil.getSession();
session.beginTransaction();
User user = (User) session.load(User.class,4);
user.setName("王五");
session.getTransaction().commit();
HibernateUtil.close();
}
@Test
//简单模拟hibernate中的事务;测试通不过,会报错:transaction not successfully started;但是实验显示第二次修改的值会保存到数据库
publicvoid testtransaction(){
Session session1 = HibernateUtil.getSession();
session1.beginTransaction();
User user1 = (User) session1.load(User.class,4);
Session session2 = HibernateUtil.getSession();
session2.beginTransaction();
User user2 = (User) session1.load(User.class,4);
user1.setName("aa");
user2.setName("bb");
session1.getTransaction().commit();
session2.getTransaction().commit();
HibernateUtil.close();
}
@Test
//测试悲观锁,在同一个资源类中做测试的时候一定记得把乐观锁的设置注释掉,尤其是映射文件中的版本标签,这是可以简单做下修改,改为property属性接着做实验
//result:这时的select语句与平时不同,where条件多了一句:where user0_.id=? for update
publicvoid find(){
Session session = HibernateUtil.getSession();
session.beginTransaction();
User user = (User) session.load(User.class, 4,LockMode.UPGRADE);
System.out.println(user.getName()+"----"+user.getBirthday());
session.getTransaction().commit();
HibernateUtil.close();
}