Spring系列五之事务

事务的概念

事务是数据库中很重要的一个概念,理解事务的帖子也很多,本教程侧重从代码上来理解。放几个与事务相关的链接:事务解释1 事务解释2

从代码理解事务

本项目的目的是完成一笔转账交易,从A的账户中转出100到B的账户中。A、B账户各自原有1000。

  1. 创建工程,添加Jar包:本次添加的jar包为
    在这里插入图片描述
    添加的具体方法参考链接
  2. 在数据库中创建表:创建account数据表,内有三列,id(序号),username(存放账户名),money(存放账户钱数)。再插入两条数据。SQL文件存放到当前工程中。
    创建SQL文件的方式
    SQL语句如下:
create table account(
	id int not null primary key auto_increment,
	username varchar(10) not null,
	money int not null
);

insert into account(username, money) values("A", "1000");
insert into account(username, money) values("B", "1000");
  1. 工程目录为:
    在这里插入图片描述
  2. 设计思路
    刚开始使用spring,对代码的分层不熟练,往往不知道service层和dao层里各要写什么样的函数。
    dao层负责数据的存储与获取,往往负责的是一个数据库表的相关操作,该层中每个函数往往完成的是一个个原子性操作,例如数据库的插入、删除、更新、查询这四种操作,每一种操作就是一个原子性操作。
    service层就叫业务层,顾名思义就是处理一个业务逻辑的。例如转账它就是一个业务,涉及到转出和转入这两个更新数据的原子操作,放入到dao层。工作后接触的项目可能一个业务逻辑一般都会涉及多张表,那就表示一个service需要由多个Dao来协助。随着见的案例多了,自然就会较合理划分了。
  3. 代码具体实现:
    包com.edu.tjdz.geng.model中创建Account类
package com.edu.tjdz.geng.model;

public class Account {
	private int id;
	private String username;
	private int money;
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public int getMoney() {
		return money;
	}
	public void setMoney(int money) {
		this.money = money;
	}
}

包com.edu.tjdz.geng.dao存放一个dao接口

package com.edu.tjdz.geng.dao;

import com.edu.tjdz.geng.model.Account;

public interface AccountDao {
	public void out(Account outter, int money);
	public void in(Account inner, int money);
}

包com.edu.tjdz.geng.dao存放一个dao的实现类

package com.edu.tjdz.geng.dao.impl;

import org.springframework.jdbc.core.JdbcTemplate;

import com.edu.tjdz.geng.dao.AccountDao;
import com.edu.tjdz.geng.model.Account;

public class AccountDaoImpl implements AccountDao {
	
	private JdbcTemplate jdbcTemplate;
	
	public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
	}
	
	public void out(Account outter, int money) {
		String sql = "update account set money = money - ? where username = ?";
		Object[] args = {money, outter.getUsername()};
		jdbcTemplate.update(sql, args);
	}

	public void in(Account inner, int money) {
		String sql = "update account set money = money + ? where username = ?";
		Object[] args = {money, inner.getUsername()};
		jdbcTemplate.update(sql, args);
	}
}

包com.edu.tjdz.geng.service存放一个service接口

package com.edu.tjdz.geng.service;

import com.edu.tjdz.geng.model.Account;

public interface AccountService {
	public void transfer(Account outter, Account inner, int money);
}

包com.edu.tjdz.geng.service.impl存放一个service接口的实现类

package com.edu.tjdz.geng.service.impl;

import com.edu.tjdz.geng.dao.AccountDao;
import com.edu.tjdz.geng.model.Account;
import com.edu.tjdz.geng.service.AccountService;

public class AccountServiceImpl implements AccountService {
	
	private AccountDao accountDao;
	
	public void setAccountDao(AccountDao accountDao) {
		this.accountDao = accountDao;
	}

	public void transfer(Account outter, Account inner, int money) {
		accountDao.out(outter, money);
		accountDao.in(inner, money);
	}
}

包com.edu.tjdz.geng.test存放测试类

package com.edu.tjdz.geng.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.edu.tjdz.geng.model.Account;
import com.edu.tjdz.geng.service.AccountService;

public class Test {

	public static void main(String[] args) {
		//创建两个账户用来测试
		Account outter = new Account();
		Account inner = new Account();
		outter.setUsername("A");
		inner.setUsername("B");
		int money = 100;
			
		ApplicationContext appContext = new ClassPathXmlApplicationContext("applicationContext.xml");
		AccountService accService = (AccountService)appContext.getBean("accountService");
		accService.transfer(outter, inner, money);
	}
  1. 配置文件信息:包括一个数据库属性文件,一个spring xml
    spring 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"
    xsi:schemaLocation="
    	http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd
        ">
    <context:property-placeholder location="classpath:db.properties"/>
    <!--1、配置数据源  -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    	<property name="driverClass" value="${jdbc.driverClass}"></property>
    	<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
    	<property name="user" value="${jdbc.user}"></property>
    	<property name="password" value="${jdbc.password}"></property>
    </bean>
    
    <!--2、配置jdbcTemplate  -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    	<property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <!-- 3、装配dao -->
    <bean id="accountDao" class="com.edu.tjdz.geng.dao.impl.AccountDaoImpl">
    	<property name="jdbcTemplate" ref="jdbcTemplate"></property>
    </bean>
    
    <!-- 3、装配service -->
    <bean id="accountService" class="com.edu.tjdz.geng.service.impl.AccountServiceImpl">
    	<property name="accountDao" ref="accountDao"></property>
    </bean>
</beans>

db.properties文件

jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai
jdbc.user=root
jdbc.password=123456
  1. 运行测试类,数据库结果如下:
    在这里插入图片描述
    上图表明转账成功,
  2. 修改代码,营造出事务的感觉
    在AccountServiceImpl类的转账业务中,模拟一个故障,例如已经转成还未转入之际,银行突然断电
    在这里插入图片描述
    这个故障造成的后果是A账户已转成100,但是B账户还未收到钱
    在这里插入图片描述
    像转账这样的过程,如果中间某个环节出了错误了,便会引发一系列灾难性后果。不管从银行的角度还是用户的角度都非常不希望这种事情发生。
    在数据库中,我们把这样有若干子操作且子操作要么都成功要么都不成功的一个整体性操作称为事务。
    Spring 事务要做的就是替程序员处理事务相关的逻辑,让程序员将注意力更多放在与主业务逻辑相关。

配置事务管理

现在使用spring的事务管理功能,避免上面的情形发生

  1. 修改配置文件
    (1)在beans标签里增加命名空间aop和tx以及他们的schema
    在这里插入图片描述
    (2)增加事务管理标签
    以后需要修改的是<aop:advisor>标签中pointcut属性值,它指定了哪些类中的哪些方法会涉及到事务管理。
    增加的是<tx:attributes>中<tx:method>的数量,针对每一个要被管理的函数,必须单独配置事务管理方式
    在这里插入图片描述
    (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:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
    	http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.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
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx.xsd
        ">
    <context:property-placeholder location="classpath:db.properties"/>
    <!--1、配置数据源  -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    	<property name="driverClass" value="${jdbc.driverClass}"></property>
    	<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
    	<property name="user" value="${jdbc.user}"></property>
    	<property name="password" value="${jdbc.password}"></property>
    </bean>
    
    <!--2、配置jdbcTemplate  -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    	<property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <!-- 3、装配dao -->
    <bean id="accountDao" class="com.edu.tjdz.geng.dao.impl.AccountDaoImpl">
    	<property name="jdbcTemplate" ref="jdbcTemplate"></property>
    </bean>
    
    <!-- 4、装配service -->
    <bean id="accountService" class="com.edu.tjdz.geng.service.impl.AccountServiceImpl">
    	<property name="accountDao" ref="accountDao"></property>
    </bean>
    
    <!--5、配置事务管理  -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    	<property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--配置具体的事务管理方式  -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
    	<tx:attributes>
    		<!-- 针对每一个要被管理的函数,配置个性化的事务管理方式 -->
    		<tx:method name="transfer" propagation="REQUIRED" isolation="DEFAULT"/>
    	</tx:attributes>
    </tx:advice>
    <aop:config>
    	<!--配置拦截点,要对service层中的哪些函数进行事务管理  -->
    	<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.edu.tjdz.geng.service.impl..*.*(..))"/>
    </aop:config>
</beans>

  1. 类的改变
    AccountServiceImpl的代码如下。其他类没有变化
package com.edu.tjdz.geng.service.impl;

import com.edu.tjdz.geng.dao.AccountDao;
import com.edu.tjdz.geng.model.Account;
import com.edu.tjdz.geng.service.AccountService;

public class AccountServiceImpl implements AccountService {

	private AccountDao accountDao;
	public void setAccountDao(AccountDao accountDao) {
		this.accountDao = accountDao;
	}
	
	public void transfer(Account outter, Account inner, int money) {
		accountDao.out(outter, money);
		int i = 1/0;
		accountDao.in(inner, money);
	}
}

  1. 测试的时候,先将 AccountServiceImpl类中int i = 1/0这一句注释,观察数据库结果,再讲这一句的注释取消后再运行,观察数据库结果。对比两次,检验事务管理是否生效。

如果xml中没有aop或tx标签的提示,请按照如下操作

以配置tx.xsd为例。
点击Window->preferences->输入xml cat->选中XML Catalog
在这里插入图片描述
选中User Specified Entries->点击Add
在这里插入图片描述
点击File System…
在这里插入图片描述
选择tx.xsd所在的本地路径。xsd文件在spring源码包中的路径是:
spring-framework-4.3.6.RELEASE\schema\tx
在这里插入图片描述
修改属性:将Key type改为Schema location
将key修改为http://www.springframework.org/schema/tx/spring-tx.xsd
(不同的命名空间,这个值也不同)
在这里插入图片描述
点击Apply and Close。之后关闭xml,重新打开就有提示了。
在这里插入图片描述
key值是图片红框的某一种,根据命名空间名称来选择。
在这里插入图片描述

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值