一、事务介绍
事务简介:
事务管理是企业级应用程序开发中必不可少的技术,用来确保数据的完整性和一致性
事务就是一系列的动作,它们被当作一个单独的工作单元。这些动作要么全部完成,要么全部不起作用。
事务的四个关键属性(ACID)
① 原子性(atomicity):事务室一个原子操作,有一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用
② 一致性(consistency):一旦所有事务动作完成,事务就被提交。数据和资源就处于一种满足业务规则的一致性状态中
③ 隔离性(isolation):可能有许多事务会同时处理相同的数据,因此每个事物都应该与其他事务隔离开来,防止数据损坏
④ 持久性(durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响。通常情况下,事务的结果被写到持久化存储器中
数据隔离级别
隔离级别 |
脏读 |
不可重复读 |
幻象读 |
第一类丢失更新 |
第二类丢失更新 |
READ UNCOMMITED |
允许 |
允许 |
允许 |
不允许 |
允许 |
READ COMMITED |
不允许 |
允许 |
允许 |
不允许 |
允许 |
REPEATABLE READ |
不允许 |
不允许 |
允许 |
不允许 |
不允许 |
SERIALIZABLE |
不允许 |
不允许 |
不允许 |
不允许 |
不允许 |
SqlServer2008R2的默认隔离级别是“READ COMMITED”,MySQL的默认隔离级别是“REPEATABLE READ”。
Spring中的事务管理
作为企业级应用程序框架,Spring在不同的事务管理API之上定义了一个抽象层。而应用程序开发人员不必了解底层的事务管理API,就可以使用Spring的事务管理机制。
Spring既支持编程式事务管理,也支持声明式的事务管理
编程式事务管理:将事务管理代码嵌入到业务方法中来控制事务的提交和回滚,在编程式事务中,必须在每个业务操作中包含额外的事务管理代码
声明式事务管理:大多数情况下比编程式事务管理更好用。它将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。事务管理作为一种横切关注点,可以通过AOP方法模块化。Spring通过Spring AOP框架支持声明式事务管理。
Spring事务的传播属性:
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。
事务的传播行为可以由传播属性指定。Spring定义了7种传播行为:
PROPAGATION_REQUIRED |
如果当前没有事务,就新建一个事务,如果已经存在一个事务,加入到这个事务中。这是最常见的选择。 |
PROPAGATION_SUPPORTS |
支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY |
使用当前的事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW |
新建事务,如果当前存在事务,把当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED |
以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER |
以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED |
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 |
并发事务所导致的问题
在同一个应用程序或者不同应用程序中的多个事务在同一个数据集上并发执行时,可能会出现许多意外的问题。
并发事务所导致的问题可以分为以下三类:
① 脏读:脏读发生在一个事务读取了另一个事务改写但尚未提交的数据时。如果改写在稍后被回滚了,那么第一个事务获取的数据就是无效的。
② 不可重复读:不可重复读发生在一个事务执行相同的查询两次或两次以上,但是每次都得到不同的数据时。这通常是因为另一个并发事务在两次查询期间更新了数据
③ 幻读:幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录
二、实例开发
Spring声明式事务让我们从复杂的事务处理中得到解脱,使得我们再也不必去处理获得连接、关闭连接、事务提交和回滚等这些操作,再也无需我们在与事务相关的方法中处理大量的try…catch…finally代码。
我们在使用Spring声明式事务时,有一个非常重要的概念就是事务属性。事务属性通常由事务的传播行为、事务的隔离级别、事务的超时值、事务只读标志组成。我们在进行事务划分时,需要进行事务定义,也就是配置事务的属性。 Spring在TransactionDefinition接口中定义这些属性,以供PlatfromTransactionManager使用,PlatfromTransactionManager是spring事务管理的核心接口。
下面用一个例子来说明Spring事务管理的好处
新建立一个java工程,导入相关的包,事个工程最终目录如下:
本文工程免费下载
这里应用到了mysql,需要建立数据库test及其下的数据表book_table
- create database test;
- use test;
- CREATE TABLE book_table (bookname varchar(100) NOT NULL, bookid int(11) NOT NULL PRIMARY KEY) ;
1、数据表对应的model类:
- package com.mucfc.model;
-
- public class Book {
- private String bookName;
- private int bookId;
-
- public Book(String bookName, int bookId) {
- super();
- this.bookName = bookName;
- this.bookId = bookId;
- }
- public Book(){
-
- }
- @Override
- public String toString() {
- return "Book [bookName=" + bookName + ", bookId=" + bookId + "]";
- }
- public String getBookName() {
- return bookName;
- }
- public void setBookName(String bookName) {
- this.bookName = bookName;
- }
- public int getBookId() {
- return bookId;
- }
- public void setBookId(int bookId) {
- this.bookId = bookId;
- }
-
- }
2、接着是DAO层:
- package com.mucfc.dao;
- import com.mucfc.model.Book;
-
-
-
-
-
- public interface LibraryDao {
-
-
-
-
-
- public Book getBook(String name);
-
-
-
-
- public void addBook(Book book);
-
-
-
-
- public void deleteBook(String name);
-
- }
实现类:
- package com.mucfc.dao;
- import java.sql.ResultSet;
- import java.sql.SQLException;
- import java.util.*;
-
- import org.springframework.jdbc.core.JdbcTemplate;
- import org.springframework.jdbc.core.RowMapper;
-
- import com.mucfc.model.Book;
-
-
-
-
-
- public class LibraryDaoImpl implements LibraryDao{
- private JdbcTemplate jdbcTemplate;
- public JdbcTemplate getJdbcTemplate() {
- return jdbcTemplate;
- }
-
- public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
- this.jdbcTemplate = jdbcTemplate;
- }
-
- @Override
- public Book getBook(String name) {
- String sql="SELECT * FROM book_table WHERE bookname=?";
- Book mBook = (Book)jdbcTemplate.queryForObject(sql,new Object[]{name},new RowMapper<Object>(){
- @Override
- public Object mapRow(ResultSet arg0, int arg1) throws SQLException {
- Book book=new Book();
- book.setBookId(arg0.getInt("bookid"));
- book.setBookName(arg0.getString("bookname"));
- return book;
- }
- });
- return mBook;
- }
-
- @Override
- public void addBook(Book book) {
- String sql="INSERT INTO book_table VALUES(?,?)";
- jdbcTemplate.update(sql, book.getBookName(),book.getBookId());
-
- }
-
- @Override
- public void deleteBook(String name) {
- String sql="DELETE FROM book_table WHERE bookname=?";
- jdbcTemplate.update(sql,name);
- }
-
- }
3、事务配置
- <?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:context="http://www.springframework.org/schema/context"
- xmlns:aop="http://www.springframework.org/schema/aop" xmlns:p="http://www.springframework.org/schema/p"
- xsi:schemaLocation="
- http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
- http://www.springframework.org/schema/aop
- http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context-3.0.xsd">
-
- <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="christmas258@" />
- </bean>
-
- <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
- <property name="dataSource" ref="dataSource" />
- </bean>
-
- <bean id="txManager"
- class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <property name="dataSource" ref="dataSource" />
- </bean>
-
- <bean id="libraryTarget" class="com.mucfc.dao.LibraryDaoImpl"
- p:jdbcTemplate-ref="jdbcTemplate" />
-
-
- <bean id="libraryFactory"
- class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
- p:transactionManager-ref="txManager" p:target-ref="libraryTarget">
-
- <property name="transactionAttributes">
- <props>
-
- <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
-
- <prop key="addBook">PROPAGATION_REQUIRED</prop>
- </props>
- </property>
-
- </bean>
- </beans>
4、测试使用:
- package com.mucfc.service;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
- import com.mucfc.dao.LibraryDao;
- import com.mucfc.model.Book;
-
- public class Test {
-
- public static void main(String[] args) {
- ApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml");
- Book book1=new Book("西游记",1);
- Book book2=new Book("红楼梦",2);
- Book book3=new Book("金瓶梅",3);
- Book book4=new Book("三国演义",4);
- Book book5=new Book("水浒传",5);
- LibraryDao libraryDaoImpl=(LibraryDao)ctx.getBean("libraryFactory");
- libraryDaoImpl.addBook(book1);
- System.out.println(libraryDaoImpl.getBook("西游记"));
-
- }
-
- }
输出结果:
这里只输出了一条
数据库中的结果:
5、上面是正常时的情况,现在我们想让它出现异常,并自动回滚。简单。
LibraryDaoImpl类中插入的时候多插一个相同的数据
- @Override
- public void addBook(Book book) {
- String sql="INSERT INTO book_table VALUES(?,?)";
- jdbcTemplate.update(sql, book.getBookName(),book.getBookId());
- jdbcTemplate.update(sql, book.getBookName(),book.getBookId());
- }
然后先把数据库内容清空:
- delete from test.book_table;
其它地方都不变:
执行程序后:
检查数据库:
发现一条数据都没有插入进去,所以说全部都回滚了。
如果这时我们不用Spring的事务管理,插入还是插入两条相同的,结果又会怎么样?
把Test类中的
- LibraryDao library=(LibraryDao)ctx.getBean("libraryFactory");
改成
- LibraryDao library=(LibraryDao)ctx.getBean("libraryTarget");
其它还是不变
- @Override
- public void addBook(Book book) {
- String sql="INSERT INTO book_table VALUES(?,?)";
- jdbcTemplate.update(sql, book.getBookName(),book.getBookId());
- jdbcTemplate.update(sql, book.getBookName(),book.getBookId());
- }
执行后
发现在抛出异常后,还是插入了一条数据,说明没有回滚事务。
三、基于tx/aop的配置
之前学了AOP,可以在想要的类或方法前面或后面织入增强,同理也可以用到这里来
上面的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:context="http://www.springframework.org/schema/context"
- xmlns:aop="http://www.springframework.org/schema/aop" xmlns:p="http://www.springframework.org/schema/p"
- xmlns:tx="http://www.springframework.org/schema/tx"
- 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
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context-3.0.xsd">
-
- <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="christmas258@" />
- </bean>
-
- <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
- <property name="dataSource" ref="dataSource" />
- </bean>
-
- <bean id="txManager"
- class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <property name="dataSource" ref="dataSource" />
- </bean>
-
- <bean id="libraryTarget" class="com.mucfc.dao.LibraryDaoImpl"
- p:jdbcTemplate-ref="jdbcTemplate" />
-
-
- <aop:config>
-
- <aop:pointcut id="serviceMethod"
- expression="execution(* com.mucfc.dao.LibraryDaoImpl.*(..))" />
-
- <aop:advisor pointcut-ref="serviceMethod" advice-ref="txAdvice" />
- </aop:config>
-
-
- <tx:advice id="txAdvice" transaction-manager="txManager">
-
- <tx:attributes>
- <tx:method name="get*" read-only="false" />
- <tx:method name="add*" rollback-for="Exception" />
- <tx:method name="del*" />
- </tx:attributes>
- </tx:advice>
- </beans>
AOP会自动将事务织入到相应的类的方法之上。重点代码如下
-
- <aop:config>
-
- <aop:pointcut id="serviceMethod"
- expression="execution(* com.mucfc.dao.LibraryDaoImpl.*(..))" />
-
- <aop:advisor pointcut-ref="serviceMethod" advice-ref="txAdvice" />
- </aop:config>
-
-
- <tx:advice id="txAdvice" transaction-manager="txManager">
-
- <tx:attributes>
- <tx:method name="get*" read-only="false" />
- <tx:method name="add*" rollback-for="Exception" />
- <tx:method name="del*" />
- </tx:attributes>
- </tx:advice>
上面的这一段是重点。
接下来要来测试一上,把之前的LibraryDaoImpl类中的方法改成如下:
- @Override
- public void addBook(Book book) throws Exception{
- String sql="INSERT INTO book_table VALUES(?,?)";
- int num=(int)book.getBookId();
- for(int i=1;i<=20;i++){
- jdbcTemplate.update(sql, book.getBookName(),num++);
- if(i==8) throw new Exception();
- }
-
- }
这里就是想插入20条数据,但是第8条插入时抛出异常,所以说最终应该会回滚,也就是说,一条数据也没有。
测试类:
- package com.mucfc.service;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- import com.mucfc.dao.LibraryDao;
- import com.mucfc.model.Book;
-
- public class Test {
-
- public static void main(String[] args) {
- ApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml");
- Book book1=new Book("西游记",1);
- LibraryDao library=(LibraryDao)ctx.getBean("libraryTarget");
- try {
- library.addBook(book1);
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- }
- }
结果:
然后看看数据库中有没有插入
那如果注释掉这一段,结果又会如何呢?把AOP增强给注释,其它都不变。理论来讲应该会提交了8条数据
- <!-- 使用tx/aop来配置 -->
- lt;!-- <aop:config>
- 通过aop定义事务增强切面
- <aop:pointcut id="serviceMethod"
- expression="execution(* com.mucfc.dao.LibraryDaoImpl.*(..))" />
- 引用事务增强
- <aop:advisor pointcut-ref="serviceMethod" advice-ref="txAdvice" />
- </aop:config>
-
- 事务增强
- <tx:advice id="txAdvice" transaction-manager="txManager">
- 事务属性定义
- <tx:attributes>
- <tx:method name="get*" read-only="false" />
- <tx:method name="add*" rollback-for="Exception" />
- <tx:method name="del*" />
- </tx:attributes>
- </tx:advice> -->
其它都不变,执行结果:
结果表明,如果 不进行事务管理,如果 出现错误,还是提交了数据操作。这不符合要求!
四、一些说明
- <tx:advice id="defaultTxAdvice">
- <tx:attributes>
- <tx:method name="get*" read-only="true"/>
- <tx:method name="*"/>
- <tx:method name="add*" propagation="REQUIRED"/>
- nbsp;</tx:attributes>
- </tx:advice>
需要注意的地方:
(1) advice(建议)的命名:由于每个模块都会有自己的Advice,所以在命名上需要作出规范,初步的构想就是模块名+Advice(只是一种命名规范)。
(2) tx:attribute标签所配置的是作为事务的方法的命名类型。
如<tx:method name="save*" propagation="REQUIRED"/>
其中*为通配符,即代表以save为开头的所有方法,即表示符合此命名规则的方法作为一个事务。
propagation="REQUIRED"代表支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
(3) aop:pointcut标签配置参与事务的类,由于是在Service中进行数据库业务操作,配的应该是包含那些作为事务的方法的Service类。
首先应该特别注意的是id的命名,同样由于每个模块都有自己事务切面,所以我觉得初步的命名规则因为 all+模块名+ServiceMethod。而且每个模块之间不同之处还在于以下一句:
expression="execution(* com.test.testAda.test.model.service.*.*(..))"
其中第一个*代表返回值,第二*代表service下子包,第三个*代表方法名,“(..)”代表方法参数。
(4) aop:advisor标签就是把上面我们所配置的事务管理两部分属性整合起来作为整个事务管理。
<tx:method/>
有关的设置
属性
是否需要?
默认值
描述
name |
是 |
|
与事务属性关联的方法名。通配符(*)可以用来指定一批关联到相同的事务属性的方法。如:'get*' 、'handle*' 、'on*Event' 等等。
|
propagation |
不 |
REQUIRED |
事务传播行为(配置文章开头) |
isolation |
不 |
DEFAULT |
事务隔离级别(配置看文章开头)propagation="REQUIRED"代表支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。 |
timeout |
不 |
-1 |
事务超时的时间(以秒为单位) |
read-only |
不 |
false |
事务是否只读? |
rollback-for |
不 |
|
将被触发进行回滚的 Exception(s) ;以逗号分开。 如:'com.foo.MyBusinessException,ServletException'
|
no-rollback-for |
不 |
|
不 被触发进行回滚的Exception(s) ;以逗号分开。 如:'com.foo.MyBusinessException,ServletException'
|
相关推荐
在Spring框架中,声明式事务管理是实现事务处理...在博文"Spring使用XML配置声明式事务"中,作者详细讲解了每个步骤,并可能通过示例代码展示了如何实际应用这些配置,帮助读者更好地理解和掌握Spring声明式事务管理。
提供的XML配置示例展示了如何在Spring中配置声明式事务。具体来说,该示例包括以下几个关键组件: 1. **SessionFactory Bean**:用于配置Hibernate的SessionFactory实例,它是Hibernate的核心组件之一,负责创建...
2. **声明式事务管理**:Spring.NET提供了基于XML配置和基于属性的两种声明式事务管理。在XML配置中,可以在`<transaction:advice>`元素中定义事务行为,然后通过`<aop:config>`或`<aop:aspect>`将事务行为应用到...
下面是一个简单的示例,展示了如何使用XML配置文件来配置Spring的声明式事务: ```xml <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> *" ...
而声明式事务管理则通过配置元数据(如XML或注解)来定义事务的边界和传播行为,这种方式更为简洁且易于维护。 ### 数据库连接配置与Hibernate整合 在给定的示例中,首先展示了数据库连接的配置文件,该文件遵循...
### Spring事务管理——声明式事务配置详解 #### 引言 在现代的Java企业级应用开发中,事务处理是确保数据一致性和完整性的重要机制。Spring框架提供了强大的事务管理功能,支持编程式和声明式两种事务管理方式。...
- 在Spring XML配置文件中,事务管理器(如`DataSourceTransactionManager`或`JtaTransactionManager`)通常会定义在`<bean>`标签中,并通过`<tx:annotation-driven>`来启用声明式事务管理。 5. **数据库兼容性**...
声明式事务管理是通过配置的方式设置事务的边界,通常通过Spring的`@Transactional`注解或者XML配置来实现。这种方式比编程式事务管理更为简洁和方便。 **示例:传统的JDBC事务管理** 在使用JDBC进行数据操作时,...
在Spring中,声明式事务管理通常基于AOP(面向切面编程)实现,可以通过XML配置或Java配置来声明事务。例如,在`spring-beans.xml`配置文件中,我们可能会看到如下配置: ```xml ``` 这段代码表示启用基于注解的...
Spring AOP(面向切面编程)是Spring框架的重要组成部分,它提供了一种模块化和声明式的方式来实现横切关注点,如日志、事务管理、性能监控等。本入门案例将帮助你理解并掌握Spring AOP的基本概念和使用方法。 在...
Spring通过与AOP的结合,提供了声明式事务管理,允许开发者仅通过配置(如XML或注解)来定义事务边界,而无需在业务代码中显式地管理事务。这种方式极大地简化了事务处理,提高了代码的可读性和可维护性。 #### 四...
通过在方法级别上指定事务属性,可以实现声明式事务管理,这样无需在业务代码中手动开始和结束事务,降低了代码的复杂性。 通过这种方式,Spring与Hibernate结合使用,可以轻松地管理事务,确保数据的一致性,并且...
而声明式事务管理则是通过配置元数据(如XML或注解)来定义事务边界,更易于维护和使用。Spring 2.5中,声明式事务管理可以通过`@Transactional`注解实现,使得事务管理更加简洁直观。 Hibernate作为ORM框架,允许...
2. **自动事务管理**:MyBatis可以与Spring框架集成,利用Spring的声明式事务管理。在这种模式下,只需要在XML配置或注解中声明事务的边界,MyBatis会自动管理事务的开始、提交和回滚。这简化了代码,并减少了出错的...
6. **事务管理**:Spring 提供了声明式和编程式的事务管理,帮助我们处理数据库事务。在"springdemo"中,可能会看到@Transactional注解的使用,以确保数据操作的一致性。 7. **测试支持**:Spring 提供了JUnit和...
- **事务管理方式**:Spring支持编程式事务管理和声明式事务管理。声明式事务管理更加简洁,通常使用`@Transactional`注解进行事务控制。 9. **补充内容** - **Spring中引入集合的操作**:Spring支持集合类型的...
让我们深入探讨这些概念以及如何在Spring中实现声明式事务管理。 **1. Inversion of Control (IoC)** IoC,也称为依赖注入(Dependency Injection),是一种设计模式,它将对象的创建和管理权交给了框架,而不是由...
编程式事务管理允许在代码中显式控制事务的开始、提交、回滚等操作,而声明式事务管理则通过配置或注解来指定哪些方法需要在事务中执行,降低了事务管理的复杂性。 Spring还包含了Spring MVC,这是一个用于构建Web...
3. **事务管理**:Spring提供了声明式事务管理,可以方便地在XML或注解中定义事务规则,保证数据的一致性。 4. **MVC(Model-View-Controller)模式**:在Spring MVC中,控制器负责处理请求,模型管理业务逻辑,...