Spring 坑路6 -> 事务的基本使用

Spring 事务的基础操作:

  事务管理是企业级应用程序开发中必不可少的技术,用来确保数据的完整性和一致性
  事务就是一系列的动作,它们被当作一个单独的工作单元,这些动作要么全部完成,要么全部不起作用。

事务的四大关键属性(ACID)
原子性(atomicity):

事务是一个原子操作,由一系列动作组成,事务的原子性确保动作要么全部完成要么完全不起作用。

一致性(consistency):

一旦所有事务动作完成,事务就被提交,数据和资源就处于一种满足业务规则的一致性状态中。

隔离性(isolation):

可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。

持久性(durability):

一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,通常情况下,事务的结果被写到持久化存储器中(数据库)。

  作为企业级应用程序框架,Spring 在不同的事务管理 API 之上定义了一个抽象层。而应用程序开发人员不必了解底层的事务管理 API,就可以使用 Spring 的事务管理机制。
Spring 既支持编程式事务管理,也支持声明式事务管理。

编程式事务管理:

将事务管理代码嵌入到业务方法中来控制事务的提交和回滚。在编程式管理事务时,必须在每个事务操作中包含额外的事务管理代码。

声明式事务管理:

大多数情况下比编程式事务管理更好用,它将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理,事务管理作为一种横切关注点,可以通过 AOP 方法模块化。Spring 通过 SpringAOP 框架支持声明式事务管理。

  Spring 从不同的事务管理 API 中抽象了一整套的事务机制,开发人员不必了解底层的事务 API,就可以利用这些事务机制,有了这些事务机制,事务管理代码就能独立于特定的事务技术了。

  Spring 的核心事务管理抽象是 Interface PlatformTransactionManager 它为事务管理封装了一组独立于技术的方法,无论使用 Spring 的哪种事务管理策略(编程式或声明式),事务管理器都是必须的

Spring中的事务管理器的不同实现

Class DataSourceTransactionManager:

在应用程序中只需要处理一个数据源,而且通过JDBC存取、

Class JtaTransationManager:

在 JavaEE应用服务器上用JTA(Java Transation API)进行事务管理。

Class HibernateTransationManager:

用 Hibernate 框架存取数据库

事务管理器以普通的 Bean 形式声明在 SpringIOC 容器中

事务管理是一种横切关注点:

  为了在 Spring2.x 中启用声明式事务管理,可以通过 tx:Schema 中定义的 元素声明事务通知,为此必须先将这个 schema 定义添加到 beans 根元素中去。
  声明了事务通知后,就需要将它与切入点关联起来,由于事务通知是在 元素外部声明的,所以它无法直接与切入点产生关联,所以必须在 元素中声明一个增强器通知与切入点关联起来。
由于 SpringAOP 是基于代理的方法,所以只能增强公共方法,因此,只有共有方法才能通过 SpringAOP 进行事务管理。
  除了在带有切入点,通知和增强器的 Bean 配置文件中声明事务外,Spring 还允许简单地使用 @Transactional 注解来标注事务方法。
  为了将方法定义为支持事务处理的,可以为方法添加 @Transactional 注解,根据 SpringAOP 基于代理机制,只能标注共有方法。
  可以在方法或者类级别上添加 @Transactional 注解,当把这个注解应用到类上时,这个类中的所有公共方法都会被定义成支持事务处理的。
  在 Bean 配置文件中只需要启用 tx:annotation-driven 元素,并为之指定事务管理器就可以了。
  如果事务处理器的名称是 transactionManager,就可以在 元素中省略 transaction-manager 属性,这个元素会自动检测该名称的事务处理器。

Spring 的事务管理实现—编程式事务

PlatformTransactionManager 实现编程式事务管理
1.配置 Spring 事务管理器
2.获取 PlatformTransactionManager 实例
3.获取 TranscationStatus 对象,打开事务
4.提交事务/回滚事务
TransactionTemplate 实现编程式事务管理
1.配置 Spring 事务管理器
2.获取 TransactionTemplate 实例
3.通过 TransactionTemplate.execute() 打开事务环境

Spring 的事务管理实现—声明式事务

注解实现声明式事务管理
1.配置 Spring 事务管理器
2.开启注解式事务管理
3.使用 @Transactional 注解使用事务
TransactionTemplate 实现声明式事务管理
1.配置 Spring 事务管理器
2.<tx:advice> 配置事务属性
3.<aop:config> 配置事务切点

现在开始上代码部分—->
首先是数据库的结构与数据:
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
然后是pom.xml文件和目录结构:
目录结构:
这里写图片描述
pom.xml:

<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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>net.simple</groupId>
    <artifactId>SpringJDBC</artifactId>
    <packaging>jar</packaging>
    <version>0.0.1-SNAPSHOT</version>
    <name>Maven Webapp</name>
    <url>http://maven.apache.org</url>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <springversion>4.2.0.RELEASE</springversion>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${springversion}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${springversion}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${springversion}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${springversion}</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.11</version>
        </dependency>
        <dependency>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
            <version>1.9.3</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.10</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${springversion}</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.1</version>
        </dependency>
    </dependencies>
</project>

BookInfo代码:

package springtx.domain;

public class BookInfo {
    private int id;
    private String name;
    private double price;
    private int stock;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public double getPrice() {
        return price;
    }
    public void setPrice(double price) {
        this.price = price;
    }
    public int getStock() {
        return stock;
    }
    public void setStock(int stock) {
        this.stock = stock;
    }
    @Override
    public String toString() {
        return "BookInfo [id=" + id + ", name=" + name + ", price=" + price + ", stock=" + stock + "]";
    }
    public BookInfo(int id, String name, double price, int stock) {
        super();
        this.id = id;
        this.name = name;
        this.price = price;
        this.stock = stock;
    }
    public BookInfo() {
        super();
    }
}

CustomerInfo 代码:

package springtx.domain;

public class CustomerInfo {
    private int id;
    private String name;
    private double account;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getAccount() {
        return account;
    }

    public void setAccount(double account) {
        this.account = account;
    }

    public CustomerInfo(int id, String name, double account) {
        super();
        this.id = id;
        this.name = name;
        this.account = account;
    }

    public CustomerInfo() {
        super();
    }

    @Override
    public String toString() {
        return "CustomerInfo [id=" + id + ", name=" + name + ", account=" + account + "]";
    }
}

OrderInfo 代码:

package springtx.domain;

public class OrderInfo {
    private int id;
    private String customerName;
    private String bookName;
    private int orderQuantities;
    private double payCount;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getCustomerName() {
        return customerName;
    }

    public void setCustomerName(String customerName) {
        this.customerName = customerName;
    }

    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

    public int getOrderQuantities() {
        return orderQuantities;
    }

    public void setOrderQuantities(int orderQuantities) {
        this.orderQuantities = orderQuantities;
    }

    public double getPayCount() {
        return payCount;
    }

    public void setPayCount(double payCount) {
        this.payCount = payCount;
    }

    @Override
    public String toString() {
        return "OrderInfo [id=" + id + ", customerName=" + customerName + ", bookName=" + bookName
                + ", orderQuantities=" + orderQuantities + ", payCount=" + payCount + "]";
    }

    public OrderInfo(int id, String customerName, String bookName, int orderQuantities, double payCount) {
        super();
        this.id = id;
        this.customerName = customerName;
        this.bookName = bookName;
        this.orderQuantities = orderQuantities;
        this.payCount = payCount;
    }

    public OrderInfo() {
        super();
    }

    public OrderInfo(String customerName, String bookName, int orderQuantities, double payCount) {
        super();
        this.customerName = customerName;
        this.bookName = bookName;
        this.orderQuantities = orderQuantities;
        this.payCount = payCount;
    }
}

AccountException 代码:

package springtx.exception;

public class AccountException extends RuntimeException {
    public AccountException(String message, Throwable cause) {
        super(message, cause);
    }

    public AccountException(String message) {
        super(message);
    }
}

BookStockException 代码:

package springtx.exception;

public class BookStockException extends RuntimeException {
    public BookStockException(String message, Throwable cause) {
        super(message, cause);
    }

    public BookStockException(String message) {
        super(message);
    }
}

BookDao 代码:

package springtx.dao;

import java.util.List;

import springtx.domain.BookInfo;

public interface BookDao {
    BookInfo getBookByName(String name);

    List<BookInfo> listBook();

    double getPriceByName(String name);

    int updateBookStock(String name, int stock);
}

BookDaoImpl 代码:

package springtx.dao.impl;

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.Component;

import springtx.dao.BookDao;
import springtx.domain.BookInfo;
import springtx.exception.BookStockException;

@Component
public class BookDaoImpl implements BookDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public BookInfo getBookByName(String name) {
        String sql = "select * from book where name = ?";
        BookInfo book = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<BookInfo>(BookInfo.class), name);
        return book;
    }

    @Override
    public List<BookInfo> listBook() {
        String sql = "select * from book";
        List<BookInfo> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<BookInfo>(BookInfo.class));
        return (List<BookInfo>) list;
    }

    @Override
    public double getPriceByName(String name) {
        String sql = "select price from book where name = ?";
        double price = jdbcTemplate.queryForObject(sql, double.class, name);
        return price;
    }

    @Override
    public int updateBookStock(String name, int stock) {
        int remainStock = this.getBookByName(name).getStock();
        if (remainStock <= stock)
            throw new BookStockException("库存不足,购买失败!");
        String sql = "update book set stock = stock - ? where name = ?";
        int row = jdbcTemplate.update(sql, stock, name);
        return row;
    }
}

BookService 代码:

package springtx.service;

import java.util.List;

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

import springtx.dao.BookDao;
import springtx.domain.BookInfo;

@Component
public class BookService {
    @Autowired
    private BookDao bookDao;

    public List<BookInfo> listBook() {
        List<BookInfo> list = bookDao.listBook();
        list.forEach(System.out::println);
        return list;
    }

    public BookInfo getBookByName(String name) {
        return bookDao.getBookByName(name);
    }

    double getPriceByName(String name) {
        return bookDao.getPriceByName(name);
    }

    public int updateBookStock(String name, int stock) {
        return bookDao.updateBookStock(name, stock);
    }
}

CustomerDao 代码:

package springtx.dao;

import java.util.List;

import springtx.domain.CustomerInfo;

public interface CustomerDao {

    List<CustomerInfo> listCustomer();

    CustomerInfo getCustomerByName(String name);

    int updateCustomer(String name, double price);
}

CustomerDaoImple 代码:

package springtx.dao.impl;

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.Component;

import springtx.dao.CustomerDao;
import springtx.domain.CustomerInfo;
import springtx.domain.OrderInfo;
import springtx.exception.AccountException;

@Component
public class CustomerDaoImpl implements CustomerDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public CustomerInfo getCustomerByName(String name) {
        String sql = "select * from customer where name = ?";
        CustomerInfo customerInfo = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<CustomerInfo>(CustomerInfo.class),
                name);
        return customerInfo;
    }

    @Override
    public int updateCustomer(String name, double price) {
        double account = this.getCustomerByName(name).getAccount();
        if (account <= price)
            throw new AccountException("金钱不足,购买失败!");
        String sql = "update customer set account = account - ? where name = ?";
        int row = jdbcTemplate.update(sql, price, name);
        return row;
    }

    @Override
    public List<CustomerInfo> listCustomer() {
        String sql = "select * from customer";
        List<CustomerInfo> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<CustomerInfo>(CustomerInfo.class));
        return list;
    }
}

CustomerService 代码:

package springtx.service;

import java.util.List;

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

import springtx.dao.CustomerDao;
import springtx.domain.CustomerInfo;

@Component
public class CustomerService {
    @Autowired
    CustomerDao customerDao;

    public CustomerInfo getCustomerByName(String name) {
        return customerDao.getCustomerByName(name);
    }

    public int updateCustomer(String name, double price) {
        return customerDao.updateCustomer(name, price);
    }

    public List<CustomerInfo> listCustomer() {
        List<CustomerInfo> list = customerDao.listCustomer();
        list.forEach(System.out::println);
        return list;
    }
}

OrderDao 代码:

package springtx.dao;

import java.util.List;

import springtx.domain.OrderInfo;

public interface OrderDao {
    List<OrderInfo> listOrder();

    int insertOrder(OrderInfo orderInfo);

    OrderInfo getOrderById(int id);
}

OrderDaoImpl 代码:

package springtx.dao.impl;

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.Component;

import springtx.dao.OrderDao;
import springtx.domain.OrderInfo;

@Component
public class OrderDaoImpl implements OrderDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public List<OrderInfo> listOrder() {
        String sql = "select * from `order`";
        List<OrderInfo> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<OrderInfo>(OrderInfo.class));
        return list;
    }

    @Override
    public int insertOrder(OrderInfo orderInfo) {
        String sql = "insert into `order`(customer_name,book_name,order_quantities,pay_count)values(?,?,?,?)";
        int row = jdbcTemplate.update(sql, orderInfo.getCustomerName(), orderInfo.getBookName(),
                orderInfo.getOrderQuantities(), orderInfo.getPayCount());
        return row;
    }

    @Override
    public OrderInfo getOrderById(int id) {
        String sql = "select * from `order` where id = ?";
        OrderInfo order = jdbcTemplate.queryForObject(sql, OrderInfo.class, id);
        return order;
    }
}

OrderService 代码:

package springtx.service;

import java.util.List;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;

import springtx.dao.OrderDao;
import springtx.domain.OrderInfo;

@Component
public class OrderService {
    @Autowired
    private OrderDao orderDao;
    @Autowired
    private BookService bookService;
    @Autowired
    private CustomerService customerService;

    public void insertOrder(String customerName, String bookName, int orderQuantities) {
        try {
            OrderInfo orderInfo = new OrderInfo();
            orderInfo.setCustomerName(customerName);
            orderInfo.setBookName(bookName);
            orderInfo.setOrderQuantities(orderQuantities);
            double price = bookService.getPriceByName(bookName);
            double payCount = orderQuantities * price;
            orderInfo.setPayCount(payCount);
            orderDao.insertOrder(orderInfo);
            customerService.updateCustomer(customerName, payCount);
            bookService.updateBookStock(bookName, orderQuantities);
        } catch (RuntimeException e) {
            System.out.println(e);
        }
    }

    public List<OrderInfo> listOrder() {
        List<OrderInfo> list = orderDao.listOrder();
        list.forEach(System.out::println);
        return list;
    }

    public OrderInfo getOrderById(int id) {
        return orderDao.getOrderById(id);
    }
}

db.properties 配置:

jdbc.url=jdbc:mysql://localhost:3306/spring_tx?useSSL=true&serverTimezone=UTC&characterEncoding=UTF-8
jdbc.username=root
jdbc.password=root  

spring-config.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-4.2.xsd
    http://www.springframework.org/schema/context  
    http://www.springframework.org/schema/context/spring-context-4.2.xsd">
    <context:component-scan base-package="springtx" />
    <context:property-placeholder location="classpath:db.properties" />
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
</beans>

SpringTest 代码如下:

package springtx;

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

import springtx.service.BookService;
import springtx.service.OrderService;
import springtx.service.CustomerService;

public class SpringTest {
    public static void main(String[] args) {
        AbstractApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        BookService bookService = (BookService) context.getBean("bookService");
        CustomerService customerService = (CustomerService) context.getBean("customerService");
        OrderService orderService = (OrderService) context.getBean("orderService");
        String customerName = "大明";
        String bookName = "Java从入门到放弃";
        int stock = 15;
        orderService.insertOrder(customerName, bookName, stock);
        bookService.listBook();
        customerService.listCustomer();
        orderService.listOrder();
    }
}

此时代码中并没有事务,只是做了简单的库存判断,运行结果如下:

springtx.exception.AccountException: 金钱不足,购买失败!
BookInfo [id=1, name=Java从入门到放弃, price=15.0, stock=100]
BookInfo [id=2, name=Mysql从删库到跑路, price=22.5, stock=120]
BookInfo [id=3, name=Linux从入门到砸电脑, price=17.0, stock=15]
CustomerInfo [id=1, name=小明, account=40.0]
CustomerInfo [id=2, name=大明, account=70.0]
OrderInfo [id=47, customerName=大明, bookName=Java从入门到放弃, orderQuantities=15, payCount=225.0]

会发现,虽然报了错,在 book 表和 customer 表中的数据都没有发生改变,但是 order 表却插入了一条不应该插入的数据,此时就是没有事务控制所带来的影响了。

此时使用第一种编程式事务模式–

TransactionTemplate 实现编程式事务管理

OrderService 的 insertOrder 部分代码变为:

@Autowired
private PlatformTransactionManager txManager;
public void insertOrder(String customerName, String bookName, int orderQuantities) {
    TransactionTemplate transactionTemplate = new TransactionTemplate(txManager);
    TransactionStatus status = txManager.getTransaction(transactionTemplate);
    try {
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setCustomerName(customerName);
        orderInfo.setBookName(bookName);
        orderInfo.setOrderQuantities(orderQuantities);
        double price = bookService.getPriceByName(bookName);
        double payCount = orderQuantities * price;
        orderInfo.setPayCount(payCount);
        orderDao.insertOrder(orderInfo);
        customerService.updateCustomer(customerName, payCount);
        bookService.updateBookStock(bookName, orderQuantities);
        txManager.commit(status);
    } catch (RuntimeException e) {
        System.out.println(e);
        txManager.rollback(status);
    }
}

spring-config 中加入:

<bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
</bean>

其他地方不变,运行程序,输出结果为:

springtx.exception.AccountException: 金钱不足,购买失败!
BookInfo [id=1, name=Java从入门到放弃, price=15.0, stock=100]
BookInfo [id=2, name=Mysql从删库到跑路, price=22.5, stock=120]
BookInfo [id=3, name=Linux从入门到砸电脑, price=17.0, stock=15]
CustomerInfo [id=1, name=小明, account=40.0]
CustomerInfo [id=2, name=大明, account=70.0]
OrderInfo [id=47, customerName=大明, bookName=Java从入门到放弃, orderQuantities=15, payCount=225.0]

会发现各个地方的数据都是没有改变过的,那么此时的事务控制是成功的了。

接下来演示第二种编程式事务—

PlatformTransactionManager 实现编程式事务管理:

OrderService 的 insertOrder 的代码如下:

public void insertOrder(String customerName, String bookName, int orderQuantities) {
    DefaultTransactionDefinition def = new DefaultTransactionDefinition();
    def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
    def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
    TransactionStatus status = txManager.getTransaction(def);
    try {
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setCustomerName(customerName);
        orderInfo.setBookName(bookName);
        orderInfo.setOrderQuantities(orderQuantities);
        double price = bookService.getPriceByName(bookName);
        double payCount = orderQuantities * price;
        orderInfo.setPayCount(payCount);
        orderDao.insertOrder(orderInfo);
        customerService.updateCustomer(customerName, payCount);
        bookService.updateBookStock(bookName, orderQuantities);
        txManager.commit(status);
    } catch (RuntimeException e) {
        System.out.println(e);
        txManager.rollback(status);
    }

输出结果如下:

springtx.exception.AccountException: 金钱不足,购买失败!
BookInfo [id=1, name=Java从入门到放弃, price=15.0, stock=100]
BookInfo [id=2, name=Mysql从删库到跑路, price=22.5, stock=120]
BookInfo [id=3, name=Linux从入门到砸电脑, price=17.0, stock=15]
CustomerInfo [id=1, name=小明, account=40.0]
CustomerInfo [id=2, name=大明, account=70.0]
OrderInfo [id=47, customerName=大明, bookName=Java从入门到放弃, orderQuantities=15, payCount=225.0]

同样的,事务控制成功。

接下去使用声明式的事务控制:

修改 SpringTest 中的引用为 spring-tx.xml:

AbstractApplicationContext context = new ClassPathXmlApplicationContext("spring-tx.xml");

spring-tx.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-4.2.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
    http://www.springframework.org/schema/context  
    http://www.springframework.org/schema/context/spring-context-4.2.xsd">
    <context:component-scan base-package="springtx" />
    <context:property-placeholder location="classpath:db.properties" />
    <!-- 数据源对象的BEAN -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--1 配置事务管理器 -->
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!-- 2配置事务属性(增强) -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="*" read-only="false" />
        </tx:attributes>
    </tx:advice>
    <!-- 3.配置事务的切入点 事务的切入点以及事务属性关联起来 -->
    <aop:config>
        <aop:pointcut id="txPointCut"
            expression="execution(* springtx.service.*.*(..))" />
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut" />
    </aop:config>
</beans>

OrderService中的insertOrder的代码为:

public void insertOrder(String customerName, String bookName, int orderQuantities) {
    OrderInfo orderInfo = new OrderInfo();
    orderInfo.setCustomerName(customerName);
    orderInfo.setBookName(bookName);
    orderInfo.setOrderQuantities(orderQuantities);
    double price = bookService.getPriceByName(bookName);
    double payCount = orderQuantities * price;
    orderInfo.setPayCount(payCount);
    orderDao.insertOrder(orderInfo);
    customerService.updateCustomer(customerName, payCount);
    bookService.updateBookStock(bookName, orderQuantities);
}

运行程序:
报错:

Exception in thread "main" springtx.exception.AccountException: 金钱不足,购买失败!
    at springtx.dao.impl.CustomerDaoImpl.updateCustomer(CustomerDaoImpl.java:32)
    at springtx.service.CustomerService.updateCustomer(CustomerService.java:21)
    at springtx.service.CustomerService$$FastClassBySpringCGLIB$$409f0601.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:717)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:653)
    at springtx.service.CustomerService$$EnhancerBySpringCGLIB$$74ebc1fb.updateCustomer(<generated>)
    at springtx.service.OrderService.insertOrder(OrderService.java:39)
    at springtx.service.OrderService$$FastClassBySpringCGLIB$$2f9eb2bd.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:717)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:653)
    at springtx.service.OrderService$$EnhancerBySpringCGLIB$$4e6cf337.insertOrder(<generated>)
    at springtx.SpringTest.main(SpringTest.java:19)

不过数据库中也没有改变任何的数据,此时控制成功。

修改 main 方法:

package springtx;

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

import springtx.service.BookService;
import springtx.service.OrderService;
import springtx.service.CustomerService;

public class SpringTest {
    public static void main(String[] args) {
        AbstractApplicationContext context = new ClassPathXmlApplicationContext("spring-tx.xml");
        BookService bookService = (BookService) context.getBean("bookService");
        CustomerService customerService = (CustomerService) context.getBean("customerService");
        OrderService orderService = (OrderService) context.getBean("orderService");
        String customerName = "大明";
        String bookName = "Java从入门到放弃";
        int stock = 15;
        try {
            orderService.insertOrder(customerName, bookName, stock);
        } catch (RuntimeException e) {
            System.out.println(e);
        }
        bookService.listBook();
        customerService.listCustomer();
        orderService.listOrder();
    }
}

运行输出:

springtx.exception.AccountException: 金钱不足,购买失败!
BookInfo [id=1, name=Java从入门到放弃, price=15.0, stock=100]
BookInfo [id=2, name=Mysql从删库到跑路, price=22.5, stock=120]
BookInfo [id=3, name=Linux从入门到砸电脑, price=17.0, stock=15]
CustomerInfo [id=1, name=小明, account=40.0]
CustomerInfo [id=2, name=大明, account=70.0]
OrderInfo [id=47, customerName=大明, bookName=Java从入门到放弃, orderQuantities=15, payCount=225.0]

最后是声明式事务的第二种实现方式,也是最常用的实现方式:

注解实现声明式事务管理

SpringText 中的引用文件切换为 spring-tx-annotation.xml:

AbstractApplicationContext context = new ClassPathXmlApplicationContext("spring-tx-annotation.xml");

配置文件 spring-tx-annotation.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:util="http://www.springframework.org/schema/util"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
    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/util http://www.springframework.org/schema/util/spring-util-4.2.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
    <context:component-scan base-package="springtx"></context:component-scan>
    <!-- 导入外部的配置文件 -->
    <context:property-placeholder location="classpath:db.properties" />
    <!-- 数据源对象的BEAN -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--1 配置事务管理器 -->
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--2 启用注解 -->
    <tx:annotation-driven transaction-manager="transactionManager" />
</beans>

并且在 OrderService 的 insertOrder 方法上加一个注解:

@Transactional(propagation = Propagation.REQUIRED)
    public void insertOrder(xxx);

运行 main 方法输出:

springtx.exception.AccountException: 金钱不足,购买失败!
BookInfo [id=1, name=Java从入门到放弃, price=15.0, stock=100]
BookInfo [id=2, name=Mysql从删库到跑路, price=22.5, stock=120]
BookInfo [id=3, name=Linux从入门到砸电脑, price=17.0, stock=15]
CustomerInfo [id=1, name=小明, account=40.0]
CustomerInfo [id=2, name=大明, account=70.0]
OrderInfo [id=47, customerName=大明, bookName=Java从入门到放弃, orderQuantities=15, payCount=225.0]

数据库依然没有改变,此时事务依然控制成功。未完待续……

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值