`
lovnet
  • 浏览: 6706808 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

Oracle培训(三十九)——Hibernate第五章知识点总结——第五章--事务管理

 
阅读更多

Oracle培训(三十九)——Hibernate第五章知识点总结——第五章--事务管理

高级映射回顾

组件映射

继承映射

每张表一个类层次、每张表一个子类、每个类一张表

值类型集合映射

Set/Bag/idBag/List/Map

sort/order-by

目标

理解事务的概念及特性

理解并应用事务隔离级别

掌握Hibernate事务API

掌握乐观锁和悲观锁的应用

知识点预览

数据库事务

Hibernate事务

数据库事务

1. 了解数据库事务

a) 数据库事务组合了数据库访问操作。

b) 一个事务要被确保以两种方式终止:提交或回滚

c) 为了在事务内执行所有的数据库操作,必须标记这个工作单的范围,必须启用事务,在某个时间提交变化。如果出现错识,必须回滚事务,保留数据的一致状态。

2. 事务管理概述

a)原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)一起称为ACID标准。

b) Atomicity:一个事务中包含的所有都是一个不可分割的工作单元。

c) Consistency:只有合法的数据可以被写入数据库,如果有任何违例(比如数据与字段类型不符),事务应该将其回滚到最初状态。

d) Isolation:事务允许多个用户对同一数据的并发访问,而不破坏数据的正确性和完整性,同时并行事务的修改和其他并行事务的修改相互独立。

e) Durability:一旦事务被提交之后,处理结果被固化(保存到可掉电存储器上)。

3. 事务隔离问题

a) 脏读(Dirty read)–如果一个事务读取另一个事务没有提交的数据。

b) 不可重复读(Unrepeatable read)–一个事务再次读取之前曾读取的数据,两次读取结果不一样。

c) 幻读(Phantom read)–一个事务执行一个查询两次,并且第二个结果集包括第一个结果集中不存在的记录。

4. 事务隔离级别

a) 未提交读(Read uncommitted)–允许脏读取,但不允许丢失更新

b) 提交读(Read committed)–不会读到另一个并行事务已修改但未提交的数据,避免“脏读”,此隔离级别是大多数主流数据库默认隔离级别,同时也适用于大多数应用系统。

c) 可重复读(Repeatable read)–一个事务不可能更新已经由另一个事务读取但未提交的数据,避免不可重复读取和脏读取

d) 串行读(Serializable)–最严格的事务隔离,要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行

e) 隔离级别

f) 4种隔离级别严密程度又前往后依次递增,同时其性能也依次下降,因此采取最高性能隔离级别并不可取,我们需根据实际情况进行取舍,以获得数据合法性和系统性能上最佳平衡。

5. 选择隔离级别

a) 首先排除”未提交读”隔离级别

b) 绝大部分应用都无须使用“序列化”隔离(一般来说,读取幻影数据并不是一个问题),此隔离级别也难以测量(scale poorly)

c) 可重复读隔离级别,消除一个事务在另外一个并发事务过程中覆盖数据的可能性

6. 设置隔离级别

a) 隔离级别值

1 –读取未提交隔离

2 –读取提交隔离

4 –可重复读隔离

8 –串行读隔离

b) Hibernate在配置文件中设置隔离级别

c) Hibernate不会改变从应用服务器获取的数据库连接的隔离级别

7. 设置隔离级别—配置文件

<!-- 设置隔离层次,控制事务的并发,缺省时Read Committed: 2 -->

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

Hibernate事务

1. Hibenrate是JDBC的轻量级封装,本身并不具备事务管理能力,因此在事务管理层Hibernate将其委托给底层的JDBC或者JTA,以实现事务的管理与调度;

2. 底层事务实现方式定义在Hibernate配置文件中,Hibernate默认事务处理机制基于JDBCTransaction

3. 事务实现方式—配置文件

<!-- 配置事务 -->

<!-- 非管理环境:桌面应用,tomcat环境,可以不写 -->

<property name="transacton.factory_class">

org.hibernate.transaction.JDBCTransactionFactory

</property>

<!-- 管理环境,JTA事务 -->

<!-- property name="transacton.factory_class">

org.hibernate.transaction.JTATransactionFactory

</property-->

4. Hibernate事务—基于JDBC实现

a) Hibernate代码片段:

Session s=sessionFactory.openSession();

Transaction tx=s.beginTransaction();

……

tx.commit();

b) JDBC代码片段:

Connection conn=DiverManager.getConnection();

conn.setAutoCommit(false);

……

conn.commit();

5. Hibernate事务—基于JTA实现

JTA事务是有JTA Container维护的,事务的生命周期由JTA Container维护,与具体Connection无关

6. Hiberante事务API

public void transientToPersist(){
Session ses = sf.openSession();
Transaction tx = ses.beginTransaction();
//创建一个transient对象c
Course c = new Course("Core C++", "C++ fundamental");
try{
//将transient 对象c变成了persistent对象c
cid = (Long)ses.save(c);
tx.commit();
}catch(HibernateException he){ tx.rollback();throw he;
}finally{ses.close();}
//persistent对象c变成了detached对象c
}//garbage collection c


7. 提交回滚关闭

调用tx.commit()方法同步Session状态到数据库。

如果s.save(c)抛出一个异常,则必须强制调用tx.rollback() 方法回滚事务。

非常重要的是,不管成功与否,都要在finally代码块关闭Session,以确保JDBC连接释放到数据库连接池。

1.

a) 业务逻辑处理中,往往需要数据访问排他性,需要通过一些机制保证这个数据在操作过程中不会被外界修改,锁是预防在并发访问数据资源时,当一个事务对数据加锁后,没有并发事务能读或者修改该数据资源。

b) 悲观锁(Pessimistic Locking

c) 乐观锁(Optimistic Locking

2. 悲观锁

a) 悲观锁在读取数据资源进行加锁,直到事务完成该锁被释放掉。

b) 依赖数据库的悲观锁(for update

select * from t_user where name=‘zz’ for update

3. 锁模式

a) Hibernate内部锁机制:

LockMode.NONE–无锁机制

LockMode.WRITEHibernateInsertUpdate记录时会自动获取

LockMode.READ Hibernate读取记录时会自动获取

b) 数据库锁机制:

LockMode.UPDGRADE–利用数据库的 for update子句加锁

LockMode.UPDGRADE_NOWAIT Oracle的特定实现,利用oraclefor update nowait子句实现加锁

c) Hibernate的LockMode类可以让你请求一个特定的悲观锁

public void update(){
Session ses = sf.openSession();
Transaction tx = ses.beginTransaction();
try{
ses.lock(c, LockMode.UPGRADE);
c.setName("Core Java");
c.setDescription("Java language");
ses.update(c); //如果使用了lock()方法,没有必要调用update()
tx.commit();
}catch(HibernateException he){tx.rollback();throw he;
}finally{ses.close();}
}


d) 应用事务

i. 假设两个不同的柜员对同一账户进行修改并提交,这时,我们有三种方法可以处理写入数据库的并发

ii. 最晚提交生效(Last commit wins)–两个变更都成功,第二个变更覆盖第一个变更

iii. 最先提交生效(First commit wins)–第一个变更提交,第二个变更提交时会得到一个错误消息

iv. 合并冲突更新(Merge conflicting updates)–第一个变更提交,第二个修改会返回错误信息,用户可以有选择的应用修改

4. 乐观锁

a) Hibernate使用乐观版本管理可以使第二和每三个策略生效

b) 实现方式:

version:版本机制(版本号或时间戳)

dirty:检查被改动的属性

all:检查被保存的所有属性

c) 版本机制

版本管理使用一个增长的版本号或一个当前时间的时间戳

操作员A此时读出数据(version=1),并从账户扣除余额

A操作过程中,B也读入此用户信息(version=1),并扣除余额

A修改完成之后,增加版本号(version=2),并更新余额信息

B修改完成后,增加版本号(version=2),在数据提交过程中,发现当前版本号不大于数据记录版本号,被驳回。

使用Hibernate版本管理,我们必须在User类添加一个版本属性,并且使用<version>标志或使用@Version注解映射该属性为一个版本号。

d) 使用版本管理—POJO类代码片段

package com.oracle.entity;

public class User {
private Integer userId;
private String userName;
private String userAddress;
private Integer userAge;
private int version;

public int getVersion() {
	return version;
}
public void setVersion(int version) {
	this.version = version;
}
}


e) 使用版本管理—映射文件

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
<class name="com.oracle.entity.User" table="T_USER" optimistic-lock="version">
<id name="userId" type="java.lang.Integer">
<column name="userId" length="32" />
<generator class="native" />
</id>
<version name="version" column="version"></version>

<property name="userName" type="java.lang.String">
	<column name="userName" length="200" />
</property>
<property name="userAddress" type="java.lang.String">
<column name="userAddress" length="200" />
</property>

<property name="userAge" type="java.lang.Integer">
	<column name="userAge" />
</property>

</class>
</hibernate-mapping>


f) 使用版本管理—持久化代码片段

public class Test {
	public static void main(String[] args) {

		try {
			sf = cfg.buildSessionFactory();
			s1 = sf.openSession();
			s2=sf.openSession();
			User u1=(User) s1.get(User.class, 1);
			User u2=(User) s2.get(User.class, 1);
			tx1 = s1.beginTransaction();
			tx2=s2.beginTransaction();
			u1.setUserAge(27);
			tx1.commit();
			u2.setUserAge(36);
			tx2.commit();
			
		} catch (HibernateException e) {
			e.printStackTrace();
			tx1.rollback();
			tx2.rollback();
		}finally{
			if(s1!=null){s1.close();}if(s2!=null){s2.close();}if(sf!=null){sf.close();}
		}

	}
}


g) 使用版本管理

i. Hibernate每次更新时,会在SQL的子句中使用版本字段如下 :

ii. update orders set name=‘new name’,version=6 where id=1001 and version=5;

iii. 如果另一个事务读取并更新相同的信息, VERSION字段不是值5,则找不到匹配的行而更新不成功,此时Hibernate会抛出异常

总结

事务管理概述

ACID/三种问题/四种隔离级别

Hibernate事务API

JDBC实现/JTA实现

锁机制

悲观锁/乐观锁

问题

Hibernate的事务隔离级别

乐观锁和悲观锁

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics