spring事务
博客专区 > 干-将 的博客 > 博客详情
spring事务
干-将 发表于5个月前
spring事务
  • 发表于 5个月前
  • 阅读 10
  • 收藏 0
  • 点赞 0
  • 评论 0

腾讯云 技术升级10大核心产品年终让利>>>   

事务是什么

事务(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
博文 96
码字总数 123051
×
干-将
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: