Hibernate第五章知识点总结——第五章--事务管理

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的事务隔离级别

乐观锁和悲观锁

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值