JDBC事务 Hibernate事务 EJB事务详解

news2024/9/23 13:26:21

 

🏆今日学习目标:

🍀JDBC事务 Hibernate事务 EJB事务详解
创作者:林在闪闪发光
⏰预计时间:30分钟
🎉个人主页:林在闪闪发光的个人主页

 🍁林在闪闪发光的个人社区,欢迎你的加入: 林在闪闪发光的社区

目录

一、JDBC事务

1.事务的概念

2.JDBC中事务的相关方法 

3.一个简单的示例

二、HIbernate中的事务

1.HIbernate中的事务管理

三、EJB事务 


当你想要某种东西时,整个宇宙会合力助你实现愿望

                                                                                      《牧羊少年奇幻之旅》

一、JDBC事务

1.事务的概念

事务(Transaction),字面理解,一般是指要做的或所做的事情。

在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。事务通常由​​高级数据库​​​操纵语言或编程语言(如SQL,C++或Java)书写的​​用户程序​​的执行所引起,并用形如begin transactionend transaction语句(或​​函数调用​​)来界定。事务由事务开始(begin transaction)和事务结束(end transaction)之间执行的全体操作组成。

上述解释来自百度百科,如果你学过操作系统,可以将事务理解成原语,事务中的每件不可分割的子任务可以视为一条指令

在我们今天要讲的数据库中的事务,就可以理解为一条或一组SQL语句,这个事务成功的执行的必要条件就是其中的SQL全部执行成功,如果其中的任一SQL执行失败(没有达到预期效果),则此次事务执行失败,所有的SQL都不会对数据库中的数据产生变更。

事务具有4个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性

  • 原子性(Atomicity):一个事务是一个不可分割的工作单位,事务中包括的操作要么都做,要么都不做;
  • 一致性(Consistency):事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的;
  • 隔离性(Isolation):一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰;
  • 持久性(Durability):持久性也称永久性(Permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。

​ 对于其中的隔离性,数据库服务器有时会为了提供更好的处理并发能力,会牺牲一定的隔离性。这也是正确性和性能之间的对抗。如MySQL默认的隔离级别为READ COMMITTED,即读提交,可以读取其他事务已经提交的内容,可以避免脏读,但是无法避免​​重复度和幻读​​。

2.JDBC中事务的相关方法 

下面我们来看下,当使用JDBC操作数据库时,要如何使用事务。这里,我们要新介绍几个Connection接口中的几个方法,如下表所示:

当Connection自动提交模式为true的时候,即默认值,其所有SQL语句将作为单个事务执行并提交,每次SQL执行时会默认提交,不需手动触发;当自动提交模式为false时,其SQL语句会分组为事务(commit与commit之间的SQL或commit与rollback之间的SQL),这些事务通过调用​​commit​​方法提交,或调用​​rollback​​方法来进行回滚。

3.一个简单的示例

正常情况

public class TestTransaction1 {
    public static void main(String[] args) {
        Connection con = null;
        PreparedStatement st = null;
        ResultSet rs = null;

        try {
            con = JDBCUtils.getConnection();
            //关闭自动提交 自动会开启事务
            con.setAutoCommit(false);//开启事务
            // A 转 B 100元
            String sql1 = "update account set money=money-100 where name='A'";
            st = con.prepareStatement(sql1);
            st.executeUpdate();
            String sql2 = "update account set money=money+100 where name='B'";
            st = con.prepareStatement(sql2);
            st.executeUpdate();
            //业务完毕,提交事务
            con.commit();

            System.out.println("A 转 B 100元 成功!");
        } catch (SQLException e) {
            e.printStackTrace();
            try {
                con.rollback();   //如果失败就回滚事务
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        } finally {
            JDBCUtils.release(con, st, rs);
        }
    }
}

异常情况

public class TestTransaction2 {
    public static void main(String[] args) {
        Connection con = null;
        PreparedStatement st = null;
        ResultSet rs = null;

        try {
            con = JDBCUtils.getConnection();
            //关闭自动提交 自动会开启事务
            con.setAutoCommit(false);//开启事务
            // A 转 B 100元
            String sql1 = "update account set money=money-100 where name='A'";
            st = con.prepareStatement(sql1);
            st.executeUpdate();

            //int x=1/0;   //报错

            String sql2 = "update account set money=money+100 where name='B'";
            st = con.prepareStatement(sql2);
            st.executeUpdate();
            //业务完毕,提交事务
            con.commit();

            System.out.println("A 转 B 100元 成功!");
        } catch (SQLException e) {
            //如果失败则会默认回滚
            e.printStackTrace();
            /*try {
                con.rollback();   //如果失败就回滚事务
            } catch (SQLException ex) {
                ex.printStackTrace();
            }*/
        } finally {
            JDBCUtils.release(con, st, rs);
        }
    }
}

二、HIbernate中的事务

1.​​​​​​​HIbernate中的事务管理

在Hibernate中,可以通过代码来操作管理事务,如通过“Transaction tx = session.beginTransaction();” 开启一个事务:持久化操作后,通过“tx.commit();” 提交事务;如果事务出现异常,又通过“tx.rollback();” 操作来撤销事务(事务回滚)。

除了在代码中对事务开启,提交和回滚操作外,还可以在Hibernate的配置文件中对事务进行配置。配置文件中,可以设置事务的隔离级别。具体的配置方法是在hibernate.cfg.xml 文件中的 标签元素中进行的。如下:

		<!-- 指定hibernate操作数据库时的隔离级别 
			#hibernate.connection.isolation 1|2|4|8		
			0001	1	读未提交
			0010	2	读已提交
			0100	4	可重复读
			1000	8	串行化
		 -->
		 <property name="hibernate.connection.isolation">4</property>

 我们在真正进行事务管理的时候,需要考虑事务的应用场景,也就是说我们的事务控制不应该是在DAO层实现的,应该在Service层实现,并且在Service中调用多个DAO实现一个业务逻辑的操作。
具体操作如下显示:

 

其实最主要的是如何保证在Service中开启的事务时使用的Session对象和DAO中多个操作使用的是同一个Session对象。
其实由两种办法可以实现:

  • 可以在业务层获取到Session,并将Session作为参数传递给DAO。
  • 可以使用ThreadLocal 将业务层获取的Session绑定到当前线程中,然后在DAO中获取Session的时候,都从当前线程中获取。

其实使用第二种方式肯定是最优方案,那么具体的实现已经不用我们来完成了,Hibernate的内部已经帮我们将这个事情做完了。我们只需要完成一段配置即可。

Hibernate5 中自身提供了三种管理Session对象的方法:

  • t h r e a d : S e s s i o n 对 象 的 生 命 周 期 与 本 地 线 程 绑 定 。 \color{red}{thread:Session对象的生命周期与本地线程绑定。} thread:Session对象的生命周期与本地线程绑定。
  • jta:Session 对象的生命周期与 JTA 事务绑定。
  • managed:Hibernate委托程序来管理Session对象的生命周期。

在hibernate.cfg.xml 中进行如下配置:

		<!-- 指定session与当前线程绑定 -->
		 <property name="hibernate.current_session_context_class">thread</property>

 

Hibernate提供sessionFactory.getCurrentSession() 创建一个session 和ThreadLocal 绑定方法。

书写一个HibernateUtils工具类:

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

public class HibernateUtils {
	private static SessionFactory sf;
	
	static{
		//1 创建,调用空参构造
		Configuration conf = new Configuration().configure();
		//2 根据配置信息,创建 SessionFactory对象
		 sf = conf.buildSessionFactory();
	}
	
	//获得session => 获得全新session
	public static Session openSession(){
		//3 获得session
		Session session = sf.openSession();
		return session;
	}
	
	//获得session => 获得与线程绑定的session
	public static Session getCurrentSession(){
		//3 获得session
		Session session = sf.getCurrentSession();
		return session;
	}
	
}

而且Hibernate中提供的这个与线程绑定的session可以不用关闭,当线程执行结束后,就会自动关闭了。

测试方法:



import org.hibernate.Session;
import org.junit.Test;

import pers.zhang.utils.HibernateUtils;

//测试getCurrentSession
public class Demo {

	@Test
	//返回同一个与线程绑定的session
	public void fun1(){
		Session session1 = HibernateUtils.getCurrentSession();
		Session session2 = HibernateUtils.getCurrentSession();
		
		System.out.println(session1==session2);//true
	}
	
	@Test
	//返回不同的session
	public void fun2(){
		Session session1 = HibernateUtils.openSession();
		Session session2 = HibernateUtils.openSession();
		
		System.out.println(session1==session2);//false
	}
	
	
}

输出

true

false

三、EJB事务 

在我们对事务的基本概念以及出现的问题和隔离级别有进一步的了解之后,接下来看看EJB是如何进行事务管理.

       在EJB中有两种使用事务的方式。第一种方式通过容器管理的事务,叫CMT(Container-Managed Transaction),另一种通过Bean管理的事务叫BMT(Bean-Managed Transaction)。

       如果使用容器来管理事务,那么EJB组件就不需要显式地给出begin 、commit 、abort 语句,EJB 容器会替我们考虑这些内容。EJB 容器会依据EJB组件提供者指定的事务行为来界定相应的事务边界。

      在使用容器管理事务时,EJB 容器会拦截客户请求,并自动为EJB组建启动新的事务,也就是说,容器会通过begin 语句调用底层事务系统,从而启动事务。随后,容器会将业务请求委派给EJB组件,组件中的业务操作将运行在这一事务中。处于事务中的EJB 组件能够执行任何业务逻辑,如写入数据库、发送异步信息、调用其他的EJB组件等。一旦在处理业务过程中出现问题,则EJB 组建需要通知EJB 容器去回滚事务。当EJB 组建完成业务处理后,会将控制权交回给EJB 容器。随后,EJB容器能够通过commit 或abort 语句调用底层事务系统。

       我们可以使用@TransactionAttribute注释或部署描述符来指定事务属性。EJB 容器通过分析事务属性便能够知道如何处理EJB 组件的事务需求。

        如果用简短的话总结上面的内容就是,用CMT管理事务,事务都是被容器管理的,开发人员不需要对事务进行管理,需要做的就是配置事务属性.

      EJB 事务属性的取值有以下几种:

(1 )Required ,如果EJB组件必须总是运行在事务中,则应该使用Required 模式。如果已经有事务在运行,则EJB 组件参与其中;如果没有事务运行,则EJB 容器会为EJB组件启动新的事务。

          Required 是默认和最常使用的事务属性值。这个值指定必须在事务之内调用EJB方法。如果从非事务性客户端调用方法,那么容器会在调用方法之前开始事务,并且在方法返回时结束事务。另一方面,如果调用者从事务性上下文调用方法,那么方法会联结已有事务。在从客户段传播事务的情况下,如果我们的方法表示应该回滚事务,那么容器不仅回回滚整个事务,而且会向客户端抛出异常,从而让客户端知道它开始的事务已经被另一个方法回滚了。

(2 )Requires_New,当客户调用EJB 时,如果总是希望启动新的事务,则应该使用RequiresNew 事务属性,如果客户在调用EJB组件时已经存在事务,则当前事务会被挂起,进而容器启动新的事务,并将调用请求委派给EJB组件。也就是说,如果客户端已经有了事务,那么它暂停该事务,知道方法返回位置,新事务是成功还是失败都不会影响客户端已有的事务。EJB组件执行相应的业务操作,容器会提交或回滚事务,最终容器将恢复原有的事务,当然,如果客户在调用EJB 组件时不存在事务,则不需要执行事务的挂起或恢复操作。

          RequiresNew 事务属性非常有用。如果EJB 组件需要事务的ACID属性,并且将EJB 组件运行在单个独立的工作单元中,从而不会将其他外部逻辑也包括在当前的事务中,则必须使用RequiredNew事务属性。如果需要事务,但是不希望事务的回滚影响客户端,就应该使用它。另外,当不希望客户端的回滚影响你的时候,也应该使用这个值。

(3 )Supports ,如果某个EJB组件使用了Supports 事务属性,则只有调用它的客户已经启用了事务时,这一EJB 组件才会运行在事务中。如果客户并没有运行在事务中,则EJB组建也不会运行在事务中。Supports 同Required 事务属性很相似,但是,Required 要求EJB 组件必须运行在事务中。如果使用Support事务属性,EJB 组建很可能没有运行在事务中。

(4 )Mandatory ,Mandatory事务属性要求调用EJB 组件的客户必须已经运行在事务中。如果从非事务性客户端调用使用Mandatory 属性的EJB方法,那么客户将接受到系统抛出的javax.ejb.EJBTransactionRequiredException 异常。EJB 组件使用Mandatory事务属性是非常安全的,它能够保证EJB 组建运行在事务中。如果客户没有运行在事务中,则不能够调用到应用了Mandatory 事务属性的EJB组件。但是,Mandatory 事务属性要求第3 方(及客户)在调用EJB 组件前必须启动了事务。EJB 容器并不会为Mandatory事务属性自动启动新事务,这是同Support 事务属性的最主要区别。

(5 )NotSupported ,如果EJB组件使用了NotSupport事务属性,它根本不会参与到事务中。如果调用者使用相关联的事务调用方法,容器就会暂停事务,调用方法,然后再方法返回时恢复事务。通常,此属性只用于非实物性的自动确认模式中,支持JMS提供者的MDB 。

(6 )Never ,如果EJB组件使用Never 事务属性,它就不能够参与到事务中,而且,如果调用它的客户已经处于事务中,则容器会将javax.ejb.EJBException异常抛给客户。

当然在上面所列出的属性中我们最常用的还是Required具体可以看下面的一段片段代码:
 

 
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Stateless(name = "UserManager")
@Remote
@TransactionManagement(TransactionManagementType.CONTAINER)
public class UserManagerBean implements UserManager {

	@PersistenceContext
	private EntityManager em ;
	@TransactionAttribute(TransactionAttributeType.REQUIRED)
	public void addUser(String name) {

		User s = new User();
		s.setName(name);
		em.persist(s);
		System.out.println("服务器端执行成功:保存姓名" + name);

	}

}

 

 上面的例子中

    @TransactionManagement(TransactionManagementType.CONTAINER)

表示指定事务的类型。如果省略,默认为CMT方式。

    @TransactionAttribute(TransactionAttributeType.REQUIRED)

通知容器如何管理事务,事务的属性控制了事务的使用范围,因为事务之间的关系非常的复杂,这个属性主要是用来处理事务与事务之间怎样来处理的的问题。

至此 事务已全部输出完毕 欢迎小伙伴补充

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/431274.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【学习笔记】Integral Human Pose Regression

【学习资料】一文总结Integral Pose Regression方法的方方面面 - 知乎&#xff08;总结的很到位&#xff0c;一定要去看&#xff09; 1.两种基本方法的对比 1.解码方式 softmax和argmax的区别&#xff1a; 由于深度神经网络中的下采样步骤&#xff0c;热图的分辨率低于输入…

如何在电脑本地下载镜像重装系统

现在网上随处可以下载操作系统&#xff0c;下载下来的是镜像系统&#xff0c;很多朋友都不知道电脑镜像重装系统是什么意思&#xff0c;怎么用镜像重装系统&#xff0c;今天小编就给大家带来了电脑镜像重装系统是什么意思的相关解答&#xff0c;一起往下看。 电脑镜像重装系统是…

从FPGA说起的深度学习(七)-循环并行化

这是新的系列教程&#xff0c;在本教程中&#xff0c;我们将介绍使用 FPGA 实现深度学习的技术&#xff0c;深度学习是近年来人工智能领域的热门话题。在本教程中&#xff0c;旨在加深对深度学习和 FPGA 的理解。用 C/C 编写深度学习推理代码高级综合 (HLS) 将 C/C 代码转换为硬…

不良事件上报系统源码开发,不良事件上报系统源码

不良事件管理系统源码&#xff0c;有演示&#xff0c;支持二开&#xff0c;可正常上线运营。 相关技术&#xff1a;PHPvscodevue2elementlaravel8mysql5.7 文末获取联系&#xff01; 医院安全不良事件上报系统&#xff0c;对患者安全&#xff08;不良&#xff09;事件实施全过…

微分方程的基本概念(通解、特解,线素场)

微分方程的基本概念(通解、特解&#xff0c;线素场)1 微分方程的定义 同学们大家好&#xff0c;今天我们来学习微分方程的基础概念。 微分方程就是含有导数的方程&#xff0c;例如&#xff1a; 它就含有导数 &#xff0c;因此它就是一个微分方程。而我们知道导数的写法不止一…

androidstudio虚拟机运行react-native项目踩坑指南

androidstudio虚拟机运行react-native项目踩坑指南安装JDK安装android studio配置环境变量新建虚拟机新建RN项目运行项目本文详细的记录了照react-native官网文档运行项目踩到的所有坑&#xff0c;诚然&#xff0c;官网只介绍了每一步&#xff0c;最后确实是可以正常运行项目&a…

VS2022配置Opencv贴心教程

所用VS2022是官网Professional版本&#xff0c;OpenCV版本是4.7.0 一、下载OpenCV 官网下载地址&#xff1a;Releases - OpenCV 选择Windows版本下载并解压到本地磁盘&#xff0c;建议路径不带中文&#xff0c;我的解压安装地址是&#xff1a; C:\opencv 二、配置Windows环…

快速部署个人-ChatGPT Next Web

前提&#xff1a;要有梯子、谷歌账号。 目录 一、源码地址&#xff1a; 二、演示地址&#xff1a; 三、获取API密钥 四、 部署 五、重新部署 一、源码地址&#xff1a; GitHub - Yidadaa/ChatGPT-Next-Web: One-Click to deploy well-designed ChatGPT web UI on Verc…

1.Shell编程自动化之Shell编程基础

一、Shell可以用来做什么 1.自动化批量系统初始化程序&#xff1b; 2.自动化批量软件部署程序&#xff1b; 3.应用程序管理&#xff1b; 4.日志分析处理程序&#xff1b; 5.自动化备份恢复程序&#xff1b; 6.自动化信息采集及监控程序&#xff1b; 7.自动化管理程序&am…

Python数据结构-----leetcode232.用栈实现队列

目录 前言&#xff1a; 方法讲解 示例&#xff1a; 代码实现 232. 用栈实现队列 前言&#xff1a; 我们都知道队列的特征是先进先出&#xff0c;就跟排队一样先到先得&#xff0c;而栈的特征是后进后出&#xff0c;那这里我们怎么去通过两个栈来实现一个队列的功能呢&#xf…

GitHub和Gitee的源码下载

1.使用clone命令下载 如果本地安装了Git环境的话&#xff0c;可以直接在命令行中使用git clone命令把仓库中的文件全部下载到本地。 通过GitHub下载源码&#xff0c;执行如下命令&#xff1a; git clone https://github.com/******.git其中后面下载链接可以从项目下图处查看:…

excel动态获取sheet页单元格内容

1、问题描述 如下图所示&#xff0c;名称列可能是动态赋值的&#xff0c;名称列的内容有对应新的sheet页&#xff0c;如名称为PJ1及其PJ1的sheet页&#xff0c;最终需要获取PJ1的sheet页的B1单元格的内容。 如下图所示&#xff0c;是要获取PJ1的sheet页的B1的值。 2、解决办法…

Qt音视频开发33-vlc和mpv打开后鼠标打圈圈问题的解决

一、前言 如果采用的vlc句柄模式,如果鼠标停留在句柄控件中会发现在打开后鼠标打圈圈,mpv句柄模式是在关闭后鼠标打圈圈,这两者真是一前一后,这种给人的体验其实很不友好的,播放开始后或者播放完成后鼠标指针居然变成了繁忙,但是当你将鼠标位置从句柄控件中移到外面的时…

瑟瑟发抖吧~OpenAI刚刚推出王炸——引入ChatGPT插件,开启AI新生态

5分钟学会使用ChatGPT 插件&#xff08;ChatGPT plugins&#xff09;——ChatGPT生态建设的开端ChatGPT插件是什么OpenAI最新官方blog资料表示&#xff0c;已经在ChatGPT中实现了对插件的初步支持。插件是专门为以安全为核心原则的语言模型设计的工具&#xff0c;可帮助ChatGPT…

电脑CPU/GPU处理器知识普及

处理器知识普及 处理器主要分为两种&#xff1a;CPU与GPU&#xff0c;二者针对不同的业务进行工作&#xff1b; CPU主要处理数量小、难度大的任务&#xff0c;能比较好的处理单线程任务&#xff1b; GPU主要处理数量达&#xff0c;难度小的任务&#xff0c;比如图形渲染、多线…

C语言实现三子棋教学

本篇博客会教你如何使用C语言实现三子棋。主要包含以下步骤&#xff1a; 初始化棋盘。打印棋盘。玩家下棋。电脑下棋。判断输赢 0.预备工作 先定义一些符号&#xff0c;后面会用到。主要是棋盘的大小&#xff08;行数列数&#xff09;&#xff0c;以及棋子。 #define ROW …

skvideo.io.vread无法读取视频(九天毕生版)

Vread无法读取视频 使用九天GPU时遇到的错误以及解决方法: 、vread无法读取视频 需要下载ffmpeg的exe&#xff08;从网上找&#xff09; 下载ffmpeg.exe&#xff08;一共三个&#xff09;后将exe的上级目录&#xff08;bin&#xff09;文件路径添加到系统路径中&#xff08;…

Grounding DINO-开集目标检测论文解读

文章目录摘要背景算法3.1Feature Extraction and Enhancer3.2. Language-Guided Query Selection3.3. Cross-Modality Decoder3.4. Sub-Sentence Level Text Feature3.5. Loss Function实验4.2 Zero-Shot Transfer of Grounding DINOCOCO数据集LVIS数据集ODinW&#xff0c;开放…

超级账本与区块链应用场景

文章目录 区块链3.0去中心化应用的新需求区块链技术在行业应用中的条件区块链3.0架构与超级账本 区块链3.0架构 超级账本(Hyperledger Fabric)超级账本的项目FabricFabric的典型运行模型在Fabric中完成一次交易的整体步骤Fabric的节点 链码(Chaincode)数字身份证书组织通道 区块…

Java之 重载 重写的区别

重载 在同一个类中&#xff0c;多个方法有相同的方法名&#xff0c;但参数列表不同&#xff0c;这种同名不同参的方法就是重载重写 子类在继承父类方法的基础上&#xff08;方法名和参数列表相同&#xff09;&#xff0c;对父类方法的实现进行覆盖的操作叫重写规则 重载的规则…