学习 Spring 声明式事务控制,基于xml配置
目录
事务控制一般分为两种方式
1、编程式事务控制
2、声明式事务控制
编程式事务控制这里不赘述。如果不懂请自行查阅资料学习,有助于理解基于 xml 配置。
什么是 Spring 的声明式事务控制?
编程式事务管理将数据层提交事务的代码加入逻辑层,与Spring无侵入式编程思想的主思想冲突,实际开发过程中,往往采用声明式事务管理形式。通过编程式事务管理的代码不难看出,在业务逻辑层对应的业务上添加某些代码即可完成整体事务管理的操作,SpringAOP的思想,将公共的代码加入后,即可完成对应的工作,这就是声明式事务管理的核心机制。
账户转账案例讲解(环境搭建)
导入 jar 包
-
spring基础的包
-
c3p0(将用到c3p0连接池)
-
mysql-connector-java(用mysql数据库)
-
spring-jdbc
-
spring-tx(事务)
-
aspectjweaver(简单理解,支持切入点表达式等等)
创建数据库
这里创建的数据库名为 spring
数据库中有一个表为 person
表中的有两个字段
- name
- money
创建dao层
创建接口 AccountDao:
public interface AccountDao {
//账户转入
public boolean in(String name,int money);
//账户转出
public boolean out(String name,int money);
}
创建实体类 AccountDaoImpl 类:
public class AccountDaoImpl implements AccountDao {
private JdbcTemplate jdbcTemplate;
//set注入
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
//账户转入
public boolean in(String name, int money) {
int update = jdbcTemplate.update("update person set money = money + ? where name = ?", money, name);
if(update != -1)
return true;
return false;
}
@Override
//账户转出
public boolean out(String name, int money) {
int update = jdbcTemplate.update("update person set money = money - ? where name = ?",money,name);
if(update != -1)
return true;
return false;
}
}
创建 service 层
创建接口 AccountService :
public interface AccountService {
//账户转账
public boolean transfer(String nameOut,String nameIn,int money);
}
创建接口 AccountServiceImpl 类 :
public class AccountServiceImpl implements AccountService {
AccountDao accountDao;
//set注入
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
//账户转账
public boolean transfer(String nameOut, String nameIn, int money) {
//转出
boolean flag1 = accountDao.out(nameOut,money);
//用于测试事务是否配置成功
// int i = 1/0;
//转入
boolean flag2 = accountDao.in(nameIn,money);
return flag1 && flag2;
}
}
创建虚假的 web 层,用于测试
创建 Controller 类 :
public class AccountController {
public static void main(String[] args){
//获取 spring 配置文件的路径
//由于这里不是放在resources资源包下的,所以是这样配置
//如果是配置到resource包下可以直接可以配置
//String xml = "applicationContext.xml";
String xml = "com/songshu/jdbc/spring事务控制_基于xml/applicationContext.xml";
//获得Spring容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xml);
//从容器中拿到accountService
AccountService accountService = (AccountService) applicationContext.getBean("accountService");
boolean flag = accountService.transfer("lisi","zhangsan",500);
System.out.println(flag);
}
}
这里要注意:
如果你的 spring 配置文件没有放到 resources 资源包下,那么不会在 target 包下生成。
需要在 pom.xml 中的 build 下配置下面这几句:
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
jdbc.properties配置文件
mysql 数据库的属性的值:
jdbc.Driver = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/spring?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC
jdbc.username = root
jdbc.password = root
到此为止,基本的环境就搭建好了,下面就是重要的部分了,xml的配置。
spring 配置文件编写
在配置之前我们先了解一些知识。
事物管理器
事务是对一系列的数据库操作(比如插入多条数据)进行统一的提交或回滚操作,如果插入成功,那么一起成功,如果中间有一条出现异
常,那么回滚之前的所有操作,这样可以防止出现脏数据,防止数据库数据出现问题。
JDBC中是通过Connection对象进行事务管理,默认是自动提交事务,可以手工将自动提交关闭,通过commit方法进行提交,rollback方
法进行回滚,如果不提交,则数据不会真正的插入到数据库中。
Spring中也有自己的事务管理机制,一般是使用TransactionMananger(事务管理器)进行管理,可以通过Spring的注入来完成此功能。
事务的传播行为(了解即可)
Propagation :属性确定代理应该给哪个方法增加事务行为。这样的属性最重要的部份是传播行为。
有以下选项可供使用:
事务传播行为类型 | 描述 |
---|---|
PROPAGATION_REQUIRED | 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY | 支持当前事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
Spring事物的隔离级别
-
ISOLATION_DEFAULT: 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别.
另外四个与JDBC的隔离级别相对应
-
ISOLATION_READ_UNCOMMITTED: 这是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据。
这种隔离级别会产生脏读,不可重复读和幻像读。
-
ISOLATION_READ_COMMITTED: 保证一个事务修改的数据提交后才能被另外一个事务读取。
另外一个事务不能读取该事务未提交的数据
-
ISOLATION_REPEATABLE_READ: 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。
它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。
-
ISOLATION_SERIALIZABLE 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。
除了防止脏读,不可重复读外,还避免了幻像读。
什么是脏读,不可重复读,幻觉读?
**脏读 **:
指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据, 那么另外一个事务读到的这个数据是脏数据,依据脏数据所做的操作可能是不正确的。
**不可重复读 **:
指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。
**幻觉读 **:
指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。
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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--激活${}占位符
通过上下文注册PropertySourcesPlaceholderConfigurer,
并通过地址解析配置文件为属性
-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!--配置数据源-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.Driver}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!--配置jdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置AccountDao-->
<bean id="accountDao" class="com.songshu.jdbc.spring事务控制_基于xml.dao.impl.AccountDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<!--配置AccountService-->
<bean id="accountService" class="com.songshu.jdbc.spring事务控制_基于xml.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!--配置平台事务管理器
class中的数据可能是需要改变的,这取决于你操作数据库的框架,
如jdbc和Mybatis用的就是org.springframework.jdbc.datasource.DataSourceTransactionManager
而Hibernate用的是 org.springframework.orm.hibernate3.HibernateTransactionManager 这之类的。
-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--给数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--通知 事务增强-->
<tx:advice id="interceptor" transaction-manager="transactionManager">
<!--设置事务的属性信息-->
<tx:attributes>
<!--
任何方法方法 isolation:隔离级别 propagation:传播行为 read-only:是否只读 timeout:超过时间
<tx:method name="*" isolation="REPEATABLE_READ" timeout="-1" propagation="REQUIRED" read-only="false"/>
方法名为以update开头的方法
<tx:method name="update*"></tx:method>
-->
<!--方法名为transfer-->
<tx:method name="transfer"></tx:method>
</tx:attributes>
</tx:advice>
<!--配置事务的aop织入-->
<aop:config>
<aop:advisor advice-ref="interceptor" pointcut="execution(* com.songshu.jdbc.spring事务控制_基于xml.service.impl.*.*(..))"></aop:advisor>
</aop:config>
</beans>
xml配置总结
声明式事务控制的配置
- 平台事物管理器配置
- 事物通知配置
- 事物 aop 织入配置
感谢您的支持,如果有错误请指出,谢谢!