详解Java的Spring框架中的事务管理方式

数据库事务是被当作单个工作单元的操作序列。这些操作要么全部完成或全部不成功。事务管理是面向企业应用程序,以确保数据的完整性和一致性RDBMS中的重要组成部分。事务的概念可以用下面的描述为ACID四个关键属性来描述:

原子性: 一个事务应该被视为单个操作单元表示的操作的任一整个序列是成功的或不成功的。

一致性: 这代表了数据库的参照完整性,在桌等唯一主键的一致性

隔离性: 可能有很多事务处理相同的数据集的同时,每个事务都应由他人隔离,以防止数据损坏。

持久性: 一旦事务完成,本次事务的结果必须作出永久性的,不能从数据库中删除因系统故障。

一个真正的RDBMS数据库系统将保证所有的四个属性为每个事务。颁发给使用SQL数据库的事务的简单观点如下:

使用begin transaction命令开始事务。

使用SQL查询执行各种删除,更新或插入操作。

如果所有的操作都成功,那么执行提交,否则回滚所有操作。

Spring框架提供的不同的底层事务管理API之上的抽象层。在Spring的事务支持,旨在通过增加事务功能,的POJO提供EJB的替代品事务。 Spring支持两种编程式和声明式事务管理。需要的EJB应用程序服务器,但Spring事务管理,而不需要一个应用服务器来实现。

局部与全局事务
局部事务是针对像一个JDBC连接一个单一的事务性资源,而全局事务可以跨越像事务多个事务资源的分布式系统。

局部事务管理可以在一个集中式计算环境下的应用程序的组件和资源都位于一个单一的网站是有用的,而事务管理只涉及一个单独的机器上运行的本地数据管理。局部事务更容易实现。

全局事务管理,需要在分布在多个系统中的所有资源的分布式计算环境。在这种情况下,事务管理既需要在地方和全局层面的工作要做。一个分布式或全局事务在多个系统上执行,其执行需要全局事务管理系统和所有相关系统的所有局部数据管理人员之间的协调。

编程与声明
Spring支持两种类型的事务管理:

  1. 编程式事务管理: Spring支持两种类型的事务管理:
  2. 声明式事务管理: 这意味着你的业务代码分开事务管理。你只用注释或基于XML 配置来管理事务。

编程式事务管理
编程式事务管理办法允许您管理与编程的源代码的帮助下事务。这就给了极大的灵活性,但它难以维护。

在我们开始之前,它至少有两个数据库表上,我们可以在事务的帮助下执行各种CRUD操作。让我们以Student表,它可以在MySQL数据库中测试用下面的DDL创建:

CREATE TABLE Student(
  ID  INT NOT NULL AUTO_INCREMENT,NAME VARCHAR(20) NOT NULL,AGE INT NOT NULL,PRIMARY KEY (ID)
);

第二个表是Marks,我们将保持标记为基于多年的学生。这里SID是表表的外键。

CREATE TABLE Marks(
  SID INT NOT NULL,MARKS INT NOT NULL,YEAR  INT NOT NULL
);

让我们使用PlatformTransactionManager直接实现编程的方法来实现事务。要开始一个新的事务,需要有TransactionDefinition 适当的事务属性的一个实例。在这个例子中,我们将简单地创建DefaultTransactionDefinition的实例使用默认的事务属性。

一旦TransactionDefinition被创建,你可以通过调用getTransaction()方法,它返回的TransactionStatus对象的一个实例开始事务。TransactionStatus对象有助于跟踪事务的当前状态,最后,如果一切顺利,可以使用提交(的PlatformTransactionManager的)方法来提交事务,否则可以使用rollback() 回滚完成操作。

现在我们编写Spring JDBC应用程序,将实现Student和Marks表简单的操作。
以下是数据访问对象接口文件StudentDAO.java的内容:

package com.jb51.cc;

import java.util.List;
import javax.sql.DataSource;

public interface StudentDAO {
  /** 
  * This is the method to be used to initialize
  * database resources ie. connection.
  */
  public void setDataSource(DataSource ds);
  /** 
  * This is the method to be used to create
  * a record in the Student and Marks tables.
  */
  public void create(String name,Integer age,Integer marks,Integer year);
  /** 
  * This is the method to be used to list down
  * all the records from the Student and Marks tables.
  */
  public List<StudentMarks> listStudents();
}

以下是StudentMarks.java文件的内容:

package com.jb51.cc;

public class StudentMarks {
  private Integer age;
  private String name;
  private Integer id;
  private Integer marks;
  private Integer year;
  private Integer sid;

  public void setAge(Integer age) {
   this.age = age;
  }
  public Integer getAge() {
   return age;
  }

  public void setName(String name) {
   this.name = name;
  }
  public String getName() {
   return name;
  }

  public void setId(Integer id) {
   this.id = id;
  }
  public Integer getId() {
   return id;
  }
  public void setMarks(Integer marks) {
   this.marks = marks;
  }
  public Integer getMarks() {
   return marks;
  }

  public void setYear(Integer year) {
   this.year = year;
  }
  public Integer getYear() {
   return year;
  }

  public void setSid(Integer sid) {
   this.sid = sid;
  }
  public Integer getSid() {
   return sid;
  }
}

以下是StudentMarksMapper.java文件的内容:

package com.jb51.cc;

import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.RowMapper;

public class StudentMarksMapper implements RowMapper<StudentMarks> {
  public StudentMarks mapRow(ResultSet rs,int rowNum) throws SQLException {

   StudentMarks studentMarks = new StudentMarks();

   studentMarks.setId(rs.getInt("id"));
   studentMarks.setName(rs.getString("name"));
   studentMarks.setAge(rs.getInt("age"));
   studentMarks.setSid(rs.getInt("sid"));
   studentMarks.setMarks(rs.getInt("marks"));
   studentMarks.setYear(rs.getInt("year"));

   return studentMarks;
  }
}

下面是实现类文件StudentJDBCTemplate.java的定义DAO接口StudentDAO:

package com.jb51.cc;

import java.util.List;
import javax.sql.DataSource;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

public class StudentJDBCTemplate implements StudentDAO {
  private DataSource dataSource;
  private JdbcTemplate jdbcTemplateObject;
  private PlatformTransactionManager transactionManager;

  public void setDataSource(DataSource dataSource) {
   this.dataSource = dataSource;
   this.jdbcTemplateObject = new JdbcTemplate(dataSource);
  }

  public void setTransactionManager(
   PlatformTransactionManager transactionManager) {
   this.transactionManager = transactionManager;
  }

  public void create(String name,Integer year){

   TransactionDefinition def = new DefaultTransactionDefinition();
   TransactionStatus status = transactionManager.getTransaction(def);

   try {
     String SQL1 = "insert into Student (name,age) values (?,?)";
     jdbcTemplateObject.update( SQL1,name,age);

     // Get the latest student id to be used in Marks table
     String SQL2 = "select max(id) from Student";
     int sid = jdbcTemplateObject.queryForInt( SQL2 );

     String SQL3 = "insert into Marks(sid,marks,year) " + 
            "values (?,?,?)";
     jdbcTemplateObject.update( SQL3,sid,year);

     System.out.println("Created Name = " + name + ",Age = " + age);
     transactionManager.commit(status);
   } catch (DataAccessException e) {
     System.out.println("Error in creating record,rolling back");
     transactionManager.rollback(status);
     throw e;
   }
   return;
  }

  public List<StudentMarks> listStudents() {
   String SQL = "select * from Student,Marks where Student.id=Marks.sid";

   List <StudentMarks> studentMarks = jdbcTemplateObject.query(SQL,new StudentMarksMapper());
   return studentMarks;
  }
}

现在让我们移动主应用程序文件MainApp.java,这是如下:

package com.jb51.cc;
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.jb51.cc.StudentJDBCTemplate;

public class MainApp {
  public static void main(String[] args) {
   ApplicationContext context = 
       new ClassPathXmlApplicationContext("Beans.xml");

   StudentJDBCTemplate studentJDBCTemplate = 
   (StudentJDBCTemplate)context.getBean("studentJDBCTemplate");
   
   System.out.println("------Records creation--------" );
   studentJDBCTemplate.create("Zara",11,99,2010);
   studentJDBCTemplate.create("Nuha",20,97,2010);
   studentJDBCTemplate.create("Ayan",25,100,2011);

   System.out.println("------Listing all the records--------" );
   List<StudentMarks> studentMarks = studentJDBCTemplate.listStudents();
   for (StudentMarks record : studentMarks) {
     System.out.print("ID : " + record.getId() );
     System.out.print(",Name : " + record.getName() );
     System.out.print(",Marks : " + record.getMarks());
     System.out.print(",Year : " + record.getYear());
     System.out.println(",Age : " + record.getAge());
   }
  }
}

以下是配置文件beans.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd ">

  <!-- Initialization for data source -->
  <bean id="dataSource" 
   class="org.springframework.jdbc.datasource.DriverManagerDataSource">
   <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
   <property name="url" value="jdbc:mysql://localhost:3306/TEST"/>
   <property name="username" value="root"/>
   <property name="password" value="password"/>
  </bean>

  <!-- Initialization for TransactionManager -->
  <bean id="transactionManager" 
   class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
   <property name="dataSource" ref="dataSource" />  
  </bean>

  <!-- Definition for studentJDBCTemplate bean -->
  <bean id="studentJDBCTemplate"
   class="com.jb51.cc.StudentJDBCTemplate">
   <property name="dataSource" ref="dataSource" />
   <property name="transactionManager" ref="transactionManager" />  
  </bean>
   
</beans>

创建源代码和bean配置文件完成后,让我们运行应用程序。如果一切顺利,这将打印以下信息:

------Records creation--------
Created Name = Zara,Age = 11
Created Name = Nuha,Age = 20
Created Name = Ayan,Age = 25
------Listing all the records--------
ID : 1,Name : Zara,Marks : 99,Year : 2010,Age : 11
ID : 2,Name : Nuha,Marks : 97,Age : 20
ID : 3,Name : Ayan,Marks : 100,Year : 2011,Age : 25

声明式事务管理
声明式事务管理的方法可帮助您管理配置,而不是在源代码中硬编码的事务。这意味着,可以单独从业务代码事务管理。只用注释或基于XML配置来管理事务。bean的配置将指定的方法是事务性。以下是声明性与事务相关的步骤:

我们使用<tx:advice/>标签,这将创建我们定义了一个切入点匹配所有我们想做成事务,并引用其中的事务通知方法的事务并同时处理建议。

如果一个方法的名字已被列入事务配置,然后创建意见,将调用该方法之前开始交易。

目标方法将在一个try/ catch块被执行。

如果方法正常完成,AOP的建议提交事务成功,否则执行回滚。

让我们来看看为何上述步骤的工作,但在我们开始之前,它至少有两个数据库表上,我们可以用交易的帮助下执行各种CRUD操作是很重要的。让我们以Student表,它可以在MySQL数据库中测试用下面的DDL创建:

CREATE TABLE Student(
  ID  INT NOT NULL AUTO_INCREMENT,PRIMARY KEY (ID)
);

第二个表是Marks ,我们将保持标记为基于多年的学生。这里SID是表Student的外键。

CREATE TABLE Marks(
  SID INT NOT NULL,YEAR  INT NOT NULL
);

同样来看一下相照应的例子。
以下是数据访问对象接口文件StudentDAO.java的内容:

package com.jb51.cc;

import java.util.List;
import javax.sql.DataSource;

public interface StudentDAO {
  /** 
  * This is the method to be used to initialize
  * database resources ie. connection.
  */
  public void setDataSource(DataSource ds);
  /** 
  * This is the method to be used to create
  * a record in the Student and Marks tables.
  */
  public void create(String name,int rowNum) throws SQLException {

   StudentMarks studentMarks = new StudentMarks();

   studentMarks.setId(rs.getInt("id"));
   studentMarks.setName(rs.getString("name"));
   studentMarks.setAge(rs.getInt("age"));
   studentMarks.setSid(rs.getInt("sid"));
   studentMarks.setMarks(rs.getInt("marks"));
   studentMarks.setYear(rs.getInt("year"));

   return studentMarks;
  }
}

下面是实现类文件StudentJDBCTemplate.java 的定义DAO接口StudentDAO:

package com.jb51.cc;

import java.util.List;
import javax.sql.DataSource;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;

public class StudentJDBCTemplate implements StudentDAO{
  private JdbcTemplate jdbcTemplateObject;

  public void setDataSource(DataSource dataSource) {
   this.jdbcTemplateObject = new JdbcTemplate(dataSource);
  }

  public void create(String name,Integer year){

   try {
     String SQL1 = "insert into Student (name,Age = " + age);
     // to simulate the exception.
     throw new RuntimeException("simulate Error condition") ;
   } catch (DataAccessException e) {
     System.out.println("Error in creating record,rolling back");
     throw e;
   }
  }

  public List<StudentMarks> listStudents() {
   String SQL = "select * from Student,Marks where Student.id=Marks.sid";

   List <StudentMarks> studentMarks=jdbcTemplateObject.query(SQL,new StudentMarksMapper());
   return studentMarks;
  }
}

现在我们移动主应用程序文件MainApp.java,如下:

package com.jb51.cc;
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {
  public static void main(String[] args) {
   ApplicationContext context = 
       new ClassPathXmlApplicationContext("Beans.xml");

   StudentDAO studentJDBCTemplate = 
   (StudentDAO)context.getBean("studentJDBCTemplate");
   
   System.out.println("------Records creation--------" );
   studentJDBCTemplate.create("Zara",Age : " + record.getAge());
   }
  }
}

以下是配置文件beans.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:tx="http://www.springframework.org/schema/tx"
  xmlns:aop="http://www.springframework.org/schema/aop"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
  http://www.springframework.org/schema/tx
  http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
  http://www.springframework.org/schema/aop
  http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

  <!-- Initialization for data source -->
  <bean id="dataSource" 
   class="org.springframework.jdbc.datasource.DriverManagerDataSource">
   <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
   <property name="url" value="jdbc:mysql://localhost:3306/TEST"/>
   <property name="username" value="root"/>
   <property name="password" value="cohondob"/>
  </bean>
 
  <tx:advice id="txAdvice" transaction-manager="transactionManager">
   <tx:attributes>
   <tx:method name="create"/>
   </tx:attributes>
  </tx:advice>
 
  <aop:config>
   <aop:pointcut id="createOperation" 
   expression="execution(* com.jb51.cc.StudentJDBCTemplate.create(..))"/>
   <aop:advisor advice-ref="txAdvice" pointcut-ref="createOperation"/>
  </aop:config>
 
  <!-- Initialization for TransactionManager -->
  <bean id="transactionManager"
  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
   <property name="dataSource" ref="dataSource" />  
  </bean>

  <!-- Definition for studentJDBCTemplate bean -->
  <bean id="studentJDBCTemplate" 
  class="com.jb51.cc.StudentJDBCTemplate">
   <property name="dataSource" ref="dataSource" /> 
  </bean>

</beans>

创建源代码和bean配置文件来完成,让我们运行应用程序。如果一切顺利,这将打印以下,将引发异常。在这种情况下,事务将回滚,并没有记录将在数据库表中创建。

------Records creation--------
Created Name = Zara,Age = 11
Exception in thread "main" java.lang.RuntimeException: simulate Error condition

你可以试试上面的例子中去除异常后,在这种情况下,应该提交事务,应该看到在数据库中的记录。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 目录 连接 连接池产生原因 连接池实现原理 小结 TEMPERANCE:Eat not to dullness;drink not to elevation.节制
摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 一个优秀的工程师和一个普通的工程师的区别,不是满天飞的架构图,他的功底体现在所写的每一行代码上。-- 毕玄 1. 命名风格 【书摘】类名用 UpperCamelC
今天犯了个错:“接口变动,伤筋动骨,除非你确定只有你一个人在用”。哪怕只是throw了一个新的Exception。哈哈,这是我犯的错误。一、接口和抽象类类,即一个对象。先抽象类,就是抽象出类的基础部分,即抽象基类(抽象类)。官方定义让人费解,但是记忆方法是也不错的 —包含抽象方法的类叫做抽象类。接口
Writer :BYSocket(泥沙砖瓦浆木匠)微 博:BYSocket豆 瓣:BYSocketFaceBook:BYSocketTwitter :BYSocket一、引子文件,作为常见的数据源。关于操作文件的字节流就是 —FileInputStream&amp;FileOutputStream。
作者:泥沙砖瓦浆木匠网站:http://blog.csdn.net/jeffli1993个人签名:打算起手不凡写出鸿篇巨作的人,往往坚持不了完成第一章节。交流QQ群:【编程之美 365234583】http://qm.qq.com/cgi-bin/qm/qr?k=FhFAoaWwjP29_Aonqz
本文目录 线程与多线程 线程的运行与创建 线程的状态 1 线程与多线程 线程是什么? 线程(Thread)是一个对象(Object)。用来干什么?Java 线程(也称 JVM 线程)是 Java 进程内允许多个同时进行的任务。该进程内并发的任务成为线程(Thread),一个进程里至少一个线程。 Ja
Writer :BYSocket(泥沙砖瓦浆木匠)微 博:BYSocket豆 瓣:BYSocketFaceBook:BYSocketTwitter :BYSocket在面向对象编程中,编程人员应该在意“资源”。比如?1String hello = &quot;hello&quot;; 在代码中,我们
摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 这是泥瓦匠的第103篇原创 《程序兵法:Java String 源码的排序算法(一)》 文章工程:* JDK 1.8* 工程名:algorithm-core-le
摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 目录 一、父子类变量名相同会咋样? 有个小故事,今天群里面有个人问下面如图输出什么? 我回答:60。但这是错的,答案结果是 40 。我知错能改,然后说了下父子类变
作者:泥瓦匠 出处:https://www.bysocket.com/2021-10-26/mac-create-files-from-the-root-directory.html Mac 操作系统挺适合开发者进行写代码,最近碰到了一个问题,问题是如何在 macOS 根目录创建文件夹。不同的 ma
作者:李强强上一篇,泥瓦匠基础地讲了下Java I/O : Bit Operation 位运算。这一讲,泥瓦匠带你走进Java中的进制详解。一、引子在Java世界里,99%的工作都是处理这高层。那么二进制,字节码这些会在哪里用到呢?自问自答:在跨平台的时候,就凸显神功了。比如说文件读写,数据通信,还
1 线程中断 1.1 什么是线程中断? 线程中断是线程的标志位属性。而不是真正终止线程,和线程的状态无关。线程中断过程表示一个运行中的线程,通过其他线程调用了该线程的 方法,使得该线程中断标志位属性改变。 深入思考下,线程中断不是去中断了线程,恰恰是用来通知该线程应该被中断了。具体是一个标志位属性,
Writer:BYSocket(泥沙砖瓦浆木匠)微博:BYSocket豆瓣:BYSocketReprint it anywhere u want需求 项目在设计表的时候,要处理并发多的一些数据,类似订单号不能重复,要保持唯一。原本以为来个时间戳,精确到毫秒应该不错了。后来觉得是错了,测试环境下很多一
纯技术交流群 每日推荐 - 技术干货推送 跟着泥瓦匠,一起问答交流 扫一扫,我邀请你入群 纯技术交流群 每日推荐 - 技术干货推送 跟着泥瓦匠,一起问答交流 扫一扫,我邀请你入群 加微信:bysocket01
Writer:BYSocket(泥沙砖瓦浆木匠)微博:BYSocket豆瓣:BYSocketReprint it anywhere u want.文章Points:1、介绍RESTful架构风格2、Spring配置CXF3、三层初设计,实现WebService接口层4、撰写HTTPClient 客户
Writer :BYSocket(泥沙砖瓦浆木匠)什么是回调?今天傻傻地截了张图问了下,然后被陈大牛回答道“就一个回调…”。此时千万个草泥马飞奔而过(逃哈哈,看着源码,享受着这种回调在代码上的作用,真是美哉。不妨总结总结。一、什么是回调回调,回调。要先有调用,才有调用者和被调用者之间的回调。所以在百
Writer :BYSocket(泥沙砖瓦浆木匠)一、什么大小端?大小端在计算机业界,Endian表示数据在存储器中的存放顺序。百度百科如下叙述之:大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加
What is a programming language? Before introducing compilation and decompilation, let&#39;s briefly introduce the Programming Language. Programming la
Writer :BYSocket(泥沙砖瓦浆木匠)微 博:BYSocket豆 瓣:BYSocketFaceBook:BYSocketTwitter :BYSocket泥瓦匠喜欢Java,文章总是扯扯Java。 I/O 基础,就是二进制,也就是Bit。一、Bit与二进制什么是Bit(位)呢?位是CPU
Writer:BYSocket(泥沙砖瓦浆木匠)微博:BYSocket豆瓣:BYSocket一、前言 泥瓦匠最近被项目搞的天昏地暗。发现有些要给自己一些目标,关于技术的目标:专注很重要。专注Java 基础 + H5(学习) 其他操作系统,算法,数据结构当成课外书博览。有时候,就是那样你越是专注方面越