zoukankan      html  css  js  c++  java
  • 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
        }
    
        
        
    }
  • 相关阅读:
    poj 1743 Musical Theme 后缀数组
    poj 1743 Musical Theme 后缀数组
    cf 432D Prefixes and Suffixes kmp
    cf 432D Prefixes and Suffixes kmp
    hdu Data Structure? 线段树
    关于position和anchorPoint之间的关系
    ios POST 信息
    CALayers的代码示例
    CALayers详解
    ios中得sqlite使用基础
  • 原文地址:https://www.cnblogs.com/lxnlxn/p/5873648.html
Copyright © 2011-2022 走看看