并发问题可以归类为以下几类:
- 第一类丢失更新:撤销一个事务时,把其他事务已提交的更新数据覆盖。
- 脏读:一个事务读取到另一个事务未提交的更新。
- 虚读:一个事务读取到另一个事务已提交的新插入数据。
- 不可重复读:一个事务读取到另一个事务已提交的事务。
- 第二类丢失更新:一个事务覆盖另一个事务已提交的更新数据。
数据库系统的锁:
根据锁的粒度分为:
- 数据库级锁:锁定数据库
- 表级锁:锁定一个表
- 区域级锁:锁定数据库的特定区域
- 页面级锁:锁定数据库的特定页面
- 键值级锁:锁定数据库中带有索引的一行数据
- 行级锁:锁定一行数据
按照封锁程度可以分为:
1、共享锁
共享锁用于读操作,它是非独占的,允许其它事务读取其锁定的资源,但不允许其它资源更新锁定的资源。
加锁条件:当一个事务执行select语句时,数据库系统就会自动为这个事务添加一个共享锁,来锁定被查询的数据。
解锁条件:当数据被读取后,数据库系统就自动解锁,例如当执行"select * from user"时,数据库系统首先锁定第一行,然后读取,然后解锁第一行,对第二行加锁,允许其它事务更新没锁定的行。
2、独占锁:使用于修改数据的场合,它锁定的数据其它事务既不能读取也不能更新。
加锁条件:当执行select、update等sql语句时,数据库系统自动对所包括的数据资源加锁。
解锁条件:一直到事务执行完毕才解锁。
3、更新锁:
加锁条件:当执行update语句时,系统为事务加上更新锁。
解锁条件:当读取完数据要对数据进行修改时,更新锁会升级为独占锁。
数据库的数据隔离级别:
1、Serializable(串行化):当数据库系统使用Serializable隔离级别时,一个事务在执行过程中完全看不到其它事务对数据库所做的操作。当两个事务操作数据库中相同的数据
的时候,如果第一个事务已经在访问数据,则第二个事务必须停下来等待第一个事务结束。
2、Repeatable Read(可重复度):一个事务在执行过程中可以看到其它事务已提交的新插入的数据,但是不能看到其它事务已提交的更新数据。
3、Read Commited(读已提交数据):一个事务在执行过程中可以看到其它事务已提交的新插入的数据,而且可以看到其它事务已提交的更新数据。
4、Read Umcommited(读未提交数据):一个事务在执行过程中可以看到其它事务未提交的新插入的数据,而且可以看到其它事务未提交的更新数据。
在应用程序中设置隔离级别:
JDBC数据库连接使用数据库默认的隔离级别。可以在hibernate中显示的设置隔离级别,每个级别对应一个整数,从上到下依次为1、2、4、8
eg:hibernate.connection.isolation=1
对于从数据库连接池中获取的每一个连接,hibernate都会把它改为使用Read Commiter隔离级别。在受管理环境中,如果hibernate使用的数据库连接来自应用服务器提供的
数据源,hibernate不会修改这些连接的隔离级别,应该在应用服务器中修改隔离级别。
从应用程序的角度,锁可以分为:
1、悲观锁:指在应用程序中显示的为数据资源加锁。悲观锁假定当前事务操作资源时,必定有其它事务同时访问该资源。为了避免当前事务操作受到影响,先给资源加锁。
2、乐观锁:乐观锁假定当前事务操作资源时,肯定不会有其它资源同时访问该资源。因此完全依靠数据库系统的隔离级别来管理锁。应用程序使用版本控制来避免并发问题。
Oracle、MySql都支持”select ... for update“形式的语句,这种语句显示指定使用独占锁来锁定资源。
在hibernate中使用get()和load()加载数据时,可以用以下形式声明悲观锁
session.get(object.class,id,LockMode.UPGRADE)
org.hibernate.LockMode表示锁定模式。
锁定模式 | 描述 |
LockMode.NONE | 如果在hibernate中存在Account对象,就直接返回该对象的引用,否则就通过 select语句到数据库中获取。这是默认值。 |
LockMode.READ | 不管hibernate中是否存在Account对象,总是执行select语句到数据库中查找, 如果映射文件中配置了版本控制,就执行版本检查,比较缓存中的Account对象 是否和数据库中的Account版本一致。 |
LockMode.UPGRADE | 不管hibernate中是否存在Account对象,总是执行select语句到数据库中查找, 如果映射文件中配置了版本控制,就执行版本检查,比较缓存中的Account对象 是否和数据库中的Account版本一致。如果数据库支持悲观锁,就执行select...for update 语句,否则执行普通的select语句。 |
LockMode.UPGRADE_NOWAIT | 和上一个有一样的功能。同时,对于oracle等支持”update nowait“的数据库, 执行”select ..for update“ |
LockMode.FORCE | 强制更新数据库对象的版本属性,表明这个属性被更新了。 |
利用hibernate的版本控制来实现乐观锁
<verson>和<timestamp>都可以实现版本控制,不过一般使用verson,因为各地时间可能不统一》。。
利用verson实现版本控制,首先在类中添加verson属性,然后再配置文件的<id>元素后紧接着配置<verson>元素...
verson属性不用手动给它赋值。
只有当hibernate通过update语句更新一个对象时,才会更新它的verson属性。版本控制不具有级联特性,但可以通过强制版本更新来修改关联对象的版本属性。