1. 程式人生 > >spring學習筆記(21)程式設計式事務配置,service層概念引入

spring學習筆記(21)程式設計式事務配置,service層概念引入

訪問資料庫事務匯入

public <E> E add(Object object) {
    return (E) getSessionFactory().openSession().save(object);
}

通過直接開啟session而後儲存物件、查詢資料等操作,是沒有事務的。而如果我們的專案規模變大,業務邏輯日益複雜,我們在一個方法中進行大量的資料庫操作,而沒有事務管理的話,一旦中間哪一個操作環節出錯,後果是嚴重的。比如,一個使用者通過支付寶轉100塊到銀行賬戶,於是使用者的100塊先轉到了銀行,但這時資料庫異常中斷,銀行無法把100塊轉給使用者賬戶,這時事務又沒有回滾,那麼可能使用者的100塊就白白損失掉了。

在spring中,有多種方式可以進行我們的事務配置,比如我們可以直接修改上面的方法,加上事務:

public <E> E add(Object object) {
    Session session = getSessionFactory().openSession();
    Transaction tx = session.beginTransaction();
    E id = (E) session.save(object);
    tx.commit();
    return id; 
}

這樣,我們就能為我們的add方法加上簡單的事務了。

多資料庫操作事務配置——引入Service層

但這樣的話我們針對的只是dao層add這個方法,但在實際中,我們可能需要同時控制大量DAO的方法在同一個事務中,為此,我們可以建立service層來統一進行我們的業務邏輯處理。比如我們根據需求,需要先獲取使用者id,並修改使用者名稱稱,這裡設計兩個資料庫操作,但我們在同一個service類方法中完成。

程式設計式事務模板類:TransactionTemplate

概念

在下例中,我們依然使用程式設計式事務,spring為此專門提供了模板類TransactionTemplate來滿足我們的需求。TransactionTemplate是執行緒安全的,也即是說,我們可以在多個業務類中共享同一個TransactionTemplate例項進行事務管理。

常用屬性

TransactionTemplate有很多常用的屬性如:
1. isolationLevel:設定事務隔離級別
2. propagationBehavior:設定我們的事務傳播行為
3. readOnly:設定為只讀事務,即資料寫操作會失敗
4. timeout:設定連結過期時間,-1和預設為無超時限制
5. transactionManager:它是我們的IOC容器配置時的必要屬性,設定我們的事務管理物件。在本例中用到hibernate,為HibernateTransactionManager。

核心方法

TransactionTemplate類在呼叫時主要用到的方法為execute(TransactionCallback
action)。

有返回值的回撥介面

其中TransactionCallback為我們的回撥介面,它只有一個方法:
T doInTransaction(TransactionStatus status),這個方法內是有事務的。通常我們的資料庫查詢操作就在這個方法裡完成。

方法入參TransactionStatus介面

doInTransaction方法的唯一入參是TransactionStatus,它常用於檢視我們當前的事務狀態,它有兩個常用的方法:
1. createSavepoint():建立一個記錄點。
2. rollbackToSavepoint(savepoint):將事務回滾到特定記錄點,這樣從回滾處到記錄點範圍內所有的資料庫操作都會失效。

無返回值的介面TransactionCallback

另外,doInTransaction是有返回值的,如果我們不需要返回值,我們可以使用TransactionCallback介面的一個子類TransactionCallbackWithoutResult,它對應的抽象方法doInTransactionWithoutResult(TransactionStatus status)是沒有返回值的。

例項演示

下面開始我們的例項演示

1. service層配置

public class MyaseServiceImpl implements MyBaseService{
    private MyBaseDao myBaseDao;
    private TransactionTemplate transactionTemplate;

    @Override//測試方法
    public void queryUpdateUser(final Integer id,final String newName) {
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {

            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                User user = myBaseDao.queryUnique(User.class, id);//根據id獲取使用者
                System.out.println(user);
                user.setName(newName);//修改名稱
                myBaseDao.update(user);//更新資料庫
                System.out.println(user);
            }
        });
        /*下面的方法是由返回值的,在這裡我們假設為User。
        User user = transactionTemplate.execute(new TransactionCallback<User>() {

            @Override
            public User doInTransaction(TransactionStatus status) {
                User user = myBaseDao.queryUnique(User.class, id);
                System.out.println(user);
                user.setName(newName);
                myBaseDao.update(user);
                System.out.println(user);
                return user;
            }
        });
        */
    }

    public void setMyBaseDao(MyBaseDao myBaseDao) {//set屬性注入
        this.myBaseDao = myBaseDao;
    }

    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
        this.transactionTemplate = transactionTemplate;
    }
}

2. DAO層配置

對應的DAO層類和部分方法如下所示:

public class MyBaseDaoImpl implements MyBaseDao{
    private SessionFactory sessionFactory;

    private Session getCurrentSession (){//根據引數來選擇建立一個新的session還是返回當前執行緒的已有session
        return sessionFactory.getCurrentSession();
    }

    @Override
    public <E> E queryUnique(Class<E> clazz, Integer entityId) {//查詢唯一的物件
            return (E) getCurrentSession().get(clazz, entityId);
    }
    @Override
    public void update(Object object) {//更新物件
        getCurrentSession().update(object);
    }
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }
}

3. Spring容器配置Bean依賴關係

如果對於Hibernate不太理解,可以先不管我們的方法實現原理,只需要知道對應的方法實現了什麼功能即可。在這裡。接下來我們要配置我們的spring容器,主要完成Bean之間的依賴配置:

<bean id="myBaseDao" class="com.yc.dao.MyBaseDaoImpl" >
    <property name="sessionFactory"  ref="sessionFactory" />
</bean>
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
    <property name="transactionManager" ref="transactionManager" />
</bean>
<bean id="myBaseServiceImpl" class="com.yc.service.MyBaseServiceImpl" >
    <property name="myBaseDao" ref="myBaseDao" />
    <property name="transactionTemplate" ref="transactionTemplate" />
</bean>

關於資料來源和sessionFactory的配置例項如下:

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
            destroy-method="close"><!-- 設定為close使Spring容器關閉同時資料來源能夠正常關閉,以免造成連線洩露 --> 
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/yc" />
        <property name="username" value="yc" />
        <property name="password" value="yc" />
        <property name="defaultReadOnly" value="false" /><!-- 設定為只讀狀態,配置讀寫分離時,讀庫可以設定為true
        在連線池建立後,會初始化並維護一定數量的資料庫安連線,當請求過多時,資料庫會動態增加連線數,
        當請求過少時,連線池會減少連線數至一個最小空閒值 -->
        <property name="initialSize" value="5" /><!-- 在啟動連線池初始建立的資料庫連線,預設為0 -->
        <property name="maxActive" value="15" /><!-- 設定資料庫同一時間的最大活躍連線預設為8,負數表示不閒置 -->
        <property name="maxIdle" value="10"/><!-- 在連線池空閒時的最大連線數,超過的會被釋放,預設為8,負數表示不閒置 -->
        <property name="minIdle" value="2" /><!-- 空閒時的最小連線數,低於這個數量會建立新連線,預設為0 -->
        <property name="maxWait" value="10000" /><!-- 連線被用完時等待歸還的最大等待時間,單位毫秒,超出時間拋異常,預設為無限等待 -->
    </bean>

    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource">
            <ref bean="dataSource" />
        </property>
        <property name="hibernateProperties">
            <props>
                <!-- MySQL的方言 -->
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
                <prop key="javax.persistence.validation.mode">none</prop>
                <!-- 必要時在資料庫新建所有表格 -->
                <prop key="hibernate.hbm2ddl.auto">update</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="current_session_context_class">thread</prop>
                <!-- <prop key="hibernate.format_sql">true</prop> -->
            </props>
        </property>
        <property name="packagesToScan" value="com.yc.model" />
    </bean>

4. 測試方法和結果分析

配置完成後,就可以進行我們的測試了。在這裡,我用到了Junit測試元件

public class Test1 {
    @Test
    public void test(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:spring/spring-datasource.xml");
        MyBaseServiceImpl myBaseServiceImpl = (MyBaseServiceImpl) ac.getBean("myBaseServiceImpl");
        myBaseServiceImpl.queryUpdateUser(1, "newName");//在這裡呼叫我們的service層方法
    }
}

呼叫測試方法,我們會看到控制檯輸出如下相關資訊:

DEBUG: org.hibernate.engine.jdbc.internal.LogicalConnectionImpl - Obtaining JDBC connection
DEBUG: org.hibernate.engine.jdbc.internal.LogicalConnectionImpl - Obtained JDBC connection
DEBUG: org.hibernate.engine.transaction.spi.AbstractTransactionImpl - begin————————這裡我們的事務開始了
DEBUG: org.hibernate.loader.Loader - Loading entity: [com.yc.model.User#1]——————————讀取id為1的使用者
DEBUG: org.hibernate.loader.Loader - Done entity load//完成裝載工作
User [id=1, name=zenghao]——————獲得了我們的User資訊
User [id=1, name=newName]——————完成了修改操作
DEBUG: org.springframework.orm.hibernate4.HibernateTransactionManager - Initiating transaction commit//初始化資料庫提交
DEBUG: org.hibernate.engine.transaction.spi.AbstractTransactionImpl - committing————————這時候才完成了事務提交
觀察列印資訊,我們會發現我們像資料庫發出了兩次請求(分別為獲取和更新)但事務才提交了一次。說明這兩個請求在同一個事務中。這樣我們就能確保多個數據庫操作在同一個事務內完成,一旦中間出現異常,能立即回滾,取消前面的資料庫操作。
很多人都知道我們的mvc模式將後端業務分成了三層(DAO,service,controller),從這裡,我們也能略微看出DAO層和service層的功能職責了。DAO主要完成資料庫查詢的封裝,而Service層則呼叫DAO層的資料庫查詢方法來完成我們的業務邏輯處理

相關推薦

spring學習筆記(21)程式設計事務配置service概念引入

訪問資料庫事務匯入 public <E> E add(Object object) { return (E) getSessionFactory().openSession().save(object); } 通過直接開啟sess

spring學習筆記(22)聲明事務配置readOnly無效寫無異常

lin top post 處理 ast cannot pro ever 也不會 在上一節內容中。我們使用了編程式方法來配置事務,這種優點是我們對每一個方法的控制性非常強。比方我須要用到什麽事務,在什麽位置假設出現異常須要回滾等。能夠進行非常細粒度的配置。

spring學習筆記(23)基於tx/aop配置切面增強事務

在上一篇文章中,我們使用了宣告式事務來配置事務,使事務配置從service邏輯處理中解耦出來。但它還存在一些缺點: 1. 我們只針對方法名的特定進行攔截,但無法利用方法簽名的其它資訊定位,如修飾符、返

spring學習筆記(3)——bean配置細節註意

collect 1.5 之前 ice ble person name return 引用 1. 一個bean引用另外一個bean 當Person類中有一個屬性是Car,那麽該如何配置呢 person: package com.zj.spring; public class

spring 宣告事務配置丟擲runtimeException異常不回滾

預設spring只在發生未被捕獲的runtimeexcetpion時才回滾。 最容易解決的辦法:程式碼級控制:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); try{ ..

Spring學習筆記三: 通過註解配置Bean

一、在 classpath 中掃描元件 元件掃描(component scanning): Spring 能夠從 classpath 下自動掃描, 偵測和例項化具有特定註解的元件. 特定元件包括: @Component: 基本註解, 標識了一個受 S

Spring學習筆記(一):眼見為實先上一個簡單例子

概述 所謂眼見為實,Spring雖然是一個輕量級的框架,但涉及眾多的概念,理解起來並不容易,因此,先參考資料寫一個簡單的Demo,從中洞見Spring的大體工作流程,為後面深入學習做鋪墊。 本文的Demo很簡單:模擬圖書資料訪問服務,即向資料庫中新增圖書資訊,涉及IBoo

用Java+xml配置方式實現Spring資料事務程式設計事務

一、用Java配置的方式 1、實體類: Role  public class Role { private int id; private String roleName; private String note; @Override

Spring筆記(4) - Spring程式設計事務和宣告事務詳解

一.背景 事務管理對於企業應用而言至關重要。它保證了使用者的每一次操作都是可靠的,即便出現了異常的訪問情況,也不至於破壞後臺資料的完整性。就像銀行的自助取款機,通常都能正常為客戶服務,但是也難免遇到操作過程中機器突然出故障的情況,此時,事務就必須確保出故障前對賬戶的操作不生效,就像使用者剛才完全沒有使用過取

Spring 學習筆記(七)—— 切入點表達

service string 出現 targe || 參數 public 例如 語法   為了能夠靈活定義切入點位置,Spring AOP提供了多種切入點指示符。 execution———用來匹配執行方法的連接點   

Python學習筆記21(讀取配置文件)

文件 class strong color () for 新的 -i .config 1、基本的讀取操作 -read(filename) 直接讀取文件內容 -sections() 得到所有的sectio

Linux學習筆記_shell程式設計之環境變數配置檔案

shell程式設計之環境變數配置檔案 https://www.imooc.com/learn/361 簡介:本課程是《Tony老師聊shell》系列課程的第三篇,為你帶來常用的Linux環境變數配置檔案的使用。對環境變數配置檔案的功能進行了詳解, 然後又介紹了其他環境變數配置檔案,包括登

Spring 程式設計事務 宣告事務

程式設計式事務通用類: import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.datasource.DataSourceTransactionManager

第十講:10.spring對事物的支援-程式設計事務管理

轉賬業務 1,複製spring403-03 改名spring403:建立表結構,資料庫的引擎一定是InnoDB Create Table CREATE TABLE `t_account` (   

淺談spring事務管理的2種方式:程式設計事務管理和宣告事務管理;以及@Transactional(rollbackFor=Exception.class)註解用法

事務的概念,以及特性: 百度百科介紹: ->資料庫事務(Database Transaction) ,是指作為單個邏輯工作單元執行的一系列操作,要麼完全地執行,要麼完全地不執行。 事務處理可以確保除非事務性單元內的所有操作都成功完成,否則不會永久更新面向資料的資源。通過

使用Spring程式設計事務TransactionTemplate

使用場景 假如有一個專案設定了資料庫最大連線數為3,然後專案中提供了一個介面,介面中的一個方法會做以下邏輯處理:①首先在資料庫的某個表中查詢出一條記錄,②然後利用該條記錄的資料去調第三方的介面,然後第三方介面放回資料,③最後修改該條記錄的某個欄位的資料然後更新回資料庫。 假設有4個使

spring 程式設計事務spring事務一)

配置檔案 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org

8.spring事務管理(上):Spring的資料庫程式設計程式設計事務管理

Spring的資料庫程式設計  Spring框架提供了JDBC模板模式------>JdbcTemplate 簡化了開發,在開發中並不經常是使用 實際開發更多使用的是Hibernate和MyBatis 1).Spring JDBCp配置 如果使用Spring JDBC操作資料庫,要有

spring 原始碼學習筆記(二)事務管理

spring 事務管理會幫我們自動管理資料庫的事務,沒讀原始碼前覺得很神祕,讀了原始碼發現原理還是很簡單的。 本質上還是用的 jdbc 的事務管理。spring 在呼叫某個方法前,判斷是否需要事務,如果需要,則呼叫 con.setAutoCommit(false);//開

手寫Spring註解事務(利用AOP技術 + 註解 + Spring程式設計事務

1.參考下面的文章搭建一個無事務管理的SSM操作資料庫的框架       Spring 使用Druid資料來源 整合 Mybatis 2.AOP技術參考       AOP技術應用實現 3.第一步首先實現Sprin