文档章节

spring事务

青梅煮酒论英雄
 青梅煮酒论英雄
发布于 2017/07/23 19:05
字数 1990
阅读 17
收藏 0

事务是什么

事务(Transaction)是并发控制的基本单位。所谓的事务,它是一个操作序列,这些 操作要么都执行,要么都不执行,它是一个不可分割的工作单位。所以,事务是数据库 维护数据一致性的单位,在每个事务结束时,都能保持数据一致性。

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

② Consistency(一致性):事务完成时,数据必须处于一致状态,数据的完整性约束没有被破坏,事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。

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

④ Durability(持久性):事务结束后,事务处理的结果必须能够得到固化

 

事务的隔离级别

1.脏读(未提交读) 读入未提交数据

这种就是比如我修改了数库没有事务或者事务未提交,其他的事务里就能发现我未提交的数据的情况

2.不可重复读(提交读)

这种情况就是其他事务能看到提交过事务的数据,但是查询同一条数据多次往往看到别人多次修改后的结果,就是说会出现拿到中间状态数据的情况造成数据修改错误,一般我们通过乐观锁的方式解决这种问题

3.幻读(可重复读)

A事务读取了B事务新增的数据,幻象读和不可重复读是两个容易混淆 的概念,前者是指读到了其它已经提交事务的新增数据,而后者是指读到了已经提交事 务的更改数据(更改或删除)

4.串行化

所有的事务都是串行执行的,安全但效率低,之所以出现前面几种事务的隔离级别都是从并发性能的角度考虑问题的,我们在使用事务的隔离级别的时候要从业务的行为来权衡使用哪种隔离级别,或者从补救措施来考虑使用哪种事务的隔离级别

几类事务问题

1.第一类丢失更新

在当前的四种任意隔离级别中,都不会发生该情况

2.第二类丢失更新

这种丢失更新是在提交读的时候会出现这种问题,不可重复读的问题

事务的隔离级别

A.

TransactionDefinition.ISOLATION_DEFAULT:这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是TransactionDefinition.ISOLATION_READ_COMMITTED。

B.

TransactionDefinition.ISOLATION_READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读和不可重复读,因此很少使用该隔离级别。

C.

TransactionDefinition.ISOLATION_READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。

D.

TransactionDefinition.ISOLATION_REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。

E.

TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。

事务的隔离级别和对应容易出现的问题

事务传播行为

1.

TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。

2.

 TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。

3.

TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。

4.

TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。

5.

TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。

6.

TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。

7.

TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。

spring的事务实现方式

编程式事务

事务管理模板

事务代理

Aop通知

注解驱动

下面介绍下一个编程试事务的demo

<?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">

	<!--加入properties配置文件-->
	<context:property-placeholder location="geym/dg/ch9/jdbc.properties"/>

	<!--c3p0数据源-->
	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="${jdbc.driverClassName}"/>
		<property name="url" value="${jdbc.url}"/>
		<property name="username" value="${jdbc.username}"/>
		<property name="password" value="${jdbc.password}"/>
	</bean>

	<!--配置Dao-->
	<!-- 直接以dataSource为参数时,set方法有所不同 -->
	<bean id="accountDao" class="geym.dg.ch9.demo.AccountDaoImpl">
		<property name="dataSource" ref="dataSource"/>
	</bean>

	<!--配置Service-->
	<bean id="accountService" class="geym.dg.ch9.demo.AccountServiceImpl">
		<property name="accountDao" ref="accountDao"/>
		<property name="txManager" ref="txManager"/>
	</bean>

	<!--事务管理器-->
	<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"/>
	</bean>

</beans>
package com.shiwu;

/**
 * Created by xuehan on 2017/7/23
 */
public interface AccountDao {

	/**
	 * 加钱
	 *
	 * @param in    : 加钱账户
	 * @param money : 增加的金额
	 */
	 void inMoney(String in, Double money);

	/**
	 * 扣钱
	 *
	 * @param out   : 扣钱账户
	 * @param money : 减少的金额
	 */
	 void outMoney(String out, Double money);
}
package com.shiwu;

import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;

/**
 * Created by xuehan on 2017/7/23
 */
public class AccountDaoImpl implements AccountDao {

	// 使用JdbcTemplate可以简化对数据库的操作
	private JdbcTemplate jdbcTemplate;

	/**这里也可以通过配置文件实现*/
	public void setDataSource(DataSource dataSource) {
		this.jdbcTemplate = new JdbcTemplate(dataSource);
	}

	@Override
	public void inMoney(String in, Double money) {
		this.jdbcTemplate.update( "update account set money = (money + ?) where name = ?", money, in);
	}

	@Override
	public void outMoney(String out, Double money) {
		this.jdbcTemplate.update( "update account set money = (money - ?) where name = ?", money, out);
	}
}
package com.shiwu;

/**
 * Created by xuehan on 2016/5/22.
 */
public interface AccountService {

	/**
	 * 转账功能
	 *
	 * @param out   : 转出账户
	 * @param in    : 转入账户
	 * @param money : 转账金额
	 */
	public void transfer(String out, String in, Double money);
}
package com.shiwu;

import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

/**
 * Created by xuehan on 2017/7/23
 */
public class AccountServiceImpl implements AccountService {

	private AccountDao accountDao;

	private DataSourceTransactionManager txManager;

	@Override
	public void transfer(final String out, final String in, final Double money) {
		DefaultTransactionDefinition def1 = new DefaultTransactionDefinition();
		def1.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
		def1.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

		TransactionStatus status1 = txManager.getTransaction(def1);

		accountDao.outMoney(out, money);
//		 int i = 10 / 0;
		accountDao.inMoney(in, money);

		txManager.commit(status1);
	}

	public void setAccountDao(AccountDao accountDao) {
		this.accountDao = accountDao;
	}

	public void setTxManager(DataSourceTransactionManager txManager) {
		this.txManager = txManager;
	}
}
package com.shiwu;

import geym.dg.ch9.ScriptRunner;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.sql.DataSource;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.concurrent.CountDownLatch;

/**
 * Created by xuehan on 2017/7/23
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:/com/shiwu/applicationContext0.xml")
public class Demo0 {

	@Autowired
	private AccountService accountService;

	@Autowired
	private DataSource dataSource;

	public void setAccountService(AccountService accountService) {
		this.accountService = accountService;
	}

	@Before
	public void setup() throws SQLException, FileNotFoundException, IOException{
		Connection conn = dataSource.getConnection();
		ScriptRunner sr = new ScriptRunner(conn,true,false);
		sr.runScript(new FileReader("./src/main/resources/com/shiwu/init.sql"));
		conn.close();
	}

	@After
	public void tearDown() throws SQLException, FileNotFoundException, IOException{
//		Connection conn = dataSource.getConnection();
//		ScriptRunner sr = new ScriptRunner(conn,true,false);
//		sr.runScript(new FileReader("./src/main/resources/com/shiwu/teardown.sql"));
//		conn.close();
	}

	/**
	 * 在测试本方法前,请先修改jdbc.properties配置文件中的数据库,并按给定的sql语句建表
	 *
	 * 思路:
	 *     1. DAO层通过JdbcTemplate来操作数据库,简化代码
	 *     2. 使用org.springframework.transaction.support.TransactionTemplate管理事务
	 *           并向TransactionTemplate中注入事务管理器
	 *     3. 在Service层中直接调用TransactionTemplate的execute方法来执行业务
	 */
	@Test
	public void testTransfer() throws InterruptedException {
		// 现在是可以正常转账状态,在AccountServiceImpl中解开第23行注释会发生异常,可以测试在异常发生时的事务处理结果
		accountService.transfer("aaa", "bbb", 100d);
		System.out.println("转账正常完成");
		new CountDownLatch(1).await();
	}
}

 

© 著作权归作者所有

共有 人打赏支持
青梅煮酒论英雄
粉丝 6
博文 84
码字总数 106947
作品 0
浦东
架构师
私信 提问

暂无文章

Caffe(二)-Python-自定义网络

这里我们用一个例子先来体验一下 首先定义一下我们的环境变量 $PYTHONPATH,我这儿是Windows开发环境,至于Windows Caffe怎么编译由读者自己下去搞定 我使用的控制台是 Windows PowerShell 添...

Pulsar-V
46分钟前
4
0
ActiveMQ从入门到精通(二)之可靠性机制

ActiveMQ的可靠性机制 缘由( 确认JMS消息) 只要消息被确认之后,才认为消息被成功消费了。消息的成功消费包括三个阶段:客户端接收消息、客户端处理消息以及客户端确认消息。在事务性会话中...

一看就喷亏的小猿
54分钟前
3
0
源码分析 Mybatis 的 foreach 为什么会出现性能问题

背景 最近在做一个类似于综合报表之类的东西,需要查询所有的记录(数据库记录有限制),大概有1W条记录,该报表需要三个表的数据,也就是根据这 1W 个 ID 去执行查询三次数据库,其中,有一...

TSMYK
今天
8
0
IC-CAD Methodology企业实战之openlava

在云计算解决安全问题并成为IC界主流运算平台之前,私有的服务器集群系统仍然是各大IC公司的计算资源平台首选。 现在主流的服务器集群管理系统包括lsf,openlava,SkyForm,三者都属于lsf一系...

李艳青1987
今天
8
0
http response stream 字节流 接收与解码

在接收图片、音频、视频的时候,需要用到二进制流。 浏览器会发给客户端 字节Byte流,一串串的发过来_int8格式 -128~127(十进制),也就是8bit(位)。 客户端接收的时候,对接收到的字节收集,...

大灰狼wow
今天
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部