zl程序教程

您现在的位置是:首页 >  后端

当前栏目

【spring框架】通过XML配置事务(Transaction)

Spring配置事务框架XML 通过 Transaction
2023-09-11 14:20:52 时间

1 前言

        在 通过注解配置事务 中介绍了使用注解配置事务(Transaction),本文将介绍通过 XML 配置事务,并沿用前面的案例。

通过 XML 配置事务,其原理是 AOP,即将事务当做一个切面,作用于切入点。关于 AOP 的介绍,见 \to XML配置AOP 。

        需要导入的 jar 包如下,其中最后三个包是 JdbcTemplate 所需的 jar 包。

2 案例

        首先在 MySQL 中创建数据库:taobao,再在此数据库中创建表:users(uid int, balance int),books(bid varchar, price int, num int),users 和 books 表中数据如下:

users
books

        Dao.java

package com.transaction;

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

@Repository
public class Dao {
	@Autowired
	private JdbcTemplate jdbcTemplate;
	
	public Integer queryPrice(String bid) { //查询书的价格
		Integer price=jdbcTemplate.queryForObject("select price from books where bid=?", new Object[] {bid},Integer.class);
		return price;
	}
	
	public Integer queryNum(String bid) { //查询书的库存量
		Integer num=jdbcTemplate.queryForObject("select num from books where bid=?", new Object[] {bid},Integer.class);
		return num;
	}
	
	public Integer queryBalance(Integer uid) { //查询用户的余额
		Integer balance=jdbcTemplate.queryForObject("select balance from users where uid=?", new Object[] {uid},Integer.class);
		return balance;
	}
	
	public void updateBook(String bid,Integer buy_num) { //更新书的数量
		Integer num=queryNum(bid);
		if(num<buy_num) {
			throw new RuntimeException();
		}else {
			jdbcTemplate.update("update books set num=num-? where bid=?", buy_num,bid);
		}
	}
	
	public void updateUser(Integer uid,Integer price,Integer buy_num) { //更新用户的余额
		Integer balance=queryBalance(uid);
		if(balance<price*buy_num) {
			throw new RuntimeException();
		}else {
			jdbcTemplate.update("update users set balance=balance-? where uid=?", price*buy_num,uid);
		}
	}
}

        其中,@Repository 用于将 Dao 标注为持久层,并由 IOC 容器生成名为 dao 的 bean;@Autowired 用于自动注入属性。@Repository 和 @Autowired 的具体用法见 \to 通过注解配置bean

        BookService.java

package com.transaction;

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

@Service
public class BookService {
	@Autowired
	private Dao dao;
	
	public void buyBook(Integer uid,String bid,Integer buy_num) {
		Integer price=dao.queryPrice(bid);
		dao.updateUser(uid, price, buy_num); //用户扣钱
		dao.updateBook(bid, buy_num); //书店减书
	}
}

        其中, @Service 用于将 BookService 标注为服务层,并由 IOC 容器生成名为 bookService 的 bean;buyBook() 方法将在 xml 文件中配置为事务,使其内部操作为一个整体,要么全部执行,要么全不执行(因为可能存在用户扣了钱,书店缺书的情况)。

        transaction.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:tx="http://www.springframework.org/schema/tx"
	xmlns:aop="http://www.springframework.org/schema/aop"
	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.0.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
	
	<!-- 引入属性文件 -->
	<context:property-placeholder location="db.properties"/>
	
	<!-- 创建数据源 -->
	<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
		<property name="driverClassName" value="${jdbc.driver}"></property>
		<property name="url" value="${jdbc.url}"></property>
		<property name="username" value="${jdbc.username}"></property>
		<property name="password" value="${jdbc.password}"></property>
	</bean>
	
	<!-- 通过数据源配置JdbcTemplate -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 扫描组件 -->
	<context:component-scan base-package="com.transaction"></context:component-scan>
	
	<!-- 配置事务管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 配置事务通知 -->
	<tx:advice id="adv" transaction-manager="transactionManager">
		<tx:attributes>
			<tx:method name="buyBook"/>
		</tx:attributes>
	</tx:advice>
	
	<!-- 配置切入点表达式 -->
	<aop:config>
		<aop:pointcut expression="execution(* com.transaction.*.*(..))" id="pcut"/>
		<aop:advisor advice-ref="adv" pointcut="pcut"/>
	</aop:config>
</beans>

        注意:需要导入 context、tx、aop 命名空间;tx:method 标签中可以设置 propagation(传播级别)、isolation(隔离级别)、timeout、read-only、rollback-for 等属性,如下:

        Test.java

package com.transaction;

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

public class Test {
	public static void main(String[] args) {
		ApplicationContext ac=new ClassPathXmlApplicationContext("transaction.xml");
		BookService service=ac.getBean("bookService",BookService.class);
		service.buyBook(1002, "b2", 1);
	}
}

        由于用户 1002 有钱(100元),但书店缺书 b2(0本),因此事务没有执行,即用户没有扣钱。如果没有将 buyBook() 方法配置为一个事务,即使书店缺书,用户也会扣钱。