Spring —— 事务控制

目录

 

1、案例环境

2、JdbcTemplate获取Connection并验证是否为同一个

3、添加事务控制

4、事务传播行为

5、Spring提供的API实现事务控制

6、使用事务控制模板

7、Spring基于xml声明式事务控制

事务属性的配置:

8、Spring基于注解声明式事务控制

9、Transactional的使用


1、案例环境

AccountServiceImpl

package shiwu;

import java.util.List;

import rumen.Account;

public class AccountServiceImpl implements IAccountService{

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

	@Override
	public void transfer(String sourceName, String targetName, Float money) {
		Account a1 = accountDao.findByName(sourceName);
		Account a2 = accountDao.findByName(targetName);
		
		a1.setMoney(a1.getMoney()-money);
		a2.setMoney(a2.getMoney()+money);
		
		accountDao.update(a1);
		//int i = 1/0;
		accountDao.update(a2);
		
	}

	@Override
	public List<Account> findAll() {
		List<Account> list = accountDao.findAll();
		return list;
	}

}

AccountDaoImpl

package shiwu;

import java.util.List;

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

import rumen.Account;

public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao{

	@Override
	public Account findByName(String name) {
		List<Account> list = getJdbcTemplate().query("select * from account where name = ?",
				new BeanPropertyRowMapper<Account>(Account.class),name );
		return list.get(0);
	}

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

	@Override
	public List<Account> findAll() {
		List<Account> list = getJdbcTemplate().query("select * from account",
				new BeanPropertyRowMapper<Account>(Account.class));
		return list;
	}

	
}

bean.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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
        
        <bean id="accountService" class="shiwu.AccountServiceImpl">
        	<property name="accountDao" ref="accountDao"></property>
        </bean>
        
        <bean id="accountDao" class="shiwu.AccountDaoImpl">
        	<property name="dataSource" ref="dataSource"></property>
        </bean>
        
        <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        	<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
        	<property name="url" value="jdbc:mysql://localhost:3306/my?serverTimezone=UTC"></property>
        	<property name="username" value="root"></property>
        	<property name="password" value="mysql"></property>
        </bean>
        
        
</beans>

AccountTest 

package shiwu;

import java.util.List;

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

import rumen.Account;

public class AccountTest {

	public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("shiwu/bean.xml");
		IAccountService as = ac.getBean("accountService",IAccountService.class);
		
		as.transfer("aa", "bb", 1000f);
		
		List<Account> list = as.findAll();
		for(Account account : list) {
			System.out.println(account);
		}

	}

}

2、JdbcTemplate获取Connection并验证是否为同一个

package shiwu;

import java.sql.Connection;

import javax.sql.DataSource;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.datasource.DataSourceUtils;

public class Test {
	
	public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("shiwu/bean.xml");
		//获取数据源
		DataSource ds = ac.getBean("dataSource",DataSource.class);
		//根据spring提供的的工具根据数据源获取connection
		Connection c1 = DataSourceUtils.getConnection(ds);
		Connection c2 = DataSourceUtils.getConnection(ds);
		System.out.println(c1==c2);//false
	}
}

结果:false,不是同一个

问题:想要完成事务,conn必须是同一个,要把conn和线程绑定

解决:

package shiwu;

import java.sql.Connection;

import javax.sql.DataSource;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.transaction.support.TransactionSynchronizationManager;

public class Test {
	
	public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("shiwu/bean.xml");
		//获取数据源
		DataSource ds = ac.getBean("dataSource",DataSource.class);
		//通过sprin提供的事务同步管理器对象把Connection和线程绑定
		TransactionSynchronizationManager.initSynchronization();
		//根据spring提供的的工具根据数据源获取connection
		Connection c1 = DataSourceUtils.getConnection(ds);
		Connection c2 = DataSourceUtils.getConnection(ds);
		System.out.println(c1==c2);//true
	}
}

3、添加事务控制

package shiwu;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;

import javax.sql.DataSource;

import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import rumen.Account;

public class AccountServiceImpl implements IAccountService{

	private IAccountDao accountDao;
	private DataSource dataSource;
	
	public void setAccountDao(IAccountDao accountDao) {
		this.accountDao = accountDao;
	}

	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}

	@Override
	public void transfer(String sourceName, String targetName, Float money) {
		Connection conn = null;
		try {
			TransactionSynchronizationManager.initSynchronization();
			conn = DataSourceUtils.getConnection(dataSource);
			conn.setAutoCommit(false);
			
			Account a1 = accountDao.findByName(sourceName);
			Account a2 = accountDao.findByName(targetName);
			
			a1.setMoney(a1.getMoney()-money);
			a2.setMoney(a2.getMoney()+money);
			
			accountDao.update(a1);
			//int i = 1/0;
			accountDao.update(a2);
			
			conn.commit();
		} catch (SQLException e) {
			try {
				conn.rollback();
			} catch (SQLException e1) {
				e1.printStackTrace();
			}
			e.printStackTrace();
		}	
		
	}

	@Override
	public List<Account> findAll() {
		List<Account> list = accountDao.findAll();
		return list;
	}

}
<bean id="accountService" class="shiwu.AccountServiceImpl">
        	<property name="accountDao" ref="accountDao"></property>
        	<property name="dataSource" ref="dataSource"></property>
        </bean>
        
        <bean id="accountDao" class="shiwu.AccountDaoImpl">
        	<property name="dataSource" ref="dataSource"></property>
        </bean>
        
        <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        	<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
        	<property name="url" value="jdbc:mysql://localhost:3306/my?serverTimezone=UTC"></property>
        	<property name="username" value="root"></property>
        	<property name="password" value="mysql"></property>
        </bean>

4、事务传播行为

REQUIRED(required):如果当前没有事务,就新建一个事务,如果已经存在一个事务,加入到这个事务中,也就有了。(默认)

SUPPORTS(supports):支持当前事务,如果当前没有事务,就以非事务方式执行。

 

5、Spring提供的API实现事务控制

package shiwu;

import java.util.List;

import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import rumen.Account;

public class AccountServiceImpl implements IAccountService{

	private IAccountDao accountDao;
	private PlatformTransactionManager manager;
	
	public void setAccountDao(IAccountDao accountDao) {
		this.accountDao = accountDao;
	}
	
	public void setManager(PlatformTransactionManager manager) {
		this.manager = manager;
	}


	@Override
	public void transfer(String sourceName, String targetName, Float money) {
		TransactionStatus status = null;
		try {
			DefaultTransactionDefinition df = new DefaultTransactionDefinition();
			//设置定义信息:隔离级别   传播行为  过期时间   是否只读
			df.setTimeout(-1);
			df.setReadOnly(false);
			df.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
			
			status = manager.getTransaction(df);
			Account a1 = accountDao.findByName(sourceName);
			Account a2 = accountDao.findByName(targetName);
			
			a1.setMoney(a1.getMoney()-money);
			a2.setMoney(a2.getMoney()+money);
			
			accountDao.update(a1);
			//int i = 1/0;
			accountDao.update(a2);
			
			//提交事务
			manager.commit(status);
		} catch (Exception e) {
			manager.rollback(status);
			e.printStackTrace();
		}	
		
	}

	@Override
	public List<Account> findAll() {
		List<Account> list = accountDao.findAll();
		return list;
	}

}
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
        
        <bean id="accountService" class="shiwu.AccountServiceImpl">
        	<property name="accountDao" ref="accountDao"></property>
        	<property name="manager" ref="manager"></property>
        </bean>
        
        <bean id="accountDao" class="shiwu.AccountDaoImpl">
        	<property name="dataSource" ref="dataSource"></property>
        </bean>
        
        <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        	<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
        	<property name="url" value="jdbc:mysql://localhost:3306/my?serverTimezone=UTC"></property>
        	<property name="username" value="root"></property>
        	<property name="password" value="mysql"></property>
        </bean>
        
        <!-- 配置事务控制管理器 -->
        <bean id="manager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        	<property name="dataSource" ref="dataSource"></property>
        </bean>
</beans>

6、使用事务控制模板

package shiwu;

import java.util.List;

import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;

import rumen.Account;

public class AccountServiceImpl implements IAccountService{

	private IAccountDao accountDao;
	private TransactionTemplate template;
	
	public void setAccountDao(IAccountDao accountDao) {
		this.accountDao = accountDao;
	}
	
	public void setTemplate(TransactionTemplate template) {
		this.template = template;
	}

	@Override
	public void transfer(String sourceName, String targetName, Float money) {
		template.execute(new TransactionCallback<Object>() {

			@Override
			public Object doInTransaction(TransactionStatus arg0) {
				Account a1 = accountDao.findByName(sourceName);
				Account a2 = accountDao.findByName(targetName);
				
				a1.setMoney(a1.getMoney()-money);
				a2.setMoney(a2.getMoney()+money);
				
				accountDao.update(a1);
				//int i = 1/0;
				accountDao.update(a2);
				
				return null;
			}
		});
			
	}

	@Override
	public List<Account> findAll() {
		List<Account> list = accountDao.findAll();
		return list;
	}

}
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
        
        <bean id="accountService" class="shiwu.AccountServiceImpl">
        	<property name="accountDao" ref="accountDao"></property>
        	<property name="template" ref="transactionTemplate"></property>
        </bean>
        
        <bean id="accountDao" class="shiwu.AccountDaoImpl">
        	<property name="dataSource" ref="dataSource"></property>
        </bean>
        
        <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        	<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
        	<property name="url" value="jdbc:mysql://localhost:3306/my?serverTimezone=UTC"></property>
        	<property name="username" value="root"></property>
        	<property name="password" value="mysql"></property>
        </bean>
        
        <!-- 配置事务控制管理器 -->
        <bean id="manager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        	<property name="dataSource" ref="dataSource"></property>
        </bean>
        
        <!-- 配置事务控制模板 -->
        <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
        	<property name="transactionManager" ref="manager"></property>
        </bean>
</beans>

7、Spring基于xml声明式事务控制

用 tx的advice配置事务的通知,设置属性时至少要用2个 method标签,用于增删改查。

设置 method 的 name 属性时,可以用 或者类似于 find* 格式,用后者要注意,只能用于find开头的方法

事务属性的配置:

1.isolation="DEFAULT":用于指定事务的隔离级别。默认值是:DEFAULT。表示数据库的隔离级别。
       2.read-only="false" :用于指定事务是否只读。默认值是:false,不只读。只有查询方法才会为true
       3.timeout="-1" :用于只读事务的过期时间。默认值是:-1,表示永不过期。如果设了值,则以为单位。
       4.propagation="REQUIRED" :用于指定事务的传播行为。默认值是:REQUIRED,表示一定有事务,一般增删改使用。当查询时用SUPPORTS
       5.rollback-for="":用于指定要用一个异常,当产生该异常时,事务回滚。产生其他异常时,事务不回滚没有默认值,表示任何异常都回滚
       6.no-rollback-for="":用于指定一个异常,当产生该异常时,事务不回滚。 产生其他异常时,事务回滚没有默认值,表示任何异常都回滚

<?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: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/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">
        
        <bean id="accountService" class="shengming.AccountServiceImpl">
        	<property name="accountDao" ref="accountDao"></property>
        	<property name="manager" ref="manager"></property>
        </bean>
        
        <bean id="accountDao" class="shengming.AccountDaoImpl">
        	<property name="dataSource" ref="dataSource"></property>
        </bean>
        
        <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        	<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
        	<property name="url" value="jdbc:mysql://localhost:3306/my?serverTimezone=UTC"></property>
        	<property name="username" value="root"></property>
        	<property name="password" value="mysql"></property>
        </bean>
        <!-- 配置事务管理器 -->
        <bean id="manager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        	<property name="dataSource" ref="dataSource"></property>
        </bean>
        
        <!-- 使用tx名称空间下的标签配置事务的通知 -->
        <tx:advice id="txAdvice" transaction-manager="manager">
        	<tx:attributes>
        		<!-- 
        		配置属性:(用到哪个,配置哪个)
        			isolation="DEFAULT"
        			read-only="false" 
        			timeout="-1" 
        			propagation="REQUIRED" 
        			rollback-for=""
        			no-rollback-for=""
        		 -->
        		<tx:method name="*" read-only="false" propagation="REQUIRED"/><!-- 增删改 -->
        		<tx:method name="find*" read-only="true" propagation="SUPPORTS"/><!-- 查 -->
        	</tx:attributes>
        </tx:advice>
        
        <!-- 配置aop -->
        <aop:config>
        	<aop:pointcut expression="execution(* shengming.*.*(..)) " id="pt"/>
        	<!-- 建立切入点表达式与事务通知的关系 -->
        	<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
        </aop:config>
   
</beans>

8、Spring基于注解声明式事务控制

AccountServiceImp

package shengming;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import rumen.Account;

@Service("accountService")
@Transactional
public class AccountServiceImpl implements IAccountService{

	@Autowired
	private IAccountDao accountDao;
	@Autowired
	private PlatformTransactionManager manager;
	
	@Override
	public void transfer(String sourceName, String targetName, Float money) {
		TransactionStatus status = null;
		try {
			DefaultTransactionDefinition df = new DefaultTransactionDefinition();
			df.setTimeout(-1);
			df.setReadOnly(false);
			df.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
			
			status = manager.getTransaction(df);
			
			Account a1 = accountDao.findByName(sourceName);
			Account a2 = accountDao.findByName(targetName);
			
			a1.setMoney(a1.getMoney()-money);
			a2.setMoney(a2.getMoney()+money);
			
			accountDao.update(a1);
			//int i = 1/0;
			accountDao.update(a2);
			
			manager.commit(status);
		}catch (Exception e) {
			manager.rollback(status);
			e.printStackTrace();
		}
		
	}

	@Override
	public List<Account> findAll() {
		List<Account> list = accountDao.findAll();
		return list;
	}

}

AccountDaoImpl

package shengming;

import java.util.List;

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

import rumen.Account;

@Repository("accountDao")
public class AccountDaoImpl implements IAccountDao{
	@Autowired
	private JdbcTemplate jdbcTemplate;
	

	@Override
	public Account findByName(String name) {
		List<Account> list = jdbcTemplate.query("select * from account where name = ?",
				new BeanPropertyRowMapper<Account>(Account.class),name );
		return list.get(0);
	}

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

	@Override
	public List<Account> findAll() {
		List<Account> list = jdbcTemplate.query("select * from account",
				new BeanPropertyRowMapper<Account>(Account.class));
		return list;
	}

	
}
<?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:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    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/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
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
        
        <context:component-scan base-package="shengming"></context:component-scan>
        
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        	<property name="dataSource" ref="dataSource"></property>
        </bean>
        
        <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        	<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
        	<property name="url" value="jdbc:mysql://localhost:3306/my?serverTimezone=UTC"></property>
        	<property name="username" value="root"></property>
        	<property name="password" value="mysql"></property>
        </bean>
        <!-- 配置事务管理器 -->
        <bean id="manager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        	<property name="dataSource" ref="dataSource"></property>
        </bean>
        
        <!-- 开启spring对注解事务的支持,并引用事务管理器 -->
        <tx:annotation-driven transaction-manager="manager"/>
   
</beans>

9、Transactional的使用

该注解可以写在类、方法、接口上。

优先级:就近原则,方法>类>接口

注:用注解的步骤要比xml少,但是常用的是xml配置。

原因:xml配置一次,当前项目中的所有业务都能使用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring中,可以使用Spring AOP来进行事务管理。其中,事务管理器是一个重要的组件。事务管理器负责管理事务的开始、提交和回滚等操作。在Spring中,常用的事务管理器是DataSourceTransactionManager。这个事务管理器是基于JDBC的事务实现的,适用于持久层采用JDBC相关技术的情况,比如Mybatis。\[1\]\[2\] 在Spring AOP事务管理中,有两个关键的角色:事务管理员和事务协调员。事务管理员通常指的是业务层开启事务的方法,而事务协调员通常指的是数据层方法或者业务层方法。事务管理员负责发起事务,而事务协调员负责加入事务。通过这种方式,可以实现对事务的管理和控制。\[3\] 总结起来,Spring AOP事务管理是通过事务管理器来管理事务的开始、提交和回滚等操作。在整合Mybatis时,可以使用DataSourceTransactionManager作为事务管理器。同时,通过事务管理员和事务协调员的配合,可以实现对事务的管理和控制。 #### 引用[.reference_title] - *1* [Spring-AOP事务管理](https://blog.csdn.net/qq_53098203/article/details/129532966)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [Spring基础3——AOP,事务管理](https://blog.csdn.net/qq_40991313/article/details/126339270)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值