事务概述
事务就是指作为单个逻辑工作单元执行的一组数据操作这些操作要么必须全部成功要么必须全部失败以保证数据的一致性和完整性事务具有ACID属性
原子性自然界最小的事务的内容要么都做要么都不做
一致性 事务结束后状态一致系统状态和业务的逻辑规则一致
隔离性 就是加锁机制多个事务访问同一个数据分别被隔开
持久性 一旦事务结束事务的结果被永久的保存下来
数据库事务管理
数据库事务的ACID特性是由关系数据库管理系统(RDBMS)来实现的数据库管理系统采用日志来保证事务的原子性一致性和持久性
数据库管理系统采用锁机制来实现事务的隔离性
Hibernate应用程序中的事务管理
Hibernate对JDBC进行轻量级的对象封装Hibernate本身在设计时并不具备事务处理功能平时所用的Hibernate的事务只是将底层的JDBCTransaction或者JTATransaction进行一下封装在外面套上Transaction和Session的外壳其实底层都是通过委托底层的JDBC或JTA来实现事务的调度功能
)Hibernate中使用JDBC事务
要在Hibernate中使用JDBC事务可以在hibernatecfgxml中指定Hibernate事务为JDBCTransaction注意如果不配置默认使用JDBC事务
<property name=hibernatetransactionfactory_class>
orghibernatetransactionJDBCTransactionFactory
</property>
Transaction tx=null
try{
tx=sessionbeginTransaction();
//执行持久化操作
mit();
}catch(Exception e){
if(tx!=null) txrollback();
throw e;
} finally{
sessionclose();
}
) Hibernate中使用JTA事务
JTA(Java Transaction API) 可以简单的理解成跨数据库的事务由应用JTA 容器实现使用JTATransaction需要配置hibernatetransactionfactory_class参数该参数缺省值是orghibernatetransaction JDBCTransactionFactory当使用JTATransaction时需要将该参数改成orghibernatetransactionJTATransactionFactory并配置jtaUserTransaction参数JNDI名(Hibernate在启动JTATransaction时要用该值到JNDI的上下文Context中去找javaxtransactionUserTransaction)
javaxtransactionUserTransactin tx = contextlookup(jndiName);
try{
txbegin();
//多个数据库的session操作;
//session…
//session…
mit();
}catch(Exception e){
txrollback(); throw e;
}
并发访问控制
数据库事务并发引起的问题:
丢失更新:你改的时候我也改我改的内容覆盖你修改的或者改的内容回滚了没有保存进去
髒读读到髒数据你读取之后我才修改的你读取的内容无效或者也是回滚我修改的一半你就读取了最后我给回滚了
不可重复读两次查询的内容不一样第二次读取的时候被修改
幻读两次查询的内容不一样在第二次查询的时候发现了与第一次不一样的内容可能原因是中间有人进行更改或者删除
事务隔离级别:为了解决多个事务并发引发的问题让用户根据需要在事务的隔离性和并发性之间做合理的权衡数据库系统提供了种事务隔离级别
读未提交(Read Uncommitted)隔离级别最低
读已提交(Read committed)一般情况下用这个
可重复读(Repeatable Read)
串行化(Serializable)隔离级别最高所以并发问题都没有了
数据库系统采用不同的锁类型来实现这中隔离级别具体实现过程对用户是透明的用户只要选择合适的隔离级别就可以了
隔离级别越高越能保证数据的完整性和一致性但对并发性能的影响也越大对于大多数应用程序可以优先考虑隔离级别为Read Committed它能够避免藏读而且具有较好的并发性能
Hibernate的配置文件可以显示的设置隔离级别每种隔离级别对于一个正整数
Read Uncommitted
Read Committed:
Repeatable Read:
Serializable:
<property name=nnectionisolation></property>
乐观并发控制
当数据库系统采用Read Committed隔离级别时仍不能防止不可重复读和幻读在可能出现这种问题的场合可以在应用程序中采用乐观锁和悲观锁方式来解决
乐观锁是假定当前事务操作数据库资源时不会有其他事务同时访问因此不做数据库层次上的锁定为了维护正确数据hibernate用version和timestamp来实现
)使用版本号进行版本控制
在持久化类中定义一个version属性类型只能是整型的
在映射文件中添加<version>标签注意一定要放在<id>元素后面
悲观并发控制
悲观锁是假定当前事务操作数据资源时一定有其他事务同时访问该数据资源所以先锁定资源
一般实现方式是由数据库来实现采用独占锁来锁定资源使用get()load()是可以显示指定锁定模式LockModeUPGRADE
Sessionget(StudentclassLockModeUPGRADE);
配置Cp连接池
<! 启用cp连接池 设置连接池提供的供应商 >
<property name=connectionprovider_class>nnectionCPConnectionProvider</property>
<! 最大连接数 >
<property name=cpmax_size></property>
<! 最小连接数 >
<property name=cpmin_size></property>
<! 每次请求连接的数目 >
<property name=cpacquire_increment></property>
<! 设置过期时间以秒为单位如果连接池中 处于空闲状态的连接超过了这个时间该连接就会从连接池中删除>
<property name=cptimeout></property>
<! 每个秒检查连接池中的空闲连接 >
<property name=cpidle_test_period></property>
Hibernate不适合的场景
不适合OLAP(OnLine Analytical Processing联机分析处理)以查询分析数据为主的系统适合OLTP(online transaction processing联机事务处理)
对于些关系模型设计不合理的老系统也不能发挥hibernate优势数据量巨大性能要求苛刻的系统hibernate也很难达到要求 批量操作数据的效率也不高
)Hibernate 整合Struts
Hibernate框架主要使用在持久层中完成实体类的CRUD操作
) 泛型DAO模式Hibenate实现
)OpenSessionInView模式
. 案例分析
Hibernatecfgxml中要配置如下数据
<property name=nnectionisolation></property>
映射文件中配置
<version name=version></version>
@Test
//测试乐观锁添加数据版本号自动生成不用手动添加
publicvoid testAdd(){
Session session = HibernateUtilgetSession();
sessionbeginTransaction();
User u = new User();
usetName(李四);
usetBirthday(new Date());
sessionsave(u);
sessiongetTransaction(mit();
HibernateUtilclose();
}
@Test
//测试乐观锁更改数据版本号会自动加
publicvoid testupdate(){
Session session = HibernateUtilgetSession();
sessionbeginTransaction();
User user = (User) sessionload(Userclass);
usersetName(王五);
sessiongetTransaction(mit();
HibernateUtilclose();
}
@Test
//简单模拟hibernate中的事务测试通不过会报错transaction not successfully started但是实验显示第二次修改的值会保存到数据库
publicvoid testtransaction(){
Session session = HibernateUtilgetSession();
sessionbeginTransaction();
User user = (User) sessionload(Userclass);
Session session = HibernateUtilgetSession();
sessionbeginTransaction();
User user = (User) sessionload(Userclass);
usersetName(aa);
usersetName(bb);
sessiongetTransaction(mit();
sessiongetTransaction(mit();
HibernateUtilclose();
}
@Test
//测试悲观锁在同一个资源类中做测试的时候一定记得把乐观锁的设置注释掉尤其是映射文件中的版本标签这是可以简单做下修改改为property属性接着做实验
//result:这时的select语句与平时不同where条件多了一句:where user_id=? for update
publicvoid find(){
Session session = HibernateUtilgetSession();
sessionbeginTransaction();
User user = (User) sessionload(Userclass LockModeUPGRADE);
Systemoutprintln(usergetName()++usergetBirthday());
sessiongetTransaction(mit();
HibernateUtilclose();
}