Hibernate事务和并发控制

Hibernate事务和并发控制

++YONG原创,转载请注明

1.    事务介绍:

1.1.       事务的定义:

事务就是指作为单个逻辑工作单元执行的一组数据操作,这些操作要么必须全部成功,要么必须全部失败,以保证数据的一致性和完整性。

1.2.       事务具有ACID属性:


原子性(Atomic):事务由一个或多个行为绑在一起组成,好像是一个单独的工作单元。原子性确保在事务中的所有操作要么都发生,要么都不发生。


一致性(Consistent):一旦一个事务结束了(不管成功与否),系统所处的状态和它的业务规则是一致的。即数据应当不会被破坏。


隔离性(Isolated):事务应该允许多个用户操作同一个数据,一个用户的操作不会和其他用户的操作相混淆。


持久性(Durable):一旦事务完成,事务的结果应该持久化。

事务的ACID特性是由关系数据库管理系统(RDBMS)来实现的。

o 数据库管理系统采用日志来保证事务的原子性、一致性和持久性。日志记录了事务对数据库所做的更新,如果某个事务在执行过程中发生错误,就可以根据日志,撤销事务对数据库已做的更新,使数据库退回到执行事务前的初始状态。

o 数据库管理系统采用锁机制来实现事务的隔离性。当多个事务同时更新数据库相同的数据时,只允许持有锁的事务能更新该数据,其他事务必须等待,直到前一个事务释放了锁,其他事务才有机会更新该数据。

2.    数据库事务声明:

数据库系统的客户程序只要向数据库系统声明了一个事务,数据库系统就会自动保证事务的ACID特性。在JDBC API中,java.sql.Connection类代表一个数据库连接。它提供了以下方法控制事务:

1.         setAutoCommit(Boolean autoCommit):设置是否自动提交事务。

2.         commit():提交事务。

3.         rollback():撤销事务。

2.1.       JDBC API声明事务的示例代码如下:


Connection = null;

PreparedStatement pstmt = null;

try{

con = DriverManager.getConnection(dbUrl, username, password);

//设置手工提交事务模式

con.setAutoCommit(false);

pstmt = ……;

pstmt.executeUpdate();

//提交事务

con.commit();

}catch(Exception e){

//事务回滚

con.rollback();

…..

} finally{

…….

}

       Hibernate

JDBC
进行了轻量级的对象封装,
Hibernate
本身在设计时并不具备事务处理功能,平时所用的
Hibernate
的事务,只是将底层的
JDBCTransaction
或者
JTATransaction
进行了一下封装,在外面套上
Transaction

Session
的外壳,其实底层都是通过委托底层的
JDBC

JTA
来实现事务的调度功能。

2.2.       Hibernate中使用JDBC事务:

要在
Hibernate
中使用事务,可以配置
Hibernate
事务为
JDBCTransaction
或者
JTATransaction
,这两种事务的生命周期不一样,可以在
hibernate.cfg.xml
中指定使用的是哪一种事务。以下配置为使用
JDBC
事务。注:如果不进行配置,
Hibernate
也会默认使用
JDBC
事务。


 

<session-factory>

……

<property name="hibernate.transaction.factory_class">

org.hibernate.transaction.JDBCTransactionFactory

</property>

……

</session-factory>

Hibernate 使用JDBC transaction处理方式如下所示:


Transaction tx = null;

try {

tx = sess.beginTransaction();

// do some work

...

tx.commit();

}

catch (RuntimeException e) {

if (tx != null) tx.rollback();

throw e; // or display error message

}

finally {

sess.close();

}

 

2.3.       Hibernate中使用JTA事务:

JTA(java Transaction API)
是事务服务的
JavaEE
解决方案。本质上,它是描述事务接口的
JavaEE
模型的一部分。

JTA
具有的
3
个接口:
UserTransaction
接口、
TransactionManager
接口和
Transaction
接口,这些接口共享公共的事务操作。
UserTransaction
能够执行事务划分和基本的事务操作,
TransactionManager
能够执行上下文管理。

在一个具有多个数据库的系统中,可能一个程序将会调用几个数据库中的数据,需要一种分布事务,或者准备用
JTA
来管理
Session
的长事务,那么就需要使用
JTATransaction


hibernate.cfg.xml
中配置
JTA
事务管理:


<session-factory>

……

<property name="hibernate.transaction.factory_class">

org.hibernate.transaction.JTATransactionFactory

</property>

……

</session-factory>

下面是一个实际应用的
JTA
示例:


// BMT(bean管理事务) idiom with getCurrentSession()

try {

UserTransaction tx = (UserTransaction)new InitialContext()

.lookup("java:comp/UserTransaction");

tx.begin();

// Do some work on Session bound to transaction

factory.getCurrentSession().load(...);

factory.getCurrentSession().persist(...);

tx.commit();

}

catch (RuntimeException e) {

tx.rollback();

throw e; // or display error message

}

在CMT方式下,事务声明是在session bean的部署描述符中,而不需要编程。 因此,代码被简化为:


// CMT idiom

Session sess = factory.getCurrentSession();

 

// do some work

...

 

3.    多个事务并发引起的问题:

3.1.        
第一类丢失更新:撤消一个事务时,把其它事务已提交的更新的数据覆盖了。

3.2.        
脏读:一个事务读到另一个事务未提交的更新数据。

3.3.        
幻读:一个事务执行两次查询,但第二次查询比第一次查询多出了一些数据行。

3.4.        
不可重复读:一个事务两次读同一行数据,可是这两次读到的数据不一样。

3.5.        
第二类丢失更新:这是不可重复读中的特例,一个事务覆盖另一个事务已提交的更新数据。

4.    事务隔离级别:

为了解决多个事务并发会引发的问题。数据库系统提供了四种事务隔离级别供用户选择。

o Serializable:串行化。隔离级别最高

o Repeatable Read:可重复读。

o Read Committed:读已提交数据。

o Read Uncommitted:读未提交数据。隔离级别最差。

数据库系统采用不同的锁类型来实现以上四种隔离级别,具体的实现过程对用户是透明的。用户应该关心的是如何选择合适的隔离级别。

对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed,它能够避免脏读,而且具有较好的并发性能。

每个数据库连接都有一个全局变量@@tx_isolation,表示当前的事务隔离级别。
JDBC
数据库连接使用数据库系统默认的隔离级别。在Hibernate的配置文件中可以显示地设置隔离级别。每一种隔离级别对应着一个正整数。

Read Uncommitted: 1

Read Committed: 2

Repeatable Read: 4

Serializable: 8

在hibernate.cfg.xml中设置隔离级别如下:


    <session-factory>

<!-- 设置JDBC的隔离级别 -->

<property name="hibernate.connection.isolation">2</property>

</session-factory>

设置之后,在开始一个事务之前,Hibernate将为从连接池中获得的JDBC连接设置级别。需要注意的是,在受管理环境中,
如果
Hibernate
使用的数据库连接来自于应用服务器提供的数据源,Hibernate
不会改变这些连接的事务隔离级别。在这种情况下,应该通过修改应用服务器的数据源配置来修改隔离级别。

5.    并发控制:

当数据库系统采用Red Committed隔离级别时,会导致不可重复读和第二类丢失更新的并发问题,在可能出现这种问题的场合。可以
在应用程序中采用悲观锁或乐观锁来避免这类问题。

5.1.       乐观锁(Optimistic Locking)

乐观锁假定当前事务操纵数据资源时,不会有其他事务同时访问该数据资源,因此不作数据库层次上的锁定。为了维护正确的数据,乐观锁使用应用程序上的版本控制(由程序逻辑来实现的)来避免可能出现的并发问题。

唯一能够同时保持高并发和高可伸缩性的方法就是使用带版本化的乐观并发控制。版本检查使用版本号、 或者时间戳来检测更新冲突(并且防止更新丢失)。

5.1.1.       
使用版本检查(<version>)

Hibernate中通过版本号检查来实现后更新为主,这也是Hibernate推荐的方式。在数据库表中加入一个version(版本)字段,在读取数据时连同版本号一起读取,并在更新数据时比较版本号与数据库表中的版本号,如果等于数据库表中的版本号则予以更新,并递增版本号,如果小于数据库表中的版本号就抛出异常。

使用<version>进行版本控制的步骤:

1)      在持久化类中定义一个代表版本号的属性:


package org.qiujy.domain.versionchecking;

import java.util.Date;

public class Product implements java.io.Serializable{

private Long id ;

/** 版本号 */

private int version;

private String name; //产品名

private String description; //描述--简介

private Double unitCost; //单价

private Date pubTime; //生产日期

public Product(){}

//以下为getter()和setter()方法

}

2)      在Product.hbm.xml文件中用<version>元素来建立Product类的version属性与表中version字段的映射。

3)      Hibernate在其数据库访问引擎中内置了乐观锁定实现,默认也是选择version方式作为Hibernate乐观锁定实现机制。所以,在配置文件及程序中可以不作其它设置。按往常一样写操作代码。


package org.qiujy.domain.versionchecking;

import java.util.Date;

import org.hibernate.HibernateException;

import org.hibernate.Session;

import org.hibernate.Transaction;

import org.qiujy.common.HibernateSessionFactory;

public class TestVersionChecking {

public static void main(String[] args) {

Product prod = new Product();

prod.setName("IBM thinkPad T60");

prod.setUnitCost(new Double(26000.00));

prod.setDescription("笔记本电脑");

prod.setPubTime(new Date());

//test start.......

Session session = HibernateSessionFactory.getSession();

Transaction tx =null;

try{

tx = session.beginTransaction();

session.save(prod);

 

tx.commit();

}catch(HibernateException e){

if(tx != null){

tx.rollback();

}

e.printStackTrace();

}finally{

HibernateSessionFactory.closeSession();

}

//进行更新 测试..

prod.setDescription("新款的");

Session session2 = HibernateSessionFactory.getSession();

Transaction tx2 =null;

try{

tx2 = session2.beginTransaction();

session2.update(prod);

tx2.commit();

}catch(HibernateException e){

if(tx2 != null){

tx2.rollback();

}

e.printStackTrace();

}finally{

HibernateSessionFactory.closeSession();

}

}

}

新增数据时产生的SQL是:


insert into products (version, name, description, unitCost, pubTime)

values(?, ?, ?, ?, ?)

程序无需为Product对象的version属性显示赋值,当持久化一个Product对象时,Hibernate会自动为它赋初始值为0。

更新数据时产生的SQL是:


    update

products

set

version=?,

name=?,

description=?,

unitCost=?,

pubTime=?

where

id=?

and version=?

当Hibernate更新一个Product对象,会根据它的id和version属性到相应的数据库表中定位匹配的记录,如果存在这条匹配的记录,就更新记录,并且把version字段的值加1。若找不到匹配的记录,此时Hibernate会抛出StaleObjectStateException。

需要注意的是,由于乐观锁定是使用系统中的程序来控制,而不是使用数据库中的锁定机制,因而如果有人故意自行更新版本信息来超过检查,则锁定机制就无效。所以建议把持久化类中的version的get方法设置为private的。

5.1.2.       
使用时间戳(<timestamp>)

跟版本检查的用法相似。不再多说。

5.2.       悲观锁(Pessimistic Locking)

悲观锁假定当前事务操纵数据资源时,肯定还会有其他事务同时访问该数据资源,为了避免当前事务的操作受到干扰,先锁定资源。尽管悲观锁能够防止丢失更新和不可重复读这类并发问题,但是它影响并发性能,因此应该很谨慎地使用悲观锁。

时间: 2025-01-21 19:13:50

Hibernate事务和并发控制的相关文章

Hibernate事务管理

Hibernate 是JDBC 的轻量级封装,本身并不具备事务管理能力.在事务管理层, Hibernate将其委托给底层的JDBC或者JTA,以实现事务管理和调度功能.      Hibernate的默认事务处理机制基于JDBC Transaction.我们也可以通过配置文件设定采用JTA作为事务管理实现: <hibernate-configuration>          <session-factory>                   ……               

Hibernate事务

Hibernate事务控制    在实际生产中,每个业务逻辑都是由一系列对数据库访问完成的,这一系列的数据访问可能包括删除一些数据,然后再插入一些数据,这里的删除和插入的动作不可分割,要么全部执行,要么都不执行,这就需要事务. 事务具备4个特性:原子性(Atomicity),一致性(Consistency),隔离性(Isolation)和持续性(Durability)        从编程角度来看,Hibernate事务是由Session对象开启的:但是从底层的实现来看,Hibernate事务由

SQL_事务的并发控制

--多个用户对同一个数据操作时,一个用户的行为结果可能导致另一个用户使用的数据无效,通过事务的并发控制可以确保 --同时发生的行为与数据的有效性不发生冲突 begin transaction select * from teachers with(holdlock) --数据库加锁 waitfor delay '00:00:10' commit transaction --通过commit 或者rollback可以解锁 begin transaction update teachers set

Spring+hibernate事务配置

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"    xmlns:task="http://www.springframework.org/schema/task"    xmlns:xsi="http://www.w3.org/2001/XMLSchem

Hibernate事务代码规范写法

----------------siwuxie095 事务相关概念 1.什么是事务 逻辑上的一组操作,构成这组操作的各个单元,要么一起成功, 要么一起失败 2.事务的四个特性 1)原子性 2)一致性 3)隔离性 4)持久性 3.不考虑隔离性,产生的问题 1)脏读 2)不可重复读 3)幻读(也称 虚读) 4.设置事务隔离级别,即可解决不考虑隔离性所产生的问题 「MySQL 默认的隔离级别:Repeatable Read(可重复读)」 5.Hibernate 也可以在核心配置文件中配置事务隔离级别

spring hibernate 事务整合 使用测试类 事务不自动提交的问题!!!

使用JUnit 测试hibernate 事务管理的时候应注意 ,测试类完成是默认回滚的. 所以只能查询数据库却不能增删改数据库. 应该在测试类上面加上注解 @Rollback(false)  表似默认不回滚. package TestHibernate; import java.util.List; import javax.annotation.Resource; import org.junit.Test; import org.junit.runner.RunWith; import or

事务与并发控制

1.定义事务   可以引用数据库的物理事务  可以引用和一个持久上下文相关的逻辑事务  可以引用应用程序的工作单元,当做由原型模式定义   2.物理事务    Hibernate使用JDBC API的持久性,在java中有两个定义良好的机制处理事务的JDBC:JDBC和JTA.  Hibernate支持结合事务机制,允许应用程序来管理物理事务.    org.hibernate.engine.transaction.spi.TransactionFactory中2个主要的功能:   它允许hib

JTA与Hibernate事务

Hibernate是JDBC的轻量级封装,本身并不具备事务管理能力,在事务管理层,Hibernate将其委托给底层的JDBC或者JTA,以实现事务的管理和调度. Hibernate的默认事务处理机制基于JDBCTransaction,也可以通过配置文件设定采用JTA作为事务管理实现: <hibernate-configuration> <session-factory> …… <property name = "hibernate.transaction.facto

Hibernate 事务提交和事务回滚

Hibernate 事务提交和事务回滚 最近在用做hibernate做项目,一直不明白session.beginTransaction().commit() 和 session.beginTransaction().rollback(); 有什么区别 一组业务整体处理的行为叫一个事务.这一组的业务都能成功处理,我们就可以把这个事务提交来保存你已做的行为结果.但如果一组中有任何的差错出现的话,我们就认为这事务不成功,需要回滚来撤消之前的操作.举例:你去银行转账,转账我们有两步吧,从你账户中取出钱再