Spring 配置事务的两种方式(注解和XML)

1、通过注解的方式配置事务

创建以下3张表

create table book(

     isbn varchar(50) primary key,

     book_name varchar(100),

     price int

     ) ;

 create table book_stock(

     isbn varchar(50) primary key,

     stock int,

     check(stock > 0)

     );

create table account(

     username varchar(50) primary key,

     balance int,

     check(balance > 0)

     );

配置事务管理器

<!-- 配置事务管理器 -->

    <bean id="transactionManager"

      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

       <property name="dataSource" ref="dataSource"></property>   

    </bean>

  

    <!-- 启用事务注解 -->

    <tx:annotation-driven transaction-manager="transactionManager"/>

创建BookShopDao接口

public interface BookShopDao {

   

    //根据书号获取书的单价

    int findBookPriceByIsbn(String isbn);

   

    //更新书的库存,书书号对应的书的库存减一

    void updateBookStock(String isbn);

   

    //更新用户的账户余额,使usernamebalance减对应的书的price

    void updateUserAccount(String username,int price);

}

创建BookShopDao接口实现类BookShopDaoImpl

@Repository("bookShopDao")

public class BookShopDaoImpl implements BookShopDao{

 

    @Autowired

    private JdbcTemplate jdbctemplate;

   

    @Override

    public int findBookPriceByIsbn(String isbn) {

       String sql = "select price from book where isbn = ?";

       return jdbctemplate.queryForObject(sql, Integer.class, isbn);

    }

 

    @Override

    public void updateBookStock(String isbn) {

       String sql2 = "select stock from book_stock where isbn = ?";

       int count = jdbctemplate.queryForObject(sql2, Integer.class, isbn);

       if(count <=0 ){

           throw new BookStockException("库存不足!");

       }

      

       String sql = "update book_stock set stock = stock-1 where isbn = ?";

       jdbctemplate.update(sql, isbn);

    }

 

    @Override

    public void updateUserAccount(String username, int price) {

       String sql2 = "select balance from account where username = ?";

       int balance = jdbctemplate.queryForObject(sql2, Integer.class, username);

       if(balance < price){

           throw new UserAccountException("余额不足!");

       }

       String sql = "update account set balance = balance - ? where username = ?";

       jdbctemplate.update(sql, price,username);

    }

 

}

创建BookShopService

public interface BookShopService {

   

    //用户购买一本书

    void purchase(String username,String isbn);

   

}

创建BookShopService

@Service("bookShopService")

public class BookShopServiceImpl implements BookShopService{

 

    @Autowired

    private BookShopDao bookShopDao;

   

    //添加事务注解

    @Transactional

    @Override

    public void purchase(String username, String isbn) {

       //查询书的价格

       int price = bookShopDao.findBookPriceByIsbn(isbn);

      

       //更新书的库存

       bookShopDao.updateBookStock(isbn);

      

       //更新用户余额

       bookShopDao.updateUserAccount(username, price);

    }

 

}

 

测试

public class TestTx {

   

    ApplicationContext ctx = null;

    BookShopDao bookShopDao = null;

    BookShopService bookShopService = null;

    {

       ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

       bookShopDao = (BookShopDao) ctx.getBean("bookShopDao");

       bookShopService = (BookShopService) ctx.getBean("bookShopService");

    }

   

    @Test

    public void testFindBookPriceByIsbn(){

       System.out.println(bookShopDao.findBookPriceByIsbn("1001"));

    }

   

    @Test

    public void testUpdateBookStock(){

       bookShopDao.updateBookStock("1001");

    }

 

    @Test

    public void testUpdateUserAccount(){

       bookShopDao.updateUserAccount("AA", bookShopDao.findBookPriceByIsbn("1001"));

    }

   

    @Test

    public void testPurchase(){

       bookShopService.purchase("AA", "1001");

    }

   

}

当执行testPurchase会发现如果没加声明事务管理器,当余额不足够买一本书时,书的库存量会减一但用户的余额不会减,声明了事务之后,当余额不足够买一本书时,书的库存和用户的余额都不会减少

 

事务的传播行为

使用propagation指定事务的传播行为,即当前的事务方法被另一个事务方法调用时,如何使用事务

默认取值为REQUIRED,即使用调用方法的事务

REQUIRED:如果有事务运行,当前的方法就在这个事务内运行,否则,就启动一个新的事务,并在自己的事务内运行

REQUIRED_NEW:当前的方法必须启动新事务,并在它自己的事务内运行,如果有事务正在运行,应该将它挂起

SUPPORTS:如果有事务在运行,当前的方法就在这个事务内运行,否则它可以不运行在事务中

NOT_SUPPORTED:当前的方法不应该运行在事务中,如果有运行的事务,将它挂起

MANDATORY:当前的方法不应该运行在事务内部,如果没有正在运行的事务,就抛出异常

NEVER:当前的方法不应该运行在事务中,如果有运行的事务,就抛出异常

NESTED:如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运行,否则,就启动一个新的事务,并在它自己的事务内运行

 

在以上测试事务的基础上添加如下代码

Cashier接口,添加一个同时购买多本书的方法

public interface Cashier {

   

    void checkout(String username,List<String> isbns);

   

}

Cashier接口的实现类CashierImpl

@Service("cashier")

public class CashierImpl implements Cashier{

 

    @Autowired

    private BookShopService bookShopService;

   

    @Transactional

    @Override

    public void checkout(String username, List<String> isbns) {

       for (String isbn : isbns) {

           bookShopService.purchase(username, isbn);

       }

    }

 

}

BookShopServiceImplpurchase方法上指定事务的传播行为

/**

     * 添加事务注解

     * 使用propagation指定事务的传播行为,即当前的事务方法被另一个事务方法调用时,如何使用事务

     * 默认取值为REQUIRED,即使用调用方法的事务

     * REQUIRES_NEW:新开一个事务,即使用自己的事务,调用的事方法的事务被挂起

     */

    @Transactional(propagation=Propagation.REQUIRED)

    @Override

    public void purchase(String username, String isbn) {

       //查询书的价格

       int price = bookShopDao.findBookPriceByIsbn(isbn);

      

       //更新书的库存

       bookShopDao.updateBookStock(isbn);

      

       //更新用户余额

       bookShopDao.updateUserAccount(username, price);

    }

 

@Test

    public void testTransactionPropagation(){

       cashier.checkout("AA", Arrays.asList("1001","1002"));

    }

 

当事务的传播行为为REQUIRED时,若用户余额不足,则所购买的所有书籍库存和用户余额都不会减少

当事务的传播行为为REQUIRES_NEW时,若用户余额不够支付所购买的所有书籍,则会购买前面能够购买的书籍,相应书籍库存和用户余额会减少

 

事务的隔离级别、回滚、只读、过期属性

/**

     * 添加事务注解

     * 1、使用propagation指定事务的传播行为,即当前的事务方法被另一个事务方法调用时,如何使用事务

     * 默认取值为REQUIRED,即使用调用方法的事务

     * REQUIRES_NEW:新开一个事务,即使用自己的事务,调用的事方法的事务被挂起

     * 2、使用isolation指定事务的隔离级别,最常用的取值为READ_COMMITTED

     * 3、默认情况下Spring的声明式事务对所有的运行时异常进行回滚,也可以通过对应的属性进行设置,

     * 通常情况下取默认值

     * 4、使用readOnly指定事务是否为只读,表示这个事务只读数据但不更新数据,

     * 这样可以帮助数据库引擎优化事务,若真的是一个只读取数据库的方法,应设置readOnly=true

     * 5、使用timeout指定强制回滚之前事务可以占用的时间,单位秒

     */

    @Transactional(propagation=Propagation.REQUIRED,

           isolation=Isolation.READ_COMMITTED,

           //noRollbackFor={UserAccountException.class},

           readOnly=false,

           timeout=3

           )

    @Override

    public void purchase(String username, String isbn) {

      

       try {

           Thread.sleep(5000);

       } catch (InterruptedException e) {

           // TODO Auto-generated catch block

           e.printStackTrace();

       }

      

       //查询书的价格

       int price = bookShopDao.findBookPriceByIsbn(isbn);

      

       //更新书的库存

       bookShopDao.updateBookStock(isbn);

      

        //更新用户余额

       bookShopDao.updateUserAccount(username, price);

    }

 

2、基于配置文件的方式配置事务

<!-- 配置bean -->

    <bean id="bookShopDao" class="beans.tx.xml.BookShopDaoImpl">

       <property name="jdbctemplate" ref="jdbcTemplate"></property>

    </bean>

   

    <bean id="bookShopService" class="beans.tx.xml.BookShopServiceImpl">

       <property name="bookShopDao" ref="bookShopDao"></property>

    </bean>

   

    <bean id="cashier" class="beans.tx.xml.CashierImpl">

       <property name="bookShopService" ref="bookShopService"></property>

    </bean>

   

    <!-- 配置事务管理器 -->

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

       <property name="dataSource" ref="dataSource"></property>

    </bean>

   

    <!-- 配置事务属性 -->

    <tx:advice id="txAdvice" transaction-manager="transactionManager">

       <tx:attributes>

           <!-- 根据方法名指定事务的属性 -->

           <tx:method name="purchase" propagation="REQUIRES_NEW"/>

           <tx:method name="*"/>

       </tx:attributes>

    </tx:advice>

   

    <!-- 配置事务切入点,以及把事务切入点和事务属性关联起来 -->

    <aop:config>

       <aop:pointcut expression="execution(* beans.tx.xml.BookShopService.*(..))

           || execution(* beans.tx.xml.Cashier.*(..))"

           id="txPointCut"/>

       <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>

    </aop:config>

 

以上内容均为学习笔记,可能不全面,推荐大家去百度传课学习尚硅谷的免费网课,讲的很好

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值