一、事务
所谓事务,是指一组相互依赖的操作单元的集合,用来保证对数据库的正确修改,保持数据的完整性,如果一个事务的某个单元操作失败,将取消本次事务的全部操作。
数据库事务必须具备以下特征(简称ACID)
①原子性(Atomic)
每个事务是一个不可分割的整体,只有所有的操作单元执行成功,整个事务才成功;
否则此次事务就失败,所有执行成功的操作单元必须撤销,数据库回到此次事务之前的状态
②一致性(Consistency)
在执行一次事务后,关系数据的完整性和业务逻辑的一致性不能被破坏。
③隔离性(Isolation)
在并发环境中,一个事务所做的修改必须与其他事务所做的修改相隔离。
例如一个事务查看的数据必须是其他并发事务修改之前或修改完毕的数据,不能是修改中的数据。
④持久性(Durability)
事务结束后,对数据的修改是永久保存的,即使系统故障导致重启数据库系统,数据依然是修改后的状态
数据库管理系统采用锁的机制来管理事务。当多个事务同时修改同一数据时,只允许持有锁的事务修改该数据,其他事务只能"排队等待",直到前一个事务释放其拥有的锁。
二、隔离级别的选择
锁机制有效地解决了事务的并发问题,但也影响了事务的并发性能。所谓并发性能是指数据库系统同时为多个用户提供服务的能力。
当一个事务将其操作的数据资源锁定时,其他欲操作该资源的事务必须等待锁定解除,才能继续进行,这就降低了数据库系统同时响应多客户的速度,因此,合理地选择隔离级别,将关系到一个软件的性能。
数据库系统提供了四种可选的事务隔离级别
①serializable(串行化)
采用此隔离级别,一个事务在执行过程中首先将欲操纵的数据锁定,待事务结束后释放。
如果此时另一个事务也要操纵该数据,必须等待前一个事务释放锁定后才能继续进行。两个事务实际上是串行化方法运行的。
②repeatable read(可重复读)
采用此隔离级别,一个事务在执行过程中只能够看到其他事务已经提交的新纪录,看不到其他事务对已有记录的修改。
③read committed(读已提交数据)
采用此隔离级别,一个事务在执行过程中不但能够看到其他事务已提交的新插入记录,而且也能看到其他事务已经提交的对已有记录的修改。
④read uncommitted(读未提交数据)
采用此隔离级别,一个事务在执行过程中不但能够看到其他事务未提交的新插入记录,而且也能看到其他事务未提交的对已有记录的修改。
综上所述可以得出,并非隔离级别越高越好对于多数应用程序,只需把隔离级别设为read committed,尽管会存在一些问题,但是可以采用悲观锁和乐观锁来解决,这样可以提高软件的并发性能
三、数据锁定
①悲观锁的使用
所谓悲观锁,是悲观地认为每个事务在操纵数据时,肯定会有其他事务同时访问数据资源,当前事务为防止自身受到影响,先将相关数据资源锁定,待事务结束时释放锁定。它同样会影响软件的并发性能,应谨慎使用。
悲观锁的实现方法有两种:
方法一:在应用程序中指定采用数据库系统的独占锁来锁定数据资源。
在事务当中执行select语句时,默认情况下数据库系统采用共享锁来锁定查询记录。
当一条记录采用共享锁锁定后,其他事务只允许访问,不允许修改,共享锁以数据库中的每条记录为锁定单位,在读取前加锁,读取结束后释放锁定。
hibernate提供了一个org.hibernate.LockMode类,可以通过它的静态方法设定锁定方式
Test test = (Test)session.load(Test.class,new Long(27),LockMode.READ)。
方法二:通过应用程序逻辑实现悲观锁
此方法的主导思想是用户自己为每个记录设定一个访问标记,用以区分数据的状态。
②乐观锁的使用
所谓乐观锁,是乐观地认为每个事务在操纵数据时,很少有其他事务同时访问该数据资源,因而不在数据库层锁定,而是通过应用程序逻辑来实现版本控制。
hibernate提供了两种实现乐观锁的方法
方法一:通过配置持久化类的<version>元素。
该方法的实现机制为在数据库中添加一个int型的(version)字段,用来标记记录的版本信息。
该字段由hibernate控制... ...
<class name="Test" table="tb_test" optimistic-lock="version">
<id name="id" column="id" type="int">
<generator class="increment">
</id>
<version name = "version" column="version"/>
</class>
<class>元素的optimistic-lock属性的作用是指定乐观锁的实现策略,它有四个可选值
①none:不适用乐观锁定
②version:利用版本控制机制实现乐观锁定
③dirty:通过检查发生改变的属性实现乐观锁定
④all:通过检查所有属性实现乐观锁定
方法二:通过配置持久化类的<timestamp>元素。
四、Session中的lock()和update()方法
在使用版本检查的情况下
lock()和update()方法的相同点都是将一个游离对象加入到Session中,使之转变为持久化对象,并且在提交事务时执行版本检查。
它们的不同点如下:
①update()方法在将游离对象与当前Session建立关联时,总是直接进行关联,不执行版本检查,版本检查是在提交事务时执行的;
②lock()方法在将游离对象与当前Session建立关联时,可以设置是否执行版本检查,如果设置为执行版本检查,则首先对欲关联游离的对象进行版本检查,版本一致则建立与当前Session的关联,否则抛出异常。