Spring框架簡介⑩

Milky-way發表於2018-08-09

事務的特性(ACID):

A: Atomic 原子性

表示組成一個事務的多個對資料庫的操作為一個不可分割的單元, 只有所有的操作都成功才算成功, 整個事務才會提交, 其中任何一個操作失敗了都會導致整個操作失敗, 事務則會回滾

C: Consistency 一致性

事務操作成功後, 資料庫所處的狀態和業務規則是一致(不變)的, 如果A賬戶給B賬戶匯100, 則A賬戶要減去100, B賬戶要加上100 兩個賬戶的總額是不變的

I: Isolation 隔離性

在多個對資料庫操作相同的資料併發時, 不同的事務有自己的資料空間, 事務與事務之間不受干擾(不是絕對的), 干擾程度受資料庫或者操作事務的隔離級別來決定, 隔離級別越高, 干擾就越低, 資料的一致性就越好, 而併發性則越差.

D: Durability 永續性

一旦事務提交成功, 資料就被持久化到資料庫, 不可以回滾, 重啟/關機都不會丟失資料了.

 

原子性由Spring的傳播特性來控制, 一致性和隔離性由資料庫的隔離級別來控制.

 

事務控制

配置檔案的配置(第一標頭檔案配置, 第二事務管理器的配置, 第三開啟事務註解驅動)

這是我的專案結構圖:

配置檔案:

<?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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-3.2.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
           http://www.springframework.org/schema/tx
           http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">

    <!-- 配置屬性檔案的位置 -->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    
    <!-- 配置資料來源 -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
    
        <!-- 採用${xxx}的形式讀取屬性檔案中對應key的內容 -->
        <property name="driverClassName" value="${driverClassName}"></property>
        <property name="url" value="${url}"></property>
        <property name="username" value="${uname}"></property>
        <property name="password" value="${pword}"></property>
        <!-- 初始化連線數 -->
        <property name="initialSize" value="${initialSize}"></property>
        <!-- 最大連線數 -->
        <property name="maxActive" value="${maxActive}"></property>
        <!-- 最大空閒連線數 -->
        <property name="maxIdle" value="${maxIdle}"></property>
        <!-- 最小空閒連線數 -->
        <property name="minIdle" value="${minIdle}"></property>
    </bean>
    
    <bean id="orderDao" class="com.rl.spring.dao.impl.OrderDaoImpl">
        <property name="ds" ref="dataSource"></property>
    </bean>
    <bean id="detailDao" class="com.rl.spring.dao.impl.DetailDaoImpl">
        <property name="ds" ref="dataSource"></property>
    </bean>
    
    <bean id="orderService" class="com.rl.spring.service.impl.OrderServiceImpl">
        <property name="orderDao" ref="orderDao"></property>
    </bean>
    
    <bean id="detailService" class="com.rl.spring.service.impl.DetailServiceImpl">
        <property name="orderDao" ref="orderDao"></property>
        <property name="detailDao" ref="detailDao"></property>
    </bean>
    
    <!-- 定義事務管理器 -->    
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!-- 配置事務管理的註解驅動 -->
    <tx:annotation-driven transaction-manager="txManager"/>
</beans>

下面只貼上實現類的程式碼

OrderDaoImpl:

package com.rl.spring.dao.impl;

import javax.sql.DataSource;

import org.springframework.jdbc.core.JdbcTemplate;

import com.rl.spring.dao.OrderDao;
import com.rl.spring.model.Order;

public class OrderDaoImpl implements OrderDao {

    private DataSource ds;
    
    private JdbcTemplate jt;
    
    public void setDs(DataSource ds) {
        this.ds = ds;
        this.jt = new JdbcTemplate(ds);
    }

    @Override
    public int saveOrder(Order order) {
        String sql = "insert into t_order values(null, ?)";
        jt.update(sql, new Object[] {order.getTotalPrice()});
        return jt.queryForInt("SELECT LAST_INSERT_ID()");//該行程式碼的作用是讓資料庫返回主鍵
    }
}

DetailDaoImpl

package com.rl.spring.dao.impl;

import javax.sql.DataSource;

import org.springframework.jdbc.core.JdbcTemplate;

import com.rl.spring.dao.DetailDao;
import com.rl.spring.model.Detail;
import com.rl.spring.model.Order;

public class DetailDaoImpl implements DetailDao {

    private DataSource ds;
    
    private JdbcTemplate jt;
    
    public void setDs(DataSource ds) {
        this.ds = ds;
        this.jt = new JdbcTemplate(ds);
    }

    @Override
    public void saveDetail(Detail detail) {
        String sql = "insert into t_detail values(null, ?, ?, ?)";
        jt.update(sql, new Object[] {detail.getItemName(), detail.getQuantity(), detail.getOrderId()});
    }
}

OrderServiceImpl(較簡單, 省略貼上上來)

DetailServiceImpl:

package com.rl.spring.service.impl;

import org.springframework.transaction.annotation.Transactional;

import com.rl.spring.dao.DetailDao;
import com.rl.spring.dao.OrderDao;
import com.rl.spring.model.Detail;
import com.rl.spring.model.Order;
import com.rl.spring.service.DetailService;

public class DetailServiceImpl implements DetailService {

    DetailDao detailDao;
    
    OrderDao orderDao;
    
    public void setOrderDao(OrderDao orderDao) {
        this.orderDao = orderDao;
    }

    public void setDetailDao(DetailDao detailDao) {
        this.detailDao = detailDao;
    }

    @Transactional//事務管理註解
    @Override
    public void saveOrderAndDetail(Order order, Detail detail) {
        Integer orderId = orderDao.saveOrder(order);
        detail.setOrderId(orderId);
        int i = 1/0;//設定執行期異常, 由於配置了Transactional註解, 所以事務會全部回滾
        detailDao.saveDetail(detail);
    }
}

測試程式碼:

package com.rl.spring.test;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.rl.spring.model.Detail;
import com.rl.spring.model.Order;
import com.rl.spring.service.DetailService;
import com.rl.spring.service.OrderService;

@RunWith(value=SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations= {"classpath:ApplicationContext.xml"})
public class TestSpring {
	
    @Autowired
    OrderService orderService;
    
    @Autowired
    DetailService detailService;
    
    @Test
    public void test() {
        Order order = new Order();
        order.setTotalPrice(100);
        
        Detail detail = new Detail();
        detail.setItemName("HUAWEI");
        detail.setQuantity(1);
        detailService.saveOrderAndDetail(order, detail);
    }
}

@Transactional註解的本質是事務的傳播特性, 下面詳講事務的傳播特性

Spring的傳播特性

Required傳播特性(80%以上使用該傳播特性)

業務方法需要在一個事務中執行, 如果一個方法已經處在一個事務中那麼就加入到這個事務中, 否則就會建立一個新事務

如下圖:

傳播特性可配置:

Never傳播特性(極少使用):

指定的業務方法絕對不能在事務中執行, 如果在事務中執行了, 則會拋異常, 只有業務方法沒有事務才會正常執行.

MANDATORY傳播特性:

與Never相反, 只能在一個已經存在的事務中執行, 不能自己發起事務, 如果業務方法沒有事務的情況下, 則拋異常

SUPPORTS傳播特性:

如果業務方法已經在某個事務中被呼叫, 則業務方法就成為事務的一部分, 如果業務方法沒有在某個事務中被呼叫, SUPPORTS也支援該業務方法的執行(一句話, 開事務我就用事務, 不開事務我就不用事務, 但都可以執行)

NOT_SUPPORTED傳播特性:

永遠不支援事務(在有事務中該事務會被掛起)

REQUIRES_NEW傳播特性:

永遠使用自己建立的事務(如果已經存在事務則掛起它, 自己新建立), 假如自己new出來的這個事務回滾了, 是不會影響到另一個被掛起的事務的.

NESTED傳播特性:

主要區分於內外部事務, 如果內部事務做回滾, 是不會影響到外部事務的(因為NESTED會設定一個事務儲存點, 回滾到該儲存點後繼續執行外部事務); 而如果是外部事務做回滾, 該內外部事務全部回滾.

這點跟REQUIRES_NEW有區別, REQUIRES_NEW是不管是否內外部事務都互不影響.

 

====================================================

Spring系列暫時寫到這裡, 後續抽時間更一下事務的隔離級別, 接下來會更Hibernate系列.

 

相關文章