• Spring中的事物管理,用 @Transactional 注解声明式地管理事务


    事物:

      事务管理是企业级应用程序开发中必不可少的技术,  用来确保数据的 完整性和 一致性.

      事务就是一系列的动作, 它们被当做一个单独的工作单元. 这些动作要么全部完成, 要么全部不起作用

    事务的四个关键属性:

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

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

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

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

    -------------------------------------------------------------------

    用 @Transactional 注解声明式地管理事务的例子:

    在mysql中建立三个数据表分别为:

    account:属性:用户名:username(varchar),该用户的账户余额:balance(int);

    book:书的编号:isbn(int),书名:book_name(varchar),价钱:price(int);

    book_stock:书的编号:isbn(int),库存:stock(int);

    导包。。。。

    建立存放连接数据库的file文件:jdbc.properties;

    jdbc.user=root
    jdbc.password=lxn123
    jdbc.driverClass=com.mysql.jdbc.Driver
    jdbc.jdbcUrl=jdbc:mysql:///spring2
    
    jdbc.initPoolSize=5
    jdbc.maxPoolSize=10

    建立spring bean configuration file的xml文件,导入资源文件和配置c3p0数据源,这是基于注解的声明管理事物,所以也将加特定注解的包进行扫描;

        <!-- 基于注解的bean配置,扫描这个包及其自包 -->
        <context:component-scan base-package="com.atguigu.spring.jdbc"></context:component-scan>
        
        <!-- 导入资源文件 -->
        <context:property-placeholder location="classpath:jdbc.properties"/>
        
        <!-- 配置c3p0数据源 -->
        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="user" value="${jdbc.user}"></property>
            <property name="password" value="${jdbc.password}"></property>
            <property name="driverClass" value="${jdbc.driverClass}"></property>
            <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
            
            <property name="initialPoolSize" value="${jdbc.initPoolSize}"></property>
            <property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
        </bean>
        
        <!-- 配置spring 的 JdbcTemplate ,里面有一些jdbc的方法,实现对数据库数据的增删改查-->
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <property name="dataSource" ref="dataSource"></property>
        </bean>

    建立一个接口:让继承他的子类去实例化一些方法

    package com.atguigu.spring.jdbc;
    
    public interface BookShopDao {
        
        //根据书号获取书的单价,isbn为书名的编号
        public int findBookPriceByIsbn(int isbn);
        
        //更新数的库存. 使书号对应的库存 - 1
        public void updateBookStock(int isbn);
        
        //更新用户的账户余额: 使 username 的 balance - price
        public void updateUserAccount(String username,int price);
    }

    建立BookShopDaoImpl 类并继承接口: BookShopDao,实例化其方法,并且在类名前 加注解:@Repository("bookShopDao"),和在属性private JdbcTemplate jdbcTemplae;的上边加注解:@Autowired

    package com.atguigu.spring.jdbc;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.stereotype.Repository;
    
    @Repository("bookShopDao")
    public class BookShopDaoImpl implements BookShopDao {
        
        @Autowired
        private JdbcTemplate jdbcTemplae;
        
        //通过书名获取书的价格
        @Override
        public int findBookPriceByIsbn(int isbn) {
            String sql="select price from book where isbn=?";
            return jdbcTemplae.queryForObject(sql, Integer.class, isbn);
        }
        
        
        @Override
        public void updateBookStock(int isbn) {
            String sql="select stock from book_stock where isbn=?";
            //stock,指数的库存
            //通过书名检查书的库存
            int stock=jdbcTemplae.queryForObject(sql, Integer.class, isbn);
            if(stock==0){
                System.out.println("库存不足!!!");
                throw new BookStockException("库存不足!!!");
            }
            
            String sql2="update book_stock set stock=stock-1 where isbn=?";
            jdbcTemplae.update(sql2, isbn);
        }
        
        //用户的余额减去要购买书的价钱
        @Override
        public void updateUserAccount(String username, int price) {
            //验证余额是否足够, 若不足, 则抛出异常
            String sql2="select balance from account where username=?";
            int balance=jdbcTemplae.queryForObject(sql2, Integer.class, username);
            if(balance<price){
                System.out.println("余额不足!!!");
                throw new BookStockException("余额不足!!!");
            }
            
            String sql="update account set balance=balance-? where username=?";
            jdbcTemplae.update(sql, price, username);
        }
    
    }

    建立一个接口:BookShopService,有一个没有实例化的方法;

    package com.atguigu.spring.jdbc;
    
    public interface BookShopService {
        public void purchase(String username,int isbn);
    }

    建立类: BookShopServiceImpl 继承于接口 BookShopService,有一个方法,合并了BookShopDaoImpl类中的三个方法,建立了一个“事物”;在该类上边加了注解@Service("bookShopService"),在属性private BookShopDao bookShopDao;加了注解@Autowired,在其方法上加了注解@Transactional(propagation=Propagation.REQUIRES_NEW),都是便于在spring的xml文件中bean的配置扫描识别;

    package com.atguigu.spring.jdbc;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Propagation;
    import org.springframework.transaction.annotation.Transactional;
    
    @Service("bookShopService")
    public class BookShopServiceImpl implements BookShopService {
        
        @Autowired
        private BookShopDao bookShopDao;
        /**    
        *添加事务注解
        *1.使用 propagation 指定事务的传播行为, 即当前的事务方法被另外一个事务方法调用时
        *如何使用事务, 默认取值为 REQUIRED, 即使用调用方法的事务
        *REQUIRES_NEW: 事务自己的事务, 调用的事务方法的事务被挂起. 
        *2.使用 isolation 指定事务的隔离级别, 最常用的取值为 READ_COMMITTED
        *3.默认情况下 Spring 的声明式事务对所有的运行时异常进行回滚. 也可以通过对应的
        *属性进行设置. 通常情况下去默认值即可. 
        *4.使用 readOnly 指定事务是否为只读. 表示这个事务只读取数据但不更新数据, 
        *这样可以帮助数据库引擎优化事务. 若真的事一个只读取数据库值的方法, 应设置 readOnly=true
        *5.使用 timeout 指定强制回滚之前事务可以占用的时间.  
        *@Transactional(propagation=Propagation.REQUIRES_NEW,
                isolation=Isolation.READ_COMMITTED,
                noRollbackFor={UserAccountException.class},
                readOnly=false,
                timeout=3)
     * 
     */
        @Transactional(propagation=Propagation.REQUIRES_NEW)
        @Override
        public void purchase(String username, int isbn) {
            
            //1. 获取书的单价
            int price=bookShopDao.findBookPriceByIsbn(isbn);
            
            //2. 更新书的库存
            bookShopDao.updateBookStock(isbn);
            
            //3. 更新用户余额
            bookShopDao.updateUserAccount(username, price);
        }
    
    }

    建立一个接口:Cashier

    package com.atguigu.spring.jdbc;
    
    import java.util.List;
    
    public interface Cashier {
        //购买多本书
        public void checkout(String username,List<Integer> isbns);
    }

    建立类:CashierImpl 继承于上边的接口 Cashier,该中有private BookShopService bookShopService这个“事物”父类的属性,类中仅有的一个方法,可以实现多个“原子事物",该类是调用事物的事物类,仅有的方法为调用事物的事物方法;在该类上边加注解@Service("cashier"),在属性private BookShopService bookShopService;上边加注解@Autowired,在方法上边加事物的注解@Transactional

    package com.atguigu.spring.jdbc;
    
    import java.util.List;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    @Service("cashier")
    public class CashierImpl implements Cashier {
        
        @Autowired
        private BookShopService bookShopService;
        
        @Transactional
        @Override
        public void checkout(String username, List<Integer> isbns) {
            for(int isbn:isbns){
                bookShopService.purchase(username, isbn);
            }
    
        }
    
    }

    在spring的xml文件中 配置事物管理器和 启用事物管理器的注解

        <!-- 配置事物管理器 -->
        <bean id="transactionManager" 
            class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
        
        <!-- 启用事物管理器的注解 -->
        <tx:annotation-driven transaction-manager="transactionManager"/>

    最后建立测试类:JUnitTest

    package com.atguigu.spring.jdbc;
    
    import java.sql.SQLException;
    import java.util.Arrays;
    
    import javax.sql.DataSource;
    
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import org.springframework.jdbc.core.JdbcTemplate;
    
    
    public class JUnitTest {
        
        private ApplicationContext ctx=null;
        private BookShopService bookShopService;
        private Cashier cashier;
        {
            ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
            bookShopService=(BookShopService) ctx.getBean("bookShopService");
            cashier=(Cashier) ctx.getBean("cashier");
        }
        
        @Test
        //调用事物的事物方法的测试,即有多个事物
        public void testCashier(){
            cashier.checkout("AA", Arrays.asList(1001,1002));
        }
        @Test
        //事物的测试方法
        public void testPurchase(){
            bookShopService.purchase("AA", 1001);
        }
        //测试数据库连接池是否连接成功
        public void testDataSource() throws SQLException {
            DataSource dataSource=(DataSource) ctx.getBean("dataSource");
            System.out.println(dataSource.getConnection());
            
        }
    
    }

    建立一个异常处理类:BookStockException 继承于异常处理父类 RuntimeException

    package com.atguigu.spring.jdbc;
    
    public class BookStockException extends RuntimeException {
        //异常类的建立:继承于RuntimeException这个父类,点击上边的Source,设置构造器,即可
        public BookStockException() {
            super();
            // TODO Auto-generated constructor stub
        }
    
        public BookStockException(String arg0, Throwable arg1, boolean arg2,
                boolean arg3) {
            super(arg0, arg1, arg2, arg3);
            // TODO Auto-generated constructor stub
        }
    
        public BookStockException(String arg0, Throwable arg1) {
            super(arg0, arg1);
            // TODO Auto-generated constructor stub
        }
    
        public BookStockException(String arg0) {
            super(arg0);
            // TODO Auto-generated constructor stub
        }
    
        public BookStockException(Throwable arg0) {
            super(arg0);
            // TODO Auto-generated constructor stub
        }
    
        
        
    }
  • 相关阅读:
    create-react-app搭建的项目中添加bootstrap
    用es6的Array.reduce()方法计算一个字符串中每个字符出现的次数
    为Docker配置阿里加速器,系统为Debian8
    基于Spring Boot,使用JPA动态调用Sql查询数据
    基于Spring Boot,使用JPA调用Sql Server数据库的存储过程并返回记录集合
    基于Spring Boot,使用JPA操作Sql Server数据库完成CRUD
    ES6,Array.includes()函数的用法
    【编程风格】c++命名约定
    【转】吴恩达的视频课程做成了文字版 ~~~
    【专业学习】常用的技术网站
  • 原文地址:https://www.cnblogs.com/lxnlxn/p/5873648.html
Copyright © 2020-2023  润新知