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 總金額)