Spring中的事务操作、注解及XML配置详解

事务

事务全称叫数据库事务,是数据库并发控制时的基本单位,它是一个操作集合,这些操作要么不执行,要么都执行,不可分割。例如我们的转账这个业务,就需要进行数据库事务的处理。

转账中至少会涉及到两条 SQL 语句:

update Acoount set balance = balance - money where id = 'A';
update Acoount set balance = balance + money where id = 'B'

上面这两条 SQL 就可以要看成是一个事务,必须都执行,或都不执行。如何保证呢,一般这样表示:

# 开启事务
begin transaction

update Account set balance = balance - money where id = 'A';
update Account set balance = balance + money where id = 'B'
# 提交事务
commit transaction

Exception
 # 回滚事务
 rollback transaction

事务的特性(笔试的时候会有)

Atomic(原子性):事务中包含的操作被看做一个逻辑单元,这个逻辑单元中的操作要么全部成功,要么全部失败。

Consistency(一致性):只有合法的数据可以被写入数据库,否则事务应该将其回滚到最初状态。在转账的时候不会出现一当少钱了,另一方没有增加的情况。

Isolation(隔离性):事务允许多个用户对同一个数据进行并发访问,而不破坏数据的正确性和完整性。同时,并行事务的修改必须与其他并行事务的修改相互独立。

Durability(持久性):事务完成之后,它对于系统的影响是永久的,该修改即使出现系统故障也将一直保留,真实的修改了数据库。

以上 4 个属性常被简称为 acid(酸的)。

事务并发的问题

脏读:事务二读取到事务一中已经更新但是还没有提交的数据,这就是脏读。

不可重复读:一个事务两次读取同一个行数据结果不同,因为有其它的事务对数据进行了更新。此时的数据即为不可重复读数据。

幻读:同一事务执行两次查询,结果不一致,因为中间有其它的事务对数据进行更改。

如何解决这些问题呢?数据库系统为事务设置了 4 种不同的隔离级别。

事务隔离级别

读未提交(read uncommitted):最低级别,可能会导入脏读。

读已提交(read committed):可以避免脏读,只能查询到已经提交的数据。且具有良好的性能,但是不能避免不可重复读和幻读。

可重复读(repeatable):解决了不可重复读,可能会出现幻读。

串行化(serializable):通过加锁,使同一时间只能执行一个事务,不出现上述问题,但是可能会导致大量的超时现象和锁竞争。

另外,MySQL 中默认的隔离级别是可重复读。Oracle 中默认的事务隔离级别是读已提交。

说完事务,想想我们曾经为了处理事务而写过的那些代码。最后在说说 Spring 中是如何处理的,学完 Spring 再也不用担心事务操作了。

在 JDBC 时代我们需要这样手动的处理事务。

// 获取连接 conn
conn.setAutoCommit(false); 设置提交方式为手工提交
// 业务代码
// 减钱
// 加钱
conn.commit(); 提交事务
// 出现异常
conn.rollback(); 回滚事务

我们说处理事务那是处理数据库事务,所以肯定要先有数据库连接才能说事务的事,而在 Java 中我们连接数据库无非就是 JDBC,或是对 JDBC 进一步的封装,比方说 Hibernate ,Mybatis 或是 Dbutils 这些框架,所以万变不离其宗,就是这么回事,就是看谁封装的好罢了。你们有兴趣可以看看它们都是如何封装的。

Spring 中的如何管理事务呢

首先,我们知道 Spring 是一个容器,不同的框架在处理事务时用到的对象不同,原生的 JDBC 使用 Connection ,而 Mybatis 中使用 SqlSession 对象。而 Spring 为了整合这些不同的框架,定义了一个 PlatformTransactionManager 接口来统一标准,对不同的框架又有不同的实现类。

在 Spring 中根据 DAO 层技术的不同来选择不同的事务处理的对象,是 JDBC 时使用 DataSourceTransactionManager,是 Hibernate 时使用 HibernateTransitionmanager 对象,核心对象就是 Transitionmanager。

在 Spring 中管理事务会涉及到这几个属性,事务隔离级别、是否只读、事务的传播行为,说到事务的传播行为,指的就是不同的业务方法之间相互调用时,应该如何管理事务。Spring 中一共定义了 7 种传播行为,无脑记住使用 required ,表示支持当前事务,若是不存在事务,就创建一个。例如在 A 调用 B 的时候,会首先使用 A 的事务,若 A 没有事务,则新创建一个,不管 B 有没有事务。

下面就是要实际操作一下,需要有具体的业务逻辑,还是那个转账的例子。来看看如何使用 Spring 来管理事务,有两种常见的管理方式,我们一种一种的说。

使用 XML 配置

1 首先是导包,Spring 涉及的包是真的多,我有一个省事的方法,可能用到的 jar 包一下子导入。

2 导入新的约束文件,不然在 XML 无法使用 tx 标签。

3 准备目标对象和通知并配置。

目标对象 AccountServiceImpl

public class AccountServiceImpl implements AccountService {

 private AccountDAO ad;

 @Transactional(isolation=Isolation.REPEATABLE_READ,propagation = Propagation.REQUIRED,readOnly=false)
 @Override
 public void transfer(Integer from, Integer to, Double money) {

 ad.decreaseMoney(from, money);

 //int i = 1/0;

 ad.increaseMoney(to, money);
 }

 public void setAd(AccountDAO ad) {
 this.ad = ad;
 }
}

在 AOP 中通知即为增强的代码,而在处理事务时,要增强的代码无非就是开启事务,提交事务和回滚事务,所以 Spring 已经为我们封装好了处理事务的通知,我们只需要配置一下即可。

<!-- 导入 properties 配置文件 -->
<context:property-placeholder location="classpath:db.properties"/>
<!-- 配置连接数据库的核心处理对象-->
<bean name="transactionManager" class = "org.springframework.jdbc.datasource.DataSourceTransactionManager">
 <property name="dataSource" ref= "dataSource"></property>
</bean>
<!-- 配置通知-->
<tx:advice id="txAdvise" transaction-manager="transactionManager">
 <tx:attributes>
 <tx:method name="transfer" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>
 </tx:attributes>
</tx:advice>
<!-- 配置 AOP ,以达成自动处理事务的要求-->
<aop:config>
 <aop:pointcut expression="execution(* yu.transation.*ServiceImpl.*(..))" id="txPointcut"/>

 <aop:advisor advice-ref="txAdvise" pointcut-ref="txPointcut"/>
</aop:config>

<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" >
 <property name="jdbcUrl" value="${jdbc.jdbcUrl}" ></property>
 <property name="driverClass" value="${jdbc.driverClass}" ></property>
 <property name="user" value="${jdbc.user}" ></property>
 <property name="password" value="${jdbc.password}" ></property>
</bean>
<!-- 配置 DAO 层对象-->
<bean name="ad" class="yu.transation.AccountDaoImpl">
 <property name="dataSource" ref = "dataSource"></property>
</bean>
<!-- 配置 Service 层对象-->
<bean name = "accountService" class = "yu.transation.AccountServiceImpl">
 <property name="ad" ref = "ad"></property>
</bean>

上面的配置文件中,在配置通知时,我们具体到不同的方法会有不同的配置,在项目应用时,会使用通配符来进行配置。下面介绍一下使用注解来处理事务,看起来会比较简单。

步骤和上面有重复的部分,需要导包导入约束,接下来就是配置一下使用注解管理事务的开关

使用注解配置

<!-- 打开注解配置 AOP 事务 -->
<tx:annotation-driven/>

下面是使用注解为 Service 中的方法配置事务处理的属性,当然,每一个方法都写会比较麻烦,也可以在类上面使用注解,若是某个方法的处理规则不一致就单独使用注解配置一下。

@Transactional(isolation=Isolation.REPEATABLE_READ,propagation = Propagation.REQUIRED,readOnly=false)
@Override
public void transfer(Integer from, Integer to, Double money) {...}

回顾一下,以上主要说了事务以及 Spring 中处理事务的方式,而这也正是 AOP 思想在 Spring 中的应用,我们可以看到不管是前面说的 IoC 还是 AOP 在这里都有体现。

注解的出现是为了替换配置文件,所以我就以配置文件为主,并说一下与之对应的注解方式。

Spring 中的配置主要在核心配置文件 applicationContext.xml 中,由不同的标签来表示,所以首先我们就需要导入各种约束,常用的约束有 bean、context、aop、tx 。

bean 标签是最基本的标签,主要用来配置各种对象。

<!--
属性介绍:
id: 为对象命名,唯一性标识,不能重复,不能使用特殊字符。
name: 和 id 的作用类似,区别在于可是使用特殊字符,可重复,但是不建议重复。
class: 指定对象的全类名。
init-method: 对象初始化之后立即执行的方法。
destroy-method: 对象销毁之前执行的方法。
scope: 对象的作用范围,可以设置单例 singleton 和多例 prototype。默认为单例
 -->
<bean name="userService" class="yu.service.UserServiceImpl" >
 <property name="" value="" ></property>
 <property name="" ref="" ></property>
</bean>

对应的注解有以下几个,但是想要使用注解之前要首先配置一下……

<!-- 打开注解配置,扫描包及其子包 -->
<context:component-scan base-package="yu"></context:component-scan>

使用注解的时候,我们可以使用 @Component 来表示将这个对象交由 Spring 管理,@Scope 来指定对象的作用域。之后便可以使用 @Resource 来获取对象。

在注册对象的时候我们可以使用 @Component ,但是若是每一个对象都是用这个注解,不能很好的分辨出对象属于哪一层,所以 Spring 又提供了 @Controller @Service @Repository 来分别表示控制器层,Service 层和 DAO 层的对象,功能和 @Component 是一模一样的。

同样的在为对象赋值的时候,我们可以使用注解 @Autowired 来自动获取容器中的对象,可是若是有重名的情况就需要另外一个注解 @Qualifier 来具体指定叫什么名字,这样就有点麻烦了,我们一般都是直接使用 @Resource 来指定对象。

@Component("user")
@Scope("prototype")
public class User{
 private String name;

 @Value(value = "18") // 属性注入,项目中不用。
 private Integer age;

 //@Autowired 自动装配 Car 类型变量,同一类型
 //@Qualifier("car") 指定具体的是哪一个。
 @Resource(name = "car") // 指名道姓指定是哪个对象
 private Car car;

 ...

 @PostConstruct
 public void init(){
 System.out.println("init 方法");
 }
 @PreDestroy
 public void destroy(){
 System.out.println("destory 方法");
 }
}

aop 相关的配置和注解

在 Spring 中我们可以自定义通知和切面,下面只是展示了如何配置,但是在具体的业务中应该不会出现 5 种通知齐上阵的现象。

<aop:config>
 <!-- 配置切点-->
 <aop:pointcut expression="execution(* yu.service.*ServiceImpl.*(..))" id="pc"/>

 <aop:aspect ref="myAdvice" >
  <!-- 指定名为before方法作为前置通知 -->
  <aop:before method="before" pointcut-ref="pc" />
  <!-- 后置 -->
  <aop:after-returning method="afterReturning" pointcut-ref="pc" />
  <!-- 环绕通知 -->
  <aop:around method="around" pointcut-ref="pc" />
  <!-- 异常拦截通知 -->
  <aop:after-throwing method="afterException" pointcut-ref="pc"/>
  <!-- 后置 -->
  <aop:after method="after" pointcut-ref="pc"/>
 </aop:aspect>
</aop:config>

同样的,我们也可以使用注解来达到自定义配置的方式。同样的套路,想用注解配置实现 aop,需要打开注解配置 AOP 的开关。

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

之后就是在通知类中进行配置即可。

@Aspect
//通知类
public class MyAdvice {

 // 快速配置切点表达式,方法直接调用即可
 @Pointcut("execution(* yu.service.*ServiceImpl.*(..))")
 public void pc(){}

 //前置通知
 @Before("MyAdvice.pc()")
 public void before(){
  System.out.println("这是前置通知!!");
 }
 //后置通知
 @AfterReturning("MyAdvice.pc()")
 public void afterReturning(){
  System.out.println("这是后置通知(如果出现异常不会调用)!!");
 }

context 主要是和全局有关的配置

<!-- 打开注解配置对象,扫描包及其子包 -->
<context:component-scan base-package="yu"></context:component-scan>

<!-- 导入 properties 配置文件 -->
<context:property-placeholder location="classpath:db.properties"/>

<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" >
 <property name="jdbcUrl" value="${jdbc.jdbcUrl}" ></property>
 <property name="driverClass" value="${jdbc.driverClass}" ></property>
 <property name="user" value="${jdbc.user}" ></property>
 <property name="password" value="${jdbc.password}" ></property>
</bean>

tx 配置事务管理中的通知

tx 用来配置通知对象,而这个对象是由 Spring 为我们写好了,而事务管理依赖于数据库连接对象,所以你能看到 transactionManager 对象依赖于 dataSource 对象。

<bean name="transactionManager" class = "org.springframework.jdbc.datasource.DataSourceTransactionManager">
 <property name="dataSource" ref= "dataSource"></property>
</bean>

<tx:advice id="txAdvise" transaction-manager="transactionManager">
 <tx:attributes>
  <tx:method name="transfer" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>
 </tx:attributes>
</tx:advice>

<aop:config>
 <aop:pointcut expression="execution(* yu.transation.*ServiceImpl.*(..))" id="txPointcut"/>

 <aop:advisor advice-ref="txAdvise" pointcut-ref="txPointcut"/>
</aop:config>

使用注解配置时还是需要打开注解配置的开关

<!-- 打开注解配置 AOP 事务 -->
<tx:annotation-driven/>

在具体的业务方法上或是类上使用注解 @Transactional 来配置事务处理的方式。

@Transactional(isolation=Isolation.REPEATABLE_READ,propagation = Propagation.REQUIRED,readOnly=false)
@Override
public void transfer(Integer from, Integer to, Double money) {...}

最后有一个完美的意外,那就是 import 标签。用于导入其它的配置模块到主配置文件中。

<!-- 导入其它的 Spring 配置模块 -->
<import resource="yu/transation/applicationContext.xml"/>

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • spring Mvc配置xml使ResponseBody返回Json的方法示例

    前言 学习了spring mvc后,发现spring mvc返回json数据比struts2要方便,使用@ResponseBody就可以了 @ResponseBody 在返回的数据不是html标签的页面,而是其他某种格式的数据时(如json.xml等)使用: 不在springMvc中配置json的处理的话,我们通常会在Controller层中获取到数据之后进行类型转化,将数据转成json字符串,比如调用fastjson进行转化,如下 @RequestMapping("/getCategoryTr

  • SpringBoot通过yml和xml文件配置日志输出方法

    SpringBoot中默认使用Logback进行日志输出,可以同时使用SpringBoot框架的配置文件application.yml或是通过logback的配置文件logback.xml进行配置. 通过application.yml配置 <?xml version="1.0" encoding="UTF-8"?> <configuration debug="false"> <!--定义日志文件的存储地址 勿在 Lo

  • 如何完成spring的最小化XML配置

    一.自动装配 1.四种类型的自动装配 类型 解释 xml 配置 byName 根据 Bean 的 name 或者 id <bean id="bean" class="-" autowire="byName"/> ByType 根据 Bean 类型自动装配 <bean id="bean" class="-" autowire="byType"/> contructo

  • spring mvc 读取xml文件数据库配置参数的方法

    本文主要介绍怎么通过属性注入与构造器注入实现把我们项目中要用到的数据库参数放到xml文件里面去,方便部署. spring mvc 4.2.6项目 SQL Server 2008数据库 本文介绍的主要使用ApplicationContext以及其实现类实现.主要用到的是ClassPathXmlApplicationContext. ClassPathXmlApplicationContext:从类路径ClassPath中寻找指定的XML配置文件,找到并装载 完成ApplicationContext

  • Spring装配Bean教程之XML安装配置bean详解

    前言 众所周知在Spring刚出现的时候,XML是描述配置的主要方式,在Spring的名义下,我们创建了无数行XML代码.在一定程度上,Spring成为了XML的同义词. 现在随着强大的自动化配置和Java代码的配置出现,XML不再是唯一选择,也不应该是首选,学习XML配置,更多用于维护已有的XML的配置.下面话不多说了,来一起看看详细的介绍吧. 创建XML配置规范 在使用XML配置前,需要创建一个新的配置规范,就像JavaConfig需要我们创建带有 @Configuration注解的类,而在

  • Spring根据XML配置文件 p名称空间注入属性的实例

    要生成对象并通过名称空间注入属性的类 代码如下: package com.swift; public class User { private String userName; public void setUserName(String userName) { this.userName = userName; } public String fun() { return "User's fun is ready."+this.userName; } } XML配置文件写法如下: &

  • Spring根据XML配置文件注入属性的方法

    方法一使用setter方法 package com.swift; public class Book { private String bookName; public void setBook(String bookName) { this.bookName = bookName; } @Override public String toString() { return "Book [book=" + bookName + "]"; } } 在Spring框架中

  • spring接口通过配置支持返回多种格式(xml,json,html,excel)

    1. 简介 本文主要给大家介绍使用SpringMVC的后端服务如何通过配置来支持多种返回值类型(xml,json,html,excel) 这里的代码使用的是springboot,下载地址:https://github.com/xiagn825/springboot-todolist/tree/springboot-ContentNegotiation 2. 基础概念 2.1 HttpHeader中Content-Type和Accept设置的区别 Accept:接口要返回给客户端的数据格式 cur

  • Spring中的事务操作、注解及XML配置详解

    事务 事务全称叫数据库事务,是数据库并发控制时的基本单位,它是一个操作集合,这些操作要么不执行,要么都执行,不可分割.例如我们的转账这个业务,就需要进行数据库事务的处理. 转账中至少会涉及到两条 SQL 语句: update Acoount set balance = balance - money where id = 'A'; update Acoount set balance = balance + money where id = 'B' 上面这两条 SQL 就可以要看成是一个事务,必

  • Spring中属性文件properties的读取与使用详解

    Spring中属性文件properties的读取与使用详解 实际项目中,通常将一些可配置的定制信息放到属性文件中(如数据库连接信息,邮件发送配置信息等),便于统一配置管理.例中将需配置的属性信息放在属性文件/WEB-INF/configInfo.properties中. 其中部分配置信息(邮件发送相关): #邮件发送的相关配置 email.host = smtp.163.com email.port = xxx email.username = xxx email.password = xxx

  • 基于Spring中各个jar包的作用及依赖(详解)

    先附spring各版本jar包下载链接http://repo.spring.io/release/org/springframework/spring/ spring.jar 是包含有完整发布模块的单个jar 包.但是不包括mock.jar, aspects.jar, spring-portlet.jar, and spring-hibernate2.jar 示例图片为Spring-2.5.6.jar的包目录 下面讲解各个jar包的作用: 1.org.springframework.aop或sp

  • Mybatis中 XML配置详解

    Mybatis常用带有禁用缓存的XML配置 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd" > <configuration> <

  • Mybatis关联查询之一对多和多对一XML配置详解

    平时在开发过程中dao.bean和XML文件都是自动生成的,很少写XML的配置关系,今天记录一下mybatis的关联查询中的多对一和一对多的情况. 首先是有两张表(学生表Student和老师Teacher表,注:这里只是为了演示一对多和多对一的情况,请不要杠),为了更易懂,这里只设置了最简单的几个必要字段.表结构如下图 Student表: Teacher表: 创建实体bean Teacher.java: import java.util.List; public class Teacher {

  • Maven默认中央仓库(settings.xml 配置详解)

    Maven的安装 安装Maven之前要确保已经安装好了jdk,并且配置好了环境变量JAVA_HOME.具体安装步骤如下: 1. 从apache网上下载maven项目的压缩包.下载地址为:http://maven.apache.org/download.html.比如现在最新的Maven版本是3.0.4,那么我下载好的安装文件就是apache-maven-3.0.4.zip. 2. 将下载后的压缩包解压到Maven的安装目录,比如说是D:\\develop,那么解压后就是D:\\develop\\

  • Spring中@Import的各种用法以及ImportAware接口详解

    @Import 注解 @Import注解提供了和XML中<import/>元素等价的功能,实现导入的一个或多个配置类.@Import即可以在类上使用,也可以作为元注解使用. @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Import { /** * {@link Configuration}, {@link ImportSelector}, {@link I

  • Spring MVC的web.xml配置详解

    spring是目前最流行的框架.创建java web项目时,我们首先会遇到的配置文件就是web.xml,这是javaweb为我们封装的逻辑,不在今天的研究中.下面我们将简单讲讲web.xml中的配置. 一.一个空的web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns:xsi="http://www.w3.org/2001/

  • Spring Boot如何通过自定义注解实现日志打印详解

    前言 在我们日常的开发过程中通过打印详细的日志信息能够帮助我们很好地去发现开发过程中可能出现的Bug,特别是在开发Controller层的接口时,我们一般会打印出Request请求参数和Response响应结果,但是如果这些打印日志的代码相对而言还是比较重复的,那么我们可以通过什么样的方式来简化日志打印的代码呢? SpringBoot 通过自定义注解实现权限检查可参考我的博客:SpringBoot 通过自定义注解实现权限检查 正文 Spring AOP Spring AOP 即面向切面,是对OO

  • spring boot使用logback实现多环境日志配置详解

    软件生存周期中,涉及代码运行的环节有编码.测试和维护阶段,而一套成熟的代码,在此三个阶段,数据库.日志路径.日志级别.线程池大小等配置一般会不一样.作为开发人员,希望将代码与配置解耦合,不同的环境,代码一套,而配置多套. 针对于多环境的配置,可以使用maven的profile及filter配置,在打包环节通过打包命令 mvn clean package -P dev/test/product决定所打环境的war/jar包.此种解决方案,产生的war\jar包在不同环境的是不同的,因此MD5校验和

随机推荐