Hibernate的事务管理 ---- Hibernate入门学习

版权声明:转载时请注明出处! https://blog.csdn.net/qq_40762011/article/details/82379813

叙:本章中电虫本人开始并不算熟悉,因此,电虫细致的梳理了一遍,可能会显得东西有些多,但是却是事务这个知识点比较全面、深入检出的笔记;

Hibernate事务管理

学习hibernate的事务管理前首先学习一下什么是事务?事务中存在哪些问题?如何解决的?这些都是事务的基础知识,其后才是hibernate的事务管理设置;

事务基础

什么是事务
事务在Java中就是指一个逻辑上的一组操作,组成这个逻辑的逻辑单元要么全部完成,要么全部失败;

事务的特性
1) 原子性:代表事务是不可分割的,要么同时完成要么同时失败,存在例外;
2) 一致性:代表数据执行前后数据保持一致,例如:甲乙买卖,最终两个人的钱和货物相加还是原始量,不会有不明下落的数据;
3) 隔离性:意思是指一个事物的执行过程中不应该和其他的事务有交集;
4) 持久性:代表事务执行完后,数据就会被持久存放到数据库中,不会被执行过的事务所调用;

在事务的特性中重点需要了解的是事物的隔离性,下面进行详细了解;

如果不考虑事务的隔离,会导致的问题:

隔离的目的是为了解决不同的事务同时并发的情况所会存在的问题,防止事务间的相互影响;如果不考虑事务的隔离,则会引发多种问题,总结为两大类,分别是读的问题和写的问题;

读的问题:
1)脏读:是指事物读B到事务A未提交的数据 — 既是:事务B读到的是事务A可能要提交也可能要回滚的数据;
2)不可重复度:是指事务A在进行操作数据并且读取到了数据,而此时事务B突然插队进来也操作数据,并且在A事务提交前进行了事务提交,则会导致事务A读取前后的数据不一致和操作不成功;
3)虚读(幻读):事务A开始操作数据库,把表中数据全部修改成某种样式的,但与此同时,事务B也在修改数据表,数据B的是添加一条原始样式的数据,在两个人都提交后,表中只有一条数据是原始样式的,当事务A回头查看时会发现又一条数据没有修改成新样式;

注意:这个幻读和不可重复度很容易搞混;
辨析:幻读是指之前查询不存在的,中途被其他事务添加了,再次查询却出现了,而不可重复度是指之前查询存在的,中途被其他事务修改了,再次查询却不同了;区别点就在于第一个事务第一次查询是否存在这个数据;

写的问题(了解即可)
1) 引发丢失更新
A事务在回滚时把B事务已经提交的更新数据也给回滚了(当然也有可能是A事务提交后覆盖了B事务的更新操作,导致需要的事务B操作的丢失了更新的结果,不一定非要是回滚,只是回滚更方便理解)!

事务的解决 — 事务的隔离级别

设置事务隔离级别(4种):

隔离级别名称 隔离读取级别 解决问题级别
Read uncommitted 读取未提交的数据 以上的读问题都会发生
Read committed 读取已提交的数据 除脏读其他均可能发生
Repeatable read 读取到已经修改过的 只有幻读可能会发生
Serializable 读问题都可以解决 串行化序列执行(例:排队上厕所,一个出来后另一个才能用)效率太低

隔离级别处理问题能力表:
隔离级别处理问题能力表
Oracle使用的是 read committed 的隔离级别来配置事务管理;
MySQL使用的是 repeatable read 的隔离级别来配置事务管理;

扫描二维码关注公众号,回复: 3189630 查看本文章

事务进阶 — hibernate中设置隔离级别

上面回顾了不隔离导致的问题以及各个隔离级别的作用范围等,现在学习在hibernate中如何设置隔离级别;

hibernate中设置事务的隔离级别
首先,要知道hibernate中设置事务的隔离级别是在核心配置文件中进行的,其次,所使用的标签以及标签所对应的属性值,具体介绍如下:
在核心文件配置中的<session-factory></session-factory>标签内进行定义的,所使用的方法举例为<property name=”hibernate.connection.isolation”>4</property>
全部代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <!-- 核心配置 -->
    <session-factory>
        <!--连接数据库的基本参数 -->
        <!-- 方言 。。。 dialect -->
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect </property>
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql:///hibernate_day02</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">root</property>

        <!-- 可选配置 -->
        <property name="hibernate.show_sql">true</property>
        <property name="hibernate.format_sql">true</property>
        <property name="hibernate.hbm2ddl.auto">update</property>

        <!-- 配置隔离级别 -->
        <property name="hibernate.connection.isloation">4</property>

        <!-- 引入orm数据源路径 -->
        <mapping resource="com/java/demo/Customer.hbm.xml" />
    </session-factory>
</hibernate-configuration>

配置标签、属性什么的都好说,只要记住就好,主要注意的是标签中间的数字,例中是4,还有其他的1,2,8;这些数字代表的是其对应的隔离级别,具体介绍如下:

隔离级别对应数值 隔离级别名称 备注
1 read uncommitted
2 read committed Oracle使用的隔离级别
4 repeatable read MySQL使用的隔离级别
8 serializable

以上就是核心配置文件中的配置方法,配置完核心配置中的后需要使用,具体的是service层在使用;

Service层事务

事务管理等是在Service层的,事务层 = Service层;
为什么要把事务加在Service层?
答:三层架构,Web层、Service层、Dao层;Dao层里封装的是一个个对数据源单一操作的方法,每个对数据的增删改查的业务要分为四个方法,每个方法都要重新创建一个单独的连接(Hibernate中的是Session,JDBC中的是Connection),这样不便于业务逻辑的管理;而在service层中封装的是一个业务逻辑操作(最少两个的对数据源操作的Dao层方法,就比如转账的业务逻辑,由转出方扣钱和转入方加钱的业务统一成一个业务逻辑),service层便于此业务逻辑的事物管理;

回顾事务管理在service层中:
Service层事务管理管理的是一个业务逻辑,其中连接Dao层的多个对数据源单一操作的方法,这样的话就需要使用同一个连接,这个连接在Hibernate中就是Session。
在JDBC中使用的有两种方法,一种是在Service层创建完后传递给Dao层,Dao层的进行调用创建好的同一个连接,第二种是使用ThreadLocal(线程本地变量/线程本地储存)对象,这个对象的原理是将连接保存到线程中,通过线程来传递连接到Dao层;
Hibernate中的事务管理:
在Hibernate框架中提供的有已绑定好的ThreadLocal,存在于SessionFactory中,方法名叫做getCurrentSession(),此方法默认是关闭的,需要手动设置打开,操作方法:
1) 工具类;向生成一般的Session对象所在的工具类中添加可以生成getCurrentSession()方法;添加部分如下所示:

package com.java.hibernate.Utils;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

/*
 * hibernate生成session的工具类
 */
public class HibernateUtils {

    public static final Configuration conf;
    public static final SessionFactory sf;

    static{
        //加载读取核心配置文件对象
        conf = new Configuration().configure();
        //使用核心配置文件对象创建Session工厂,生成链接session连接对象
        sf = conf.buildSessionFactory();
    }

    public static Session openSession(){
        return sf.openSession();
    }
    //添加 --- 生成线程session代码
    public static Session getCurrentSession(){
        return sf.getCurrentSession();
    }
}

2) 配置允许使用getCurrentSession()方法的操作;在核心配置文件中进行配置,配置如下所示:

<property name="hibernate.current_session_context_class">thread</property>

其属性有三个,分别是:
(1) Thread:Session对象生命周期与本地线程绑定!!!
(2) jta:Session对象生命周期与JTA事务绑定(跨数据库事务);
(3) managed: Hibernate委托程序来管理session生命周期;
3) 测试

@Test
public void demo1(){
        Session session = HibernateUtils.getCurrentSession();
        Transaction bt = session.beginTransaction();

        Customer cust = session.load(Customer.class, 1l);
        System.out.println(cust);

        bt.commit();
    }

能通过就算成功

注意:在使用此方法进行操作时后不需要有释放资源的操作(即:session.close()),因为线程会自动关闭一次,如果自己再手动关闭Session就会报错;


《本章完》

猜你喜欢

转载自blog.csdn.net/qq_40762011/article/details/82379813