声明式事务:AOP
编程式事务:需要在代码中进行事务管理
*使用步骤是固定的,只要把事务的使用信息提供给Spring就完事了。
1、注解方案(适合中小型项目使用,本文重点介绍)
2、使用aspectJ框架(适合大型项目)
在Spring配置文件中声明类、方法需要的事务,达到业务方法和事务配置完全分离。一键跳转~
声明事务管理器对象
开启事务注解驱动
告诉spring框架爹要用注解的方式管理事务
spring使用AOP机制,创建@Transactional所标注类的代理对象,给方法加入事务功能
spring给业务方法加入事务:使用AOP环绕通知,运行方法前开启事务,运行方法后提交或回滚事务
@Around(value = "execution(..)")
Object myAround(){
// TODO: Spring开启事务
try{
// TODO: 执行业务方法
// TODO: Spring提交事务
} catch(Exception e){
// TODO: Spring回滚事务
}
}
在业务方法上加@Transactional注解
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>hom.wanggroupId>
<artifactId>st-07-spring-transartifactId>
<version>1.0-SNAPSHOTversion>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<maven.compiler.source>1.8maven.compiler.source>
<maven.compiler.target>1.8maven.compiler.target>
properties>
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.11version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.3.18version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-txartifactId>
<version>5.3.18version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.3.18version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.7version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>2.0.6version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.29version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.2.6version>
dependency>
dependencies>
<build>
<resources>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.xmlinclude>
includes>
<filtering>falsefiltering>
resource>
resources>
<plugins>
<plugin>
<artifactId>maven-compiler-pluginartifactId>
<version>3.8.1version>
<configuration>
<source>1.8source>
<target>1.8target>
configuration>
plugin>
plugins>
build>
project>
package hom.wang.data;
import java.math.BigDecimal;
public class Goods {
private String goodsId;
private String goodsName;
private Integer goodsAmount;
private BigDecimal goodsPrice;
}
package hom.wang.data;
public class Sale {
private Integer saleId;
private String goodsId;
private Integer saleNum;
}
package hom.wang.dao;
import hom.wang.data.Goods;
public interface GoodsDao {
int updateGoods(Goods goods);
Goods selectGoodsById(Goods goods);
}
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="hom.wang.dao.GoodsDao">
<update id="updateGoods">
update goods
set goods_amount = #{goodsAmount}
where goods_id = #{goodsId}
update>
<resultMap id="goodsMap" type="hom.wang.data.Goods">
<id property="goodsId" column="goods_id" />
<result property="goodsName" column="goods_name" />
<result property="goodsAmount" column="goods_amount" />
<result property="goodsPrice" column="goods_price" />
resultMap>
<select id="selectGoodsById" resultMap="goodsMap">
select
goods_id,
goods_name,
goods_amount,
goods_price
from goods
where goods_id = #{goodsId}
select>
mapper>
package hom.wang.dao;
import hom.wang.data.Sale;
public interface SaleDao {
int insertSale(Sale sale);
}
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="hom.wang.dao.SaleDao">
<insert id="insertSale">
insert into sale(goods_id, sale_num) values(#{goodsId}, #{saleNum})
insert>
mapper>
package hom.wang.exce;
/* 库存不足异常 */
public class NotEnoughException extends RuntimeException {
public NotEnoughException() {
}
public NotEnoughException(String message) {
super(message);
}
}
package hom.wang.service.impl;
import hom.wang.dao.GoodsDao;
import hom.wang.dao.SaleDao;
import hom.wang.data.Goods;
import hom.wang.data.Sale;
import hom.wang.exce.NotEnoughException;
import hom.wang.service.BuyService;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
public class BuyServiceImpl implements BuyService {
GoodsDao goodsDao;
SaleDao saleDao;
/**
* rollbackFor:发生指定异常一定回滚
* spring处理逻辑:
* 1、首先判断抛出异常是否是rollbackFor包含的,是就回滚
* 2、不在rollbackFor数组中则判断是否是RuntimeException,是就回滚
*/
/* @Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.DEFAULT,
readOnly = false,
rollbackFor = {
NullPointerException.class,
NotEnoughException.class
}
)*/
// 上面配置的都是默认值,故而直接使用@Transactional就可以
@Transactional
@Override
public void buy(Sale sale) {
System.out.println("============ buy start =============");
// 1、判断库存是否充足
Goods goods = new Goods();
goods.setGoodsId(sale.getGoodsId());
goods = goodsDao.selectGoodsById(goods);
if(null == goods){
throw new NullPointerException("编号[" + sale.getGoodsId() + "]商品不存在!");
}
if(goods.getGoodsAmount() < sale.getSaleNum()){
throw new NotEnoughException("库存不足!");
}
// 2、减少库存
goods.setGoodsAmount(goods.getGoodsAmount() - sale.getSaleNum());
goodsDao.updateGoods(goods);
// 3、增加销售记录
saleDao.insertSale(sale);
System.out.println("============ buy end =============");
}
public void setGoodsDao(GoodsDao goodsDao) {
this.goodsDao = goodsDao;
}
public void setSaleDao(SaleDao saleDao) {
this.saleDao = saleDao;
}
}
DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<package name="hom.wang.data"/>
typeAliases>
<mappers>
<mapper resource="hom/wang/dao/GoodsMapper.xml"/>
<mapper resource="hom/wang/dao/SaleMapper.xml"/>
mappers>
configuration>
jdbc.url=jdbc:mysql://127.0.0.1:3306/ms_user
jdbc.username=root
jdbc.password=root
jdbc.maxActive=20
<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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:property-placeholder location="classpath:jdbc.properties">context:property-placeholder>
<bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="${jdbc.url}">property>
<property name="username" value="${jdbc.username}">property>
<property name="password" value="${jdbc.password}">property>
<property name="maxActive" value="${jdbc.maxActive}">property>
bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="myDataSource">property>
<property name="configLocation" value="classpath:mybatis.xml">property>
bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory">property>
<property name="basePackage" value="hom.wang.dao">property>
bean>
<bean id="buyService" class="hom.wang.service.impl.BuyServiceImpl">
<property name="goodsDao" ref="goodsDao">property>
<property name="saleDao" ref="saleDao">property>
bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="myDataSource" />
bean>
<tx:annotation-driven transaction-manager="transactionManager" />
beans>
package hom.wang;
import hom.wang.data.Sale;
import hom.wang.service.BuyService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void test(){
ApplicationContext ac =
new ClassPathXmlApplicationContext("applicationContext.xml");
BuyService buyService = (BuyService) ac.getBean("buyService");
//buyService的代理对象是:com.sun.proxy.$Proxy17,jdk动态代理对象
System.out.println("buyService的代理对象是:" + buyService.getClass().getName());
Sale sale = new Sale();
sale.setGoodsId("p_yz_00002");
sale.setSaleNum(1);
buyService.buy(sale);
}
}
*没错,最终结果正如你所预料的!