Spring申明式事物
来源:互联网 发布:vb对皮肤瘙痒有用吗 编辑:程序博客网 时间:2024/05/21 20:24
SPring的事物有两种方式,一种是编程式事物,一种是基于注解的申明式事物,编程式事物由纯Java代码编写,侵入性比较强,所以实际项目上应用比较少。本章主要介绍的就是Spring的申明式事物,在介绍Spring事物之前,首先要了解几个基本概念:
- 事物是什么?
官方一点的说法,事物就是数据库的一次原子操作。何为原子操作?就是逻辑上一个不可分割的操作单元,比如本章中举的例子,取商店买书,你要给老板钱(减余额),然后老板给你书(减库存),这是两个不可少的步骤,其中一个操作不成功,那么本次操作就算失败了,这就是事物 - 事物的基本特性
原子性,隔离性,一致性,持久性。(具体本章不再讨论,不清楚的请自行百度)。
本案例中使用的数据库是oracle 11g,客户端工具使用的是PLSQL,首先测试之前要创建好相关的表结构和数据:
CREATE TABLE BOOK( BID INTEGER PRIMARY KEY,--书本主键 BNAME VARCHAR2(50) NOT NULL,--书本名称 PRICE FLOAT NOT NULL,--价格 STOCK INTEGER DEFAULT 0--库存);CREATE TABLE CUSTOMER( CID INTEGER PRIMARY KEY,--人员编号 CNAME VARCHAR2(25) NOT NULL,--人员名称 BALANCE FLOAT DEFAULT 0--人员余额);INSERT INTO BOOK VALUES(1,'三国演义',19.5,10);INSERT INTO BOOK VALUES(2,'红楼梦',21,5);INSERT INTO CUSTOMER VALUES(1,'张三',250);
这里创建了一张Customer和Book表,并插入了几条数据做测试数据。接下来Java代码的编写,本项目使用mavne和idea来搭建的:
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.12.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>4.3.12.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-dbcp2 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-dbcp2</artifactId> <version>2.1.1</version> </dependency> <!-- https://mvnrepository.com/artifact/com.oracle/ojdbc6 --> <dependency> <groupId>com.oracle</groupId> <artifactId>ojdbc6</artifactId> <version>12.1.0.1-atlassian-hosted</version> </dependency> <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.11</version> </dependency>
这里引用了spring-aop,spring-core,spring-beans,spring-context,spring-jdbc,spring-tx,aspectjweaver,commons-logging,commons-dbcp,commons-pools等一些关键的jar。
BookDao.java
import org.springframework.beans.factory.annotation.Autowired;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.stereotype.Repository;import java.util.Map;@Repository("bookDao")public class BookDao { @Autowired JdbcTemplate jdbc; public Map<String,Object> findBookInfoById(int id,String fields) throws Exception{ String sql = "SELECT "+fields+" FROM BOOK WHERE BID="+id+""; if (jdbc == null) System.out.println("未获取到jdbc"); Map<String,Object> obj = jdbc.queryForMap(sql); if (obj == null) throw new RuntimeException("未找到书籍!"); return obj; } public void subBookStock(int id,int amount) throws Exception{ String sql = "UPDATE BOOK SET STOCK = STOCK-"+amount+" WHERE ID = "+id; jdbc.execute(sql); }}
CustomerDao.java
import org.springframework.beans.factory.annotation.Autowired;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.stereotype.Repository;import java.util.Map;@Repository("customerDao")public class CustomerDao { @Autowired JdbcTemplate jdbc; public float findCustomerBalance(int id) throws Exception{ String sql = "SELECT BALANCE FROM CUSTOMER WHERE CID = "+id; Map<String,Object> obj = jdbc.queryForMap(sql); if (obj == null) throw new RuntimeException("没有找到该用户!"); return Integer.parseInt(obj.get("BALANCE").toString()); } public void subCustomerBalance(int id,float total) throws Exception{ String sql = "UPDATE CUSTOMER SET BALANCE = BALANCE-"+total+"WHERE CID = "+id; }}
ShopService.java
import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;import java.util.Map;@Service("shopService")public class ShopService { @Autowired private BookDao bookDao; @Autowired private CustomerDao customerDao; /** * 销售数据 * @param bid 书本编号 * @param cid 人员编号 * @param amount 书本数量 */ @Transactional public void sellBook(int bid,int cid,int amount) throws Exception { Map<String,Object> bookInfo = bookDao.findBookInfoById(bid,"PRICE,STOCK"); float balance = customerDao.findCustomerBalance(cid); float price = Float.parseFloat(bookInfo.get("PRICE").toString()); int stock = Integer.parseInt(bookInfo.get("STOCK").toString()); float total = price*amount; if (balance < total) throw new RuntimeException("余额不足!"); customerDao.subCustomerBalance(cid, total);//顾客交钱 if (stock < amount) throw new RuntimeException("库存不足!"); bookDao.subBookStock(bid, amount);//减书本库存 }}
App.java
import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class App { public static void main( String[] args ) { ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml"); ShopService shopService = (ShopService) context.getBean("shopService"); if (shopService == null) System.out.println("获取shopService失败"); try { shopService.sellBook(1,1,11);//可以修改相关的参数和购买数量 System.out.println("出售成功"); }catch (Exception e){ System.out.println("出错--->"+e.getLocalizedMessage()); } }}
spring-context.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:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd "> <beans> <context:annotation-config/> <tx:annotation-driven/> <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"> <property name="driverClassName" value="oracle.jdbc.OracleDriver"/> <property name="url" value="jdbc:oracle:thin:@localhost:1521:ORCL"/> <property name="username" value="amxing"/> <property name="password" value="950411"/> <property name="defaultAutoCommit" value="true"/> <property name="defaultQueryTimeout" value="10000"/> <property name="enableAutoCommitOnReturn" value="true"/> </bean> <bean id="jdbc" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="bookDao" class="com.amx.BookDao"/> <bean id="customerDao" class="com.amx.CustomerDao"/> <bean id="shopService" class="com.amx.ShopService"/> <tx:advice id="tx_bookservice"> <tx:attributes> <tx:method name="sellBook" rollback-for="Exception" propagation="MANDATORY"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="sellMethod" expression="execution(* com.amx.ShopService.sellBook(..))"/> <aop:advisor advice-ref="tx_bookservice" pointcut-ref="sellMethod"/> </aop:config> </beans></beans>
这就是主要的代码,可以看到spring的申明式事物主要是基于aop来实现的,配置好事物规则,再在相关的方法上添加@Transactional注解即可关于这个注解,有几个参数
- value String 可选的限定描述符,指定使用的事务管理器
- propagation enum: Propagation 可选的事务传播行为设置
- isolation enum: Isolation 可选的事务隔离级别设置
- readOnly boolean 读写或只读事务,默认读写
- timeout int (in seconds granularity) 事务超时时间设置
- rollbackFor Class对象数组,必须继承自Throwable 导致事务回滚的异常类数组
- rollbackForClassName 类名数组,必须继承自Throwable 导致事务回滚的异常类名字数组
- noRollbackFor Class对象数组,必须继承自Throwable 不会导致事务回滚的异常类数组
- noRollbackForClassName 类名数组,必须继承自Throwable 不会导致事务回滚的异常类名字数组
显而易见sellBook方法的参数为1,1,11,就是张三这个人要买11本红楼梦,但是呢,红楼梦只有10,所以事物是失败的,但是在sellBook方法中,用户是先交钱,张三的余额有250元是足够买11本红楼梦的,所以这时张三的余额是会减少的,但是接下来库存不够了,书本减少库存这个操作就不能做了,所以如果我们此处不用事物的话,会出现以下的情况:
程序执行失败,然后取查看一下用户的余额:
余额减少了19.5*11元,再去看一下书本的库存:
这个时候就发现问题了,用户的余额减少了,但是商品的库存没有减少,这在逻辑上是完全不正确的,所以这个时候就体现了事物的作用。如果这里我们使用了事物就不会出现这样的情况了。
这是一个最基本的Spring申明式事物的小例子,当然在实际开发的过程中,很多复杂的逻辑会写在存储过程中,这里我也写了一个小例子,仅供参考:
--出售商品CREATE OR REPLACE PROCEDURE PROC_SELL_BOOK(V_BID IN INTEGER, V_CID IN INTEGER, V_AMOUNT IN INTEGER, V_RESULT OUT VARCHAR2) AS SELL_EXCEPTION EXCEPTION; V_SLID INTEGER; --日志ID V_NAME VARCHAR2(50); --商品名称 V_STOCK INTEGER; --库存数量 V_PRICE FLOAT; --商品价格 V_CNAME VARCHAR2(50); --顾客姓名 V_BALANCE FLOAT; --账户余额BEGIN SELECT SELL_LOG_ID.NEXTVAL INTO V_SLID FROM DUAL; SELECT BNAME, PRICE, STOCK INTO V_NAME, V_PRICE, V_STOCK FROM BOOK WHERE BID = V_BID; SELECT CNAME, BALANCE INTO V_CNAME, V_BALANCE FROM CUSTOMER WHERE CID = V_CID; IF V_BALANCE < V_PRICE * V_AMOUNT THEN V_RESULT := V_CNAME || '的余额不足!'; RAISE SELL_EXCEPTION; ELSE UPDATE CUSTOMER SET BALANCE = BALANCE - V_PRICE * V_AMOUNT WHERE CID = V_CID; END IF; IF V_STOCK < V_AMOUNT THEN V_RESULT := V_NAME || '的库存不足!'; RAISE SELL_EXCEPTION; ELSE UPDATE BOOK SET STOCK = STOCK - V_AMOUNT WHERE BID = V_BID; END IF; INSERT INTO SELL_LOG VALUES (V_SLID, V_BID, V_CID, V_RESULT, SYSDATE); COMMIT;EXCEPTION WHEN SELL_EXCEPTION THEN ROLLBACK; INSERT INTO SELL_LOG VALUES (V_SLID, V_BID, V_CID, V_RESULT, SYSDATE); COMMIT; WHEN NO_DATA_FOUND THEN V_RESULT := '未找到对应的商品或用户,请检查用户ID是是否正确!'; INSERT INTO SELL_LOG VALUES (V_SLID, V_BID, V_CID, V_RESULT, SYSDATE); COMMIT;END;
测试结果如下:
- Spring申明式事物
- 申明式事物管理
- 6.springboot整合mybitas(xml方式)+开启申明式事物
- spring的申明式事务处理
- Spring声明式事物
- spring 声明式事物
- spring AOP事物管理(声明式事物)
- Spring 申明式事务之注解
- Spring 申明式事务之XML模式
- Spring 声明式事物详解
- Spring声明式事物配置
- Spring 事物
- Spring事物
- Spring事物
- spring 事物
- spring事物
- Spring事物
- spring 事物
- AutoMapper官方文档(十八)【条件映射,开放泛型,了解你的映射】
- 变量
- 欢迎使用CSDN-markdown编辑器
- 基于Android的Traceroute(路由分析)软件实现
- MVC、MVP和MVVM的图示
- Spring申明式事物
- Servlet的配置(四)
- python的一些基础知识
- ssm框架创建简单的DVD管理系统(四)spring的配置与mybatis的配置
- 复习1、变量,元组,字典,字典练习等
- 反转部分单向链表 Python 版
- Java 基础 —— 域
- GTX 950 内存太小,训练VGG6 Resource exhausted 错误
- C++——黑白棋(连续子个数)