Spring 使用注解对事务控制详解与实例
1.什么是事务
一荣俱荣,一损俱损,很多复杂的操作我们可以把它看成是一个整体,要么同时成功,要么同时失败。
事务的四个特征ACID:
原子性(Atomic):表示组成一个事务的多个数据库的操作的不可分割的单元,只有所有的操作成功才算成功,整个事务提交,其中任何一个操作失败了都是导致整个所有操作失败,事务会回滚。
一致性(Consistentcy):事务操作成功后,数据库所处的状态和业务规则一致。如果A账户给B账户汇100,A账户减去100,B加上100,两个账户的总额是不变的。
隔离性(islation):在多个数据库的操作相同的数据并发时,不同的事务有自己的数据空间,事务与事务之间不受干扰(不是绝对的)。干扰程度受数据库或者操作事务的隔离级别来决定,隔离级别越高,干扰就越低,数据的一致性越好,并发性就越差。
持久性(Druability):一旦事务提交成功,数据就被持久化到数据库,不可以回滚。
2.spring使用注解对事务的控制
Spring 事务管理有两种方式:编程式事务管理、声明式事务管理
编程式事务管理通过TransactionTemplate手动管理事务,在实际应用中很少使用,我们来重点学习声明式事务管理
声明式事务管理有三种实现方式:基于TransactionProxyFactoryBean的方式、基于AspectJ的XML方式、基于注解的方式
1. 新建一个java工程——导入spring 和事务管理所需要的jar ——然后选择全部jar 右键Build path ——最后如图 (我的java版本jre是1.8) Spring事务需要的jar
2.在src目录下新建5个包 如图
dao :数据连接层,主要用于存放对数据进行操作的类
model:模型层,主要用于存放实体类
service:服务层,主要用于对数据连接层进行操作的一些服务类。
util:工具类,主要用于存储工具方法
test:用于测试类
3.在dao数据连接层,建立里与数据库操作的类与对应的接口 (Order 订单 ,detail订单明细)
3.1 定义detailDao接口 代码如下:
1 package com.spring.dao; 2 3 import com.spring.model.Detail; 4 import com.spring.model.Order; 5 6 public interface detailDao { 7 //保存订单明细 8 public void saveDetail(Detail detail); 9 10 11 12 }
3.2 定义OrderDao接口 代码如下:
1 package com.spring.dao; 2 3 import com.spring.model.Order; 4 5 public interface OrderDao { 6 //保存订单 7 public void saveOrder(Order order); 8 9 10 11 }
3.3 实现以上接口
detailDaoImp 代码如下:
1 package com.spring.dao; 2 3 import javax.sql.DataSource; 4 5 import org.springframework.jdbc.core.JdbcTemplate; 6 7 import com.spring.model.Detail; 8 import com.spring.model.Order; 9 10 public class detailDaoImp implements detailDao { 11 12 private DataSource dataSource; 13 private JdbcTemplate jdbcTemplate; 14 15 public void setDataSource(DataSource dataSource) { 16 this.dataSource = dataSource; 17 this.jdbcTemplate = new JdbcTemplate(dataSource); 18 } 19 //保存订单明细 20 public void saveDetail(Detail detail) { 21 String SQL = "insert into t_detail values(null,?,?,?))"; 22 jdbcTemplate.update(SQL, new Object[] { detail.getItemName(),detail.getQuantity(),detail.getOrderId() }); 23 System.out.println("Insert detail success!"); 24 } 25 26 }
OrderDaoImp代码如下:
package com.spring.dao; import javax.sql.DataSource; import org.springframework.jdbc.core.JdbcTemplate; import com.spring.model.Order; public class OrderDaoImp implements OrderDao { private DataSource dataSource; private JdbcTemplate jdbcTemplate; public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; this.jdbcTemplate = new JdbcTemplate(dataSource); } //插入订单 public void saveOrder(Order order) { String SQL = "insert into t_order values (null,?)"; System.out.println(SQL); System.out.println(order.getTotal_price()); jdbcTemplate.update(SQL, new Object[]{order.getTotal_price()}); System.out.println("Insert order success!"); } }
4.在model层中新建两个实体类 (Order订单类,detail订单明细类)
Order订单类代码如下:
1 package com.spring.model; 2 3 /* 4 * 订单表 5 */ 6 public class Order { 7 //订单编号 8 private Integer order_id; 9 //订单总金额 10 private Integer total_price; 11 public Integer getOrder_id() { 12 return order_id; 13 } 14 public void setOrder_id(Integer order_id) { 15 this.order_id = order_id; 16 } 17 public Integer getTotal_price() { 18 return total_price; 19 } 20 public void setTotal_price(Integer total_price) { 21 this.total_price = total_price; 22 } 23 }
detail订单明细类代码如下:
1 package com.spring.model; 2 3 /** 4 * 商品明细表 5 * @author Administrator 6 * 7 */ 8 public class Detail { 9 //ID 10 private Integer detailId; 11 //外键 暂时可以忽略 12 private Integer orderId; 13 public Integer getDetailId() { 14 return detailId; 15 } 16 public void setDetailId(Integer detailId) { 17 this.detailId = detailId; 18 } 19 public Integer getOrderId() { 20 return orderId; 21 } 22 public void setOrderId(Integer orderId) { 23 this.orderId = orderId; 24 } 25 public Integer getQuantity() { 26 return quantity; 27 } 28 public void setQuantity(Integer quantity) { 29 this.quantity = quantity; 30 } 31 public String getItemName() { 32 return itemName; 33 } 34 public void setItemName(String itemName) { 35 this.itemName = itemName; 36 } 37 //商品数量 38 private Integer quantity; 39 //商品数量 40 private String itemName; 41 42 }
5.在service中定义一个接口并且实现它
OrderService代码如下:
1 package com.spring.service; 2 3 import com.spring.model.Detail; 4 import com.spring.model.Order; 5 6 public interface OrderService { 7 //插入订单和订单明细 8 public void saveOrderAndDetail(Order order,Detail detail); 9 }
OrderServiceImp代码如下:
1 package com.spring.service; 2 3 import org.springframework.transaction.annotation.Transactional; 4 5 import com.spring.dao.OrderDaoImp; 6 import com.spring.dao.detailDaoImp; 7 import com.spring.model.Detail; 8 import com.spring.model.Order; 9 10 /** 11 * 服务层 使用注解来实现事务管理 12 * 13 * @author Administrator 14 * 15 */ 16 17 public class OrderServiceImp implements OrderService { 18 // 注入两个对象 19 public OrderDaoImp OrderDaoImp; 20 21 public detailDaoImp detailDaoImp; 22 23 @Transactional 24 @Override 25 // 如果加上@Transaction时,方法执行有异常,整个事务数据都会回滚,数据库中不会存在有数据。 26 public void saveOrderAndDetail(Order order, Detail detail) { 27 OrderDaoImp.saveOrder(order); 28 /** 29 * 如果不加@transaction时有异常存在,OrderDaoImp.saveOrder(order)方法将会执行。 30 * 但detailDaoImp.saveDetail(detail)不会执行 31 **/ 32 int i = 100 / 0; 33 detailDaoImp.saveDetail(detail); 34 35 } 36 37 public OrderDaoImp getOrderDaoImp() { 38 return OrderDaoImp; 39 } 40 41 public void setOrderDaoImp(OrderDaoImp orderDaoImp) { 42 OrderDaoImp = orderDaoImp; 43 } 44 45 public detailDaoImp getDetailDaoImp() { 46 return detailDaoImp; 47 } 48 49 public void setDetailDaoImp(detailDaoImp detailDaoImp) { 50 this.detailDaoImp = detailDaoImp; 51 } 52 53 }
6.在util包中定义一个通知方法类
1 package com.spring.util; 2 3 public class SeizedAdvice { 4 /** 5 * 定义通知 6 */ 7 8 public void beforeAdvice(){ 9 System.out.println("——————我是前置通知————————"); 10 11 } 12 public void afterAdvice(){ 13 System.out.println("------我是后置通知-------"); 14 15 } 16 public void afterReturningAdvice(Object object){ 17 System.out.println("————————我是返回后通知——————————"+object.toString()); 18 19 } 20 public void afterThrowingAdvice(IllegalAccessError illegalAccessError){ 21 System.out.println("--------我是异常返回异常返回通知---------"+illegalAccessError.toString()); 22 23 24 } 25 26 27 28 }
7.在test中定义个测试类,用于测试
1 package com.test; 2 3 import org.springframework.context.ApplicationContext; 4 import org.springframework.context.support.ClassPathXmlApplicationContext; 5 6 import com.spring.model.Detail; 7 import com.spring.model.Order; 8 import com.spring.service.OrderServiceImp; 9 10 public class Test { 11 12 public static void main(String[] args) { 13 ApplicationContext applicationContext=new ClassPathXmlApplicationContext("bean.xml"); 14 OrderServiceImp imp=(OrderServiceImp)applicationContext.getBean("OrderServiceImp"); 15 16 Order order=new Order(); 17 order.setTotal_price(699); 18 Detail detail=new Detail(); 19 detail.setItemName("牙刷"); 20 detail.setQuantity(3); 21 detail.setOrderId(4); 22 imp.saveOrderAndDetail(order,detail); 23 24 } 25 }
8.最后就是我们的配置文件和数据库中的两张表
Spring bean.xml代码如下:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" 4 xmlns:aop="http://www.springframework.org/schema/aop" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans 6 http://www.springframework.org/schema/beans/spring-beans-4.3.xsd 7 http://www.springframework.org/schema/tx 8 http://www.springframework.org/schema/tx/spring-tx-4.3.xsd 9 http://www.springframework.org/schema/aop 10 http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> 11 <!-- 定义数据源 --> 12 <bean id="dataSource" 13 class="org.springframework.jdbc.datasource.DriverManagerDataSource"> 14 <property name="driverClassName" value="com.mysql.jdbc.Driver" /> 15 <property name="url" value="jdbc:mysql://localhost:3306/jerry" /> 16 <property name="username" value="root" /> 17 <property name="password" value="1234" /> 18 </bean> 19 20 21 22 <!-- 事务管理方式注解驱动 --> 23 <tx:annotation-driven transaction-manager="transactionManager" /> 24 25 <!-- 配置数据 proxy-target-class属性值决定是基于接口的还是基于类的代理被创建。 26 如果proxy-target-class 属性值被设置为true,那么基于类的代理将起作用--> 27 <aop:config proxy-target-class="true"> 28 <!-- 切 面--> 29 <aop:aspect id="myAspect" ref="SeizedAdvice"> 30 <!-- 切 点--> 31 <aop:pointcut expression="execution(* com.spring.service.OrderServiceImp.*(..))" 32 id="myPointCut" /> 33 <aop:before pointcut-ref="myPointCut" method="beforeAdvice" /> 34 <aop:after pointcut-ref="myPointCut" method="afterAdvice" /> 35 </aop:aspect> 36 </aop:config> 37 38 39 <!-- 定义事务管理器 --> 40 <bean id="transactionManager" 41 class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 42 <property name="dataSource" ref="dataSource" /> 43 </bean> 44 45 <!-- 定义我们所需的bean --> 46 <bean id="SeizedAdvice" class="com.spring.util.SeizedAdvice"></bean> 47 48 <bean id="OrderServiceImp" class="com.spring.service.OrderServiceImp"> 49 <property name="detailDaoImp" ref="detailDaoImp"></property> 50 <property name="OrderDaoImp" ref="OrderDaoImp"></property> 51 </bean> 52 53 <bean id="detailDaoImp" class="com.spring.dao.detailDaoImp"> 54 <property name="dataSource" ref="dataSource" /> 55 </bean> 56 57 <bean id="OrderDaoImp" class="com.spring.dao.OrderDaoImp"> 58 <property name="dataSource" ref="dataSource" /> 59 </bean> 60 61 </beans>
9. 数据库设计
detail(订单明细表 detail_id 订单明细ID,item_name 商品名称,quantity 订单数量 ,order_id 外键 暂时不用管这个属性)
Order(订单表,order_id 订单id,total_price 总金额)