spring——事务管理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_35537301/article/details/81747989

转账案例

dao层

import java.util.List;

import org.springframework.jdbc.core.JdbcTemplate;

import com.itheima.domain.Account;
import com.itheima.rowmapper.MyRowMapper;

public class AccountDaoImpl implements AccountDao {

	private JdbcTemplate jdbcTemplate;

	public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
	}

	@Override
	public void updateAcount(Account a) {
		jdbcTemplate.update("update account set money=? where name=?", a.getMoney(), a.getName());

	}

	@Override
	public Account findAccount(String name) {
		List<Account> list = jdbcTemplate.query("select * from account where name = ?", new MyRowMapper(), name);
		return list.isEmpty() ? null : list.get(0);
	}

}

service层

import com.itheima.dao.AccountDao;
import com.itheima.domain.Account;

public class AccountServiceImpl implements AccountService {

	private AccountDao accountDao;

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

	@Override
	public void transfer(String fromName, String toName, float money) {
		// 1.查询2个账户对象
		Account fromAccount = accountDao.findAccount(fromName);
		Account toAccount = accountDao.findAccount(toName);

		// 2.计算余额
		fromAccount.setMoney(fromAccount.getMoney() - money);
		toAccount.setMoney(toAccount.getMoney() + money);

		// 3.更新表数据
		accountDao.updateAcount(fromAccount);
		int i = 10 / 0;
		accountDao.updateAcount(toAccount);
	}

}

 db.properties

jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///spring
jdbc.username=root
jdbc.password=root

applicationContext.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">

	<!-- 加载jdbc.properties文件 -->
	<context:property-placeholder location="classpath:db.properties"/>
	
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="${jdbc.driverClass}"/>
		<property name="jdbcUrl" value="${jdbc.url}"/>
		<property name="user" value="${jdbc.username}"/>
		<property name="password" value="${jdbc.password}"/>
	</bean> 
	
	<!--  使用JDBC模板 -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"/>
	</bean>
	
	<bean id="accountDao" class="com.itheima.dao.AccountDaoImpl">
		<property name="jdbcTemplate" ref="jdbcTemplate"></property>
	</bean>
	
	<bean id="accountService" class="com.itheima.service.AccountServiceImpl">
		<property name="accountDao" ref="accountDao"></property>
	</bean>

</beans>

测试类

import static org.junit.Assert.*;

import org.junit.After;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.itheima.service.AccountService;

public class TestTransfer {

	@Test
	public void test01() throws Exception {
		ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
		AccountService as = (AccountService) ac.getBean("accountService");

		as.transfer("tom", "jerry", 1000);
	}

}

结果

事务

spring-tx-4.2.4.RELEASE.jar(事务)

概念

  • 事务是逻辑上的一组操作,要么全都成功,要么全都失败
  • 一组操作:多条sql语句不可分隔(在一个语句块)
  • 如何保证多条语句在一个语句块:要使用同一个Connection连接

事务的特性

  • 原子性:(事务内的操作是一个整体,不可分割);
  • 一致性:(在事务开启之后,到事务提交之前,应该保证事务的操作不能改变);
  • 隔离性:(一个事务在执行期间不能受到其他事务的干扰)
  • 持久性:(事务一旦结束,结果就不能再次改变);

不考虑事务的安全性问题

  • 读问题
    • 脏读:一个事务读到了另一个事务未提交的数据。
    • 不可重复读
      • 一个事务读到了另一个事务提交的数据(update)
      • 老汉原来卡里有1万,去银行存5000,银行业务员操作时,家人网购花了800,并提交,业务员提交,打印回执单发现,此时只有7000元
    • 虚读/幻读:一个事务读到了另一个事务提交的数据(insert)
  • 写问题
    • 丢失更新
    • 解决:悲观锁和乐观锁

事务的隔离级别

  • 1 read uncommitted  :未提交读.脏读,不可重复读,虚读都可能发生
  • 2 read committed    :已提交读.避免脏读.但是不可重复读和虚读有可能发生.(Oracle默认)
  • 4 repeatable read   :可重复读.避免脏读,不可重复读.但是虚读有可能发生.(MySql默认)
  • 8 serializable     :可解决所有问题

spring事务管理的API

平台事务管理器

  • PlatformTransactionManager接口
    • 真正管理事务的类
    • 该接口有具体的实现类,根据不同的持久层框架,需要选择不同的实现类
      • DataSourceTransactionManager :使用的Spring的JDBC模板或者MyBatis框架开发的时候,使用事务管理
      • HibernateTransactionManager    :Hibernate开发的时候,使用事务管理
    • 该接口的常用方法
      • void commit(TransactionStatus status)
      • TransactionStatus getTransaction(TransactionDefinition definition)
      • void rollback(TransactionStatus status)

事务定义信息

  • TransactionDefinition接口
  • 定义事务的隔离级别,传播行为,超时,只读
  • 事务隔离级别的常量
    • static int ISOLATION_DEFAULT                  -- 采用数据库的默认隔离级别
    • static int ISOLATION_READ_UNCOMMITTED
    • static int ISOLATION_READ_COMMITTED
    • static int ISOLATION_REPEATABLE_READ
    • static int ISOLATION_SERIALIZABLE

事务的状态

  • TransactionStatus接口
  • 定义事务是否是新事务,是否有保存点

API的关系

平台事务管理器根据事务定义信息(TransactionDefinition)中定义内容进行事务的管理,在管理事务的过程中就会产生一些事务的状态,将产生的状态保存到事务状态信息(TransactionStatus)中

事务的传播行为

PROPAGATION_REQUIRED            :默认值。支持当前的事务,如果不存在,新建一个事务。

PROPAGATION_REQUIRES_NEW   :有事务,挂起原有事务,新建一个事务。

PROPAGATION_NESTED                :嵌套事务,执行完之前的操作的时候,设置一个保存点,如果后续代码没有错误,执行通过,如果有错误,允许用户自己设置回滚到保存点还是最初始状态。

XML方式管理事务

applicationContext.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"
	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">

	<!-- 加载jdbc.properties文件 -->
	<context:property-placeholder location="classpath:db.properties" />

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

	<!-- 创建dao -->
	<bean id="accountDao" class="com.itheima.dao.AccountDaoImpl">
		<property name="dataSource" ref="dataSource"></property>
	</bean>

	<!-- 创建service -->
	<bean id="accountService" class="com.itheima.service.AccountServiceImpl">
		<property name="accountDao" ref="accountDao"></property>
	</bean>

	<!-- 配置平台事务管理器 -->
	<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>
			<!-- 
				name :要绑定事务的方法名,可以使用通配符,可以配置多个 
				propagation :传播行为 
				isolation :隔离级别 
				read-only :是否只读
				timeout :超时信息 
				rollback-for:发生哪些异常回滚 
				no-rollback-for:发生哪些异常不回滚 
			-->

			<tx:method name="transfer" propagation="REQUIRED" />
		</tx:attributes>
	</tx:advice>

	<!-- 配置AOP -->
	<aop:config>
		<!-- 配置切入点 -->
		<aop:pointcut expression="execution(* com.itheima.service.*.*(..))"
			id="pointcut1" />

		<!-- 织入 -->
		<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1" />
	</aop:config>
</beans>

 dao层

import java.util.List;

import org.springframework.jdbc.core.support.JdbcDaoSupport;

import com.itheima.domain.Account;
import com.itheima.rowmapper.MyRowMapper;

/**
 * @ClassName: AccountDaoImpl
 * @Description:继承JdbcDaoSupport,只能用xml注入
 * @author jsz
 * @date 2018年8月16日
 */
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {

	@Override
	public void updateAcount(Account a) {
		this.getJdbcTemplate().update("update account set money=? where name=?", a.getMoney(), a.getName());

	}

	@Override
	public Account findAccount(String name) {
		List<Account> list = this.getJdbcTemplate().query("select * from account where name = ?", new MyRowMapper(), name);
		return list.isEmpty() ? null : list.get(0);
	}

}

service层

import com.itheima.dao.AccountDao;
import com.itheima.domain.Account;

public class AccountServiceImpl implements AccountService {

	private AccountDao accountDao;

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

	@Override
	public void transfer(String fromName, String toName, float money) {
		// 1.查询2个账户对象
		Account fromAccount = accountDao.findAccount(fromName);
		Account toAccount = accountDao.findAccount(toName);

		// 2.计算余额
		fromAccount.setMoney(fromAccount.getMoney() - money);
		toAccount.setMoney(toAccount.getMoney() + money);

		// 3.更新表数据
		accountDao.updateAcount(fromAccount);
		int i = 10 / 0;
		accountDao.updateAcount(toAccount);
	}

}

测试类

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

import com.itheima.service.AccountService;

public class TestTransfer {

	@Test
	public void test01() throws Exception {
		ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
		AccountService as = (AccountService) ac.getBean("accountService");

		as.transfer("tom", "jerry", 1000);
	}
	
}

 XML&&注解的方式配置事务

dao层

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import com.itheima.domain.Account;
import com.itheima.rowmapper.MyRowMapper;

@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {

	@Autowired
	private JdbcTemplate jdbcTemplate;
	@Override
	public void updateAcount(Account a) {
		jdbcTemplate.update("update account set money=? where name=?", a.getMoney(), a.getName());

	}

	@Override
	public Account findAccount(String name) {
		List<Account> list = jdbcTemplate.query("select * from account where name = ?", new MyRowMapper(), name);
		return list.isEmpty() ? null : list.get(0);
	}

}

service层

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.itheima.dao.AccountDao;
import com.itheima.domain.Account;

@Service("accountService")
@Transactional(readOnly=true,propagation=Propagation.SUPPORTS)
public class AccountServiceImpl implements AccountService {

	@Autowired
	private AccountDao accountDao;

	@Override
	@Transactional(readOnly=false,propagation=Propagation.REQUIRED)
	public void transfer(String fromName, String toName, float money) {
		// 1.查询2个账户对象
		Account fromAccount = accountDao.findAccount(fromName);
		Account toAccount = accountDao.findAccount(toName);

		// 2.计算余额
		fromAccount.setMoney(fromAccount.getMoney() - money);
		toAccount.setMoney(toAccount.getMoney() + money);

		// 3.更新表数据
		accountDao.updateAcount(fromAccount);
		int i = 10 / 0;
		accountDao.updateAcount(toAccount);
	}

}

applicationContext.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"
	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">


	<!-- 配置spring注解要扫描的包 -->
	<context:component-scan base-package="com.itheima"/>

	<!-- 加载jdbc.properties文件 -->
	<context:property-placeholder location="classpath:db.properties" />

	<!-- 配置数据源 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="${jdbc.driverClass}" />
		<property name="jdbcUrl" value="${jdbc.url}" />
		<property name="user" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
	</bean>

	<!-- 配置JdbcTemplate -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"></property>
	</bean>

	<!-- 开启spring对注解事务的支持 -->
	<tx:annotation-driven transaction-manager="transactionManager" /> 
	
</beans>

测试类

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

import com.itheima.service.AccountService;

public class TestTransfer {

	@Test
	public void test01() throws Exception {
		ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
		AccountService as = (AccountService) ac.getBean("accountService");

		as.transfer("tom", "jerry", 1000);
	}
	
}

纯注解方式配置事务

jar包

 配置类

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@ComponentScan(basePackages = "com.itheima")
@EnableTransactionManagement // 开启spring对注解的支持
public class SpringConfiguration {

	/**
	 * @MethodName:createDS
	 * @Description:配置数据源
	 * @return
	 * @throws Exception
	 */
	@Bean(name = "dataSource")
	public DataSource createDS() throws Exception {
		DriverManagerDataSource dataSource = new DriverManagerDataSource();
		dataSource.setUsername("root");
		dataSource.setPassword("root");
		dataSource.setDriverClassName("com.mysql.jdbc.Driver");
		dataSource.setUrl("jdbc:mysql:///spring");
		return dataSource;
	}

	/**
	 * @MethodName:createTransactionManager
	 * @Description:配置事务管理器
	 * @param dataSource
	 * @return
	 */
	@Bean
	public PlatformTransactionManager createTransactionManager(@Qualifier("dataSource") DataSource dataSource) {
		return new DataSourceTransactionManager(dataSource);
	}

	/**
	 * @MethodName:createTemplate
	 * @Description:配置JDBC模板
	 * @param dataSource
	 * @return
	 */
	@Bean
	public JdbcTemplate createTemplate(@Qualifier("dataSource") DataSource dataSource) {
		return new JdbcTemplate(dataSource);
	}

}

 dao层

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import com.itheima.domain.Account;
import com.itheima.rowmapper.MyRowMapper;

@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {

	@Autowired
	private JdbcTemplate jdbcTemplate;
	@Override
	public void updateAcount(Account a) {
		jdbcTemplate.update("update account set money=? where name=?", a.getMoney(), a.getName());

	}

	@Override
	public Account findAccount(String name) {
		List<Account> list = jdbcTemplate.query("select * from account where name = ?", new MyRowMapper(), name);
		return list.isEmpty() ? null : list.get(0);
	}

}

service层

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.itheima.dao.AccountDao;
import com.itheima.domain.Account;

@Service("accountService")
@Transactional(readOnly=true,propagation=Propagation.SUPPORTS)
public class AccountServiceImpl implements AccountService {

	@Autowired
	private AccountDao accountDao;

	@Override
	@Transactional(readOnly=false,propagation=Propagation.REQUIRED)
	public void transfer(String fromName, String toName, float money) {
		// 1.查询2个账户对象
		Account fromAccount = accountDao.findAccount(fromName);
		Account toAccount = accountDao.findAccount(toName);

		// 2.计算余额
		fromAccount.setMoney(fromAccount.getMoney() - money);
		toAccount.setMoney(toAccount.getMoney() + money);

		// 3.更新表数据
		accountDao.updateAcount(fromAccount);
		int i = 10 / 0;
		accountDao.updateAcount(toAccount);
	}

}

测试类

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 com.itheima.config.SpringConfiguration;
import com.itheima.service.AccountService;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=SpringConfiguration.class)
public class TestTransfer {

	@Autowired
	private AccountService accountService;
	@Test
	public void test01() throws Exception {
		accountService.transfer("tom", "jerry", 500);
	}
	
}

猜你喜欢

转载自blog.csdn.net/qq_35537301/article/details/81747989