Spring事务管理范例

Spring中常用的事务管理有以下两种方式:

一种是基于@Transactional注解的声明式,另外一个是基于XML配置的事务管理。

下边以Spring 4.3.23.RELEASE这个版本为例来提供两种事务管理方式的常用代码。

先以添加员工基本信息和员工部门关联信息为例来说明纯注解的事务声明方式,下边是配置类的代码:

package com.huixin.tx;

import javax.sql.DataSource;

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.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.mchange.v2.c3p0.ComboPooledDataSource;

/**
 * @Configuration 告诉Spring容器这是一个配置类.<br>
 * @ComponentScan 包扫描, 相当于XML配置文件中的 &lt;context:component-scan base-package="com.huixin.tx"/&gt; 标签.
 * 该注解会将指定包下的带有@Controller、@Service、@Repository、@Component注解的类加入到Spring容器中.<br>
 * Spring事务相关配置<br>
 * @EnableTransactionManagement 表示开启事务管理, 相当于XML配置文件中的 &lt;tx:annotation-driven/&gt; 标签<br>
 * <b>使用声明式事务的步骤:</b><br>
 * 1、将DAO、Service注册到Spring容器中.<br>
 * 2、在需要添加事务的方法上加上 @Transactional 注解.<br>
 * 3、把DataSource、TransactionManager对象注册到Spring容器中.<br>
 * 4、在配置类上加上 @EnableTransactionManagement 注解开启事务管理.<br>
 */
@Configuration
@ComponentScan({"com.huixin.tx"})
@EnableTransactionManagement
public class TransactionConfig {
	
	/**
	 * 注册DataSource
	 * @return
	 * @throws Exception
	 */
	@Bean
	public DataSource dataSource() throws Exception {
		ComboPooledDataSource dataSource = new ComboPooledDataSource();
		dataSource.setDriverClass("oracle.jdbc.driver.OracleDriver");
		dataSource.setJdbcUrl("jdbc:oracle:thin:@127.0.0.1:1521:Ora11g");
		dataSource.setUser("username");
 		dataSource.setPassword("user_pass");
 		return dataSource;
	}
	
	/**
	 * 注册JdbcTemplate
	 * @param dataSource
	 * @return
	 */
	@Bean
	public JdbcTemplate jdbcTemplate(DataSource dataSource) {
		JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
		return jdbcTemplate;
	}
	
	/**
	 * 注册TransactionManager
	 * @param dataSource
	 * @return
	 */
	@Bean
	public PlatformTransactionManager transactionManager(DataSource dataSource) {
		return new DataSourceTransactionManager(dataSource);
	}
	
}

上边数据源的各项参数都是写死的,实际的开发中可以放在配置文件中,或者配在应用服务器的JNDI中。

下边是Service类:

package com.huixin.tx;

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

// ID默认为 empService
@Service
public class EmpService {

	/*
	 * @Autowired 自动装配, 默认先按照类型进行查找装配. 如果按照类型查找到多个, 则按照ID查找装配(属性名作为默认的ID进行匹配). 自动装配推荐使用该注解.
	 */
	@Autowired
	private EmpDAO empDAO;
	
	/**
	 * @Transactional 表示为该方法添加事务.
	 * 该方法中插入了员工基本信息和员工部门关联信息
	 */
	@Transactional
	public void insert() {
		empDAO.insertEmpBaseInfo();
		empDAO.insertEmpDeptInfo();
		System.out.println(10/0);
	}
	
}

在该事务方法中,后边的10/0会抛异常,那么前边插入的两条记录应该回滚。

下边是DAO类的代码:

package com.huixin.tx;

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

//ID默认为 empDAO
@Repository
public class EmpDAO {

	@Autowired
	private JdbcTemplate jdbcTemplate;
	
	public void insertEmpBaseInfo() {
		String sql = "INSERT INTO tab_emp_base_info (emp_id,emp_name,emp_sex) VALUES (?,?,?)";
		jdbcTemplate.update(sql, 1, "张三", 'M');
	}
	
	public void insertEmpDeptInfo() {
		String sql = "INSERT INTO tab_emp_dept_info (emp_id,dept_id) VALUES (?,?)";
		jdbcTemplate.update(sql, 1, 1001);
	}
	
}

至此纯注解的事务配置写完了。调用Service的insert方法,如果出险异常,事务会回滚。

下边以经典的转账为例来说明纯XML配置事务的方法。先写一个转账的DAO接口,代码如下:

package com.example.transaction.dao;

public interface AccountInfoDAO {
	/**
	 * 根据账号查询余额
	 * @param accountNo 账号
	 * @return 余额
	 */
	public double findBalanceByAccountNo(String accountNo);
	
	/**
	 * 根据账号减少金额
	 * @param accountNo 账号
	 * @param varyMoney 变化金额
	 * @return 影响记录数
	 */
	public int reduceBalanceByAccountNo(String accountNo, double varyMoney);
	
	/**
	 * 根据账号增加金额
	 * @param accountNo 账号
	 * @param varyMoney 变化金额
	 * @return 影响记录数
	 */
	public int addBalanceByAccountNo(String accountNo, double varyMoney);
}

下边是转账DAO接口的实现类:

package com.example.transaction.dao.impl;

import org.springframework.jdbc.core.JdbcTemplate;

import com.example.transaction.dao.AccountInfoDAO;

public class AccountInfoDAOImpl implements AccountInfoDAO {
	
	private JdbcTemplate jdbcTemplate;
	
	public JdbcTemplate getJdbcTemplate() {
		return jdbcTemplate;
	}

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

	@Override
	public double findBalanceByAccountNo(String accountNo) {
		String sql = "SELECT balance FROM tab_account_info WHERE account_no=?";
		double balance = jdbcTemplate.queryForObject(sql, Double.class, accountNo);
		return balance;
	}

	@Override
	public int reduceBalanceByAccountNo(String accountNo, double varyMoney) {
		String sql = "UPDATE tab_account_info SET balance = balance-? WHERE account_no = ?";
		int rows = jdbcTemplate.update(sql, varyMoney, accountNo);
		return rows;
	}

	@Override
	public int addBalanceByAccountNo(String accountNo, double varyMoney) {
		String sql = "UPDATE tab_account_info SET balance = balance+? WHERE account_no = ?";
		int rows = jdbcTemplate.update(sql, varyMoney, accountNo);
		return rows;
	}

}

下边是转账的Service接口:

package com.example.transaction.service;

public interface AccountInfoService {
	/**
	 * 转账
	 * @param srcAccountNo 源账号
	 * @param targetAccountNo 目标账号
	 * @param transferMoney 转账金额
	 * @return true表示成功, 否则表示失败
	 */
	public boolean transferAccount(String srcAccountNo, String targetAccountNo, double transferMoney);
}

下边写转账Service接口的实现类:

package com.example.transaction.service.impl;

import com.example.transaction.dao.AccountInfoDAO;
import com.example.transaction.service.AccountInfoService;

public class AccountInfoServiceImpl implements AccountInfoService {
	
	private AccountInfoDAO accountInfoDAO;

	public AccountInfoDAO getAccountInfoDAO() {
		return accountInfoDAO;
	}

	public void setAccountInfoDAO(AccountInfoDAO accountInfoDAO) {
		this.accountInfoDAO = accountInfoDAO;
	}

	@Override
	public boolean transferAccount(String srcAccountNo, String targetAccountNo, double transferMoney) {
		double balance = accountInfoDAO.findBalanceByAccountNo(srcAccountNo);
		if (balance < transferMoney) {
			return false;
		}
		
		int rows = accountInfoDAO.reduceBalanceByAccountNo(srcAccountNo, transferMoney);
		if (rows <= 0) {
			return false;
		}
		
		rows = accountInfoDAO.addBalanceByAccountNo(targetAccountNo, transferMoney);
		if (rows <= 0) {
			return false;
		}
		
		return true;
	}

}

下边写一个配置文件datasource.properties,用于保存数据源的用户名密码等信息,并将该文件放在classpath下:

oracle.driverClass=oracle.jdbc.driver.OracleDriver
oracle.jdbcUrl=jdbc:oracle:thin:@localhost:1521:Ora11g
oracle.username=username
oracle.password=user_pass

下来写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:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	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-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">

	<!-- 包扫描配置 -->
	<context:component-scan base-package="com.example.transaction"/>
	<!-- 加载外部的属性配置文件, 在后边通过${}的方式获取properties配置文件中的值    -->
	<context:property-placeholder location="datasource.properties"/>
	
	<!-- 配置DataSource -->
	<bean id="datasource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
		<property name="driverClass" value="${oracle.driverClass}" />
		<property name="jdbcUrl" value="${oracle.jdbcUrl}" />
		<property name="user" value="${oracle.username}" />
		<property name="password" value="${oracle.password}" />
		<!-- <property name="acquireIncrement" value="20" />
		<property name="acquireRetryAttempts" value="5" />
		<property name="acquireRetryDelay" value="2000" />
		<property name="autoCommitOnClose" value="false" />
		<property name="idleConnectionTestPeriod" value="5" /> -->
		<property name="minPoolSize" value="5" />
		<property name="initialPoolSize" value="20" />
		<property name="maxPoolSize" value="200" />
		<property name="maxIdleTime" value="0" />
		<property name="preferredTestQuery" value="SELECT SYSDATE FROM dual" />
	</bean>
	
	<!-- 配置JdbcTemplate -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<constructor-arg name="dataSource" ref="datasource"/>
	</bean>
	
	<bean id="accountInfoDAO" class="com.example.transaction.dao.impl.AccountInfoDAOImpl">
		<property name="jdbcTemplate" ref="jdbcTemplate"/>
	</bean>
	
	<bean id="accountInfoService" class="com.example.transaction.service.impl.AccountInfoServiceImpl">
		<property name="accountInfoDAO" ref="accountInfoDAO"/>
	</bean>
	
	<!-- 配置TransactionManager -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="datasource"/>
	</bean>
	
	<!-- 配置事务通知的属性  -->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<tx:method name="transferAccount" propagation="REQUIRED"/>
		</tx:attributes>
	</tx:advice>
	
	<aop:config>
		<!--  配置事务切入点 -->
		<aop:pointcut expression="execution(public * com.example.transaction.service.impl.AccountInfoServiceImpl.transferAccount(String, String, double))" id="txPointcut"/>
		<!-- 把事务切入点和事务通知属性关联起来   -->
		<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
	</aop:config>
	
</beans>

至此,XML配置事务的代码写完,调用Service里边的transferAccount方法进行转账,任何一部出错,都会回滚事务。

当然还有一些混合型的,比如XML配置文件中注册DataSource、TransactionManager等等信息:

<?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:context="http://www.springframework.org/schema/context"
	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-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">

	<!-- 包扫描配置 -->
	<context:component-scan base-package="com.example.transaction"/>
	<!-- 加载外部的属性配置文件, 在后边通过${}的方式获取properties配置文件中的值    -->
	<context:property-placeholder location="datasource.properties"/>
	
	<!-- 配置DataSource -->
	<bean id="datasource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
		<property name="driverClass" value="${oracle.driverClass}" />
		<property name="jdbcUrl" value="${oracle.jdbcUrl}" />
		<property name="user" value="${oracle.username}" />
		<property name="password" value="${oracle.password}" />
		<!-- <property name="acquireIncrement" value="20" />
		<property name="acquireRetryAttempts" value="5" />
		<property name="acquireRetryDelay" value="2000" />
		<property name="autoCommitOnClose" value="false" />
		<property name="idleConnectionTestPeriod" value="5" /> -->
		<property name="minPoolSize" value="5" />
		<property name="initialPoolSize" value="20" />
		<property name="maxPoolSize" value="200" />
		<property name="maxIdleTime" value="0" />
		<property name="preferredTestQuery" value="SELECT SYSDATE FROM dual" />
	</bean>
	
	<!-- 配置JdbcTemplate -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<constructor-arg name="dataSource" ref="datasource"/>
	</bean>
	
	<!-- 配置TransactionManager -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="datasource"/>
	</bean>
	
	<!-- 启用事务的注解驱动 -->
	<tx:annotation-driven transaction-manager="transactionManager"/>

</beans>

在Service类上加@Service注解,事务方法上加@Transactional注解,DAO类上加@Repository注解。这些就不再赘述。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值