版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011301372/article/details/82931710
事务的相关概念
什么是事务
- 事务是逻辑上的一组操作,组成事务的各个执行单元,操作要么全都成功,要么全部失败
- 转账的例子:小王给小李转钱,扣钱,加钱,两个操作组成了一个事务
事务的特性
- 原子性------事务的不可分割
- 一致性----事务执行的前后数据的完整性保持一致
- 隔离性-----一个事务执行的过程中,不应该受到其他的事务的干扰
- 持久性----事务一旦提交,数据就永久保持到数据库中
如果不考虑隔离性:引发一些读的问题
- 脏读----一个事务读到了另一个事务未提交的数据
- 不可重复读-----一个事务读到了另一个事务已经提交的update数据,导致多次查询结果不一致。
- 虚读----一个事务读到了另一个事务已经提交的inset数据,导致多级查询机构不一致。
通过设置数据库的隔离级别来解决上述读的问题
- 未提交读:以上的读的问题都有可能发生
- 已提交读:避免脏读,但是不可重复读,虚读都有可能发生
- 可重复读:避免脏读,不可重复读,但是虚读是有可能发生
- 串行化:以上读的情况都可以避免
如果想在Hibernate的框架中来设置隔离级别,需要在Hibernate.cfg.xml的配置文件中通过标签来配置
- 通过:hibernate.connection,isolation = 4 来配置
- 取值:
- 1——Read uncommitted isolation
- 2——Read commited isolation
- 4——Repeatable read isolation
- 8——Serializable isolation
<!--设置数据库的隔离级别,就使用默认值就OK-->
<property name="hibernate.connection.isolation">4</property>
丢失更新的问题
- 如果不考虑隔离性,也会产生写入数据的问题,这一类的问题叫丢失更新的问题
- 例如:两个事务同时对某一条记录做修改,就会引发丢失更新的问题
- A 事务和B事务同时获取到一条数据,同时再做修改
- 如果A事务修改完成后,提交了事务
- B事务修改完成后,不管是提交还是回滚,如果不做处理,都会对数据产生影响
两种解决方案
悲观锁
- 采用的是数据库提供的一种锁机制,如果采用做了这种机制,在SQL语句的后面添加 for update 子句
- 只有当A事务提交后,锁释放了,其他事务才能操作该条记录
乐观锁
- 采用版本号的机制来解决的,会给表结构添加一个字段version = 0 ,默认值是)
- 当A事务在操作完该条记录,提交事务时,会先检查版本号,如果发生版本号的值相同时,才可以提交事务。同时会更新版本号version = 1
- 当B事务操作完该条记录时,提交事务时,会先检查版本号,如果发现版本不同时,程序会出抛出异常。
//分别交替的逐行调试run1(),run2()方法,开启两个事务,查看数据库中的更新信息
@Test
public void run1(){
Session session = HibernateUtils.getsSession();
Transaction tr = session.beginTransaction();
//获取持久态的对象
User user = session.get(User.class,1);
//重新设置新的名称
user.setName("新");
tr.commit();
session.close();
}
@Test
public void run2(){
Session session = HibernateUtils.getsSession();
Transaction tr = session.beginTransaction();
//获取持久态的对象
User user = session.get(User.class,1);
//重新设置新的名称
user.setAge(67);
tr.commit();
session.close();
}
使用Hibernate框架解决丢失更新的问题
悲观锁
- 使用session.get(Customer.class 1.LockMode.UPDATE);方法
乐观锁
- 在对应的JavaBean中添加一个属性,名称可以是任意的。例如:private Integer version;提供get 和set 方法
- 在映射的配置文件中,提供标签即可。
//在JavaBean中添加属性
private Integer version;
//get 和set方法
// 在User.hbm.xml
<!--乐观锁-->
<class ,,,,,>
<!--name的值为JavaBean中的属性名称-->
<version name="version" />
</class>
//重新调试以上run1(),run2()方法
绑定本地的Session
在JavaWeb的事务处理中,需要在业务层使用Connection来开启事务
- 一种是通过参数的方式传递下去
- 另一种是吧Connection绑定到ThreadLocal对象中
在Hibernate框架中,使用session对象开启事务,所以需要来传递session对象,框架提供了ThreadLocal的方式
- 需要在Hibernate.cfg.xml的配置文件中提供配置
<property name = "hibernate.current_session_context_class">thread</property>
- 重新HibernateUtils的工具类,使用SessionFactory的getCurrentSession()方法,获取当前的Session对象,并且该Session对象不用手动关闭,线程结束了,会自动关闭。
public static Session getCurrentSession(){ return factory.getCurrentSession(); }
- 注意:想使用getCurrentSession()方法,必须要先配置才可以使用。
<!--web.xml-->
<servlet>
<servlet-name>SaveServlet</servlet-name>
<servlet-class>cm.zst.servlet.SaveServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>SaveServlet</servlet-name>
<url-pattern>/SaveServlet</url-pattern>
</servlet-mapping>
public class SaveServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
User u1 = new User();
u1.setName("测试1");
User u2 = new User();
u2.setName("测试2");
new UserService().save3(u1,u2);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
public class UserService {
public void save3(User u1,User u2){
UserDao dao = new UserDao();
dao.save1(u1);
// int a = 10/0;//在service层,打开此行,重新访问SaveServlet,会抛出异常,查看数据库信息,只更新一条数据
//在dao层开启的事务,这两个操作不在同一个事务中,正常情况下应该在service层开启事务
dao.save2(u2);
//提交事务
//出现问题:回滚事务
}
}
public class UserDao {
public void save1(User u1){
Session session = HibernateUtils.getsSession();
Transaction tr = session.beginTransaction();
session.save(u1);
tr.commit();
session.close();
}
public void save2(User u2){
Session session = HibernateUtils.getsSession();
Transaction tr = session.beginTransaction();
session.save(u2);
tr.commit();
session.close();
}
}
打开服务器,访问SaveServlet,查看数据库信息,如下
+----+-------+------+---------+
| id | name | age | version |
+----+-------+------+---------+
| 1 | 旧 | 67 | 1 |
| 2 | 小王 | 23 | 0 |
| 3 | 测试1 | NULL | 0 |
| 4 | 测试2 | NULL | 0 |
+----+-------+------+---------+
解决方法
<!--在hibernate.cfg.xml中配置-->
<!--开启绑定本地的session-->
<property name = "hibernate.current_session_context_class">thread</property>
//修改HibernateUtils工具类中的方法
public static Session getCurrentSession(){
return FACTORY.getCurrentSession();
}
//业务层改造完成
public class UserService {
public void save3(User u1,User u2){
UserDao dao = new UserDao();
//获取session
Session session = HibernateUtils.getCurrentSession();
//开启事务
Transaction tr = session.beginTransaction();
try{
dao.save1(u1);
// int a = 10/0;
dao.save2(u2);
//提交事务
tr.commit();
}catch (Exception e){
e.printStackTrace();
//出现问题:回滚事务
tr.rollback();
}fianlly{
//自己释放资源,现在,session不用关闭,线程结束后,自动关闭。
}
}
}
//dao层
public class UserDao {
public void save1(User u1){
Session session = HibernateUtils.getCurrentSession();
session.save(u1);
}
public void save2(User u2){
Session session = HibernateUtils.getCurrentSession();
session.save(u2);
}
}