(Spring)聲明式事務管理


在寫代碼之前我們需要了解一下什么是事務管理以及geCurrentSession跟openSession的區別? 

1.事務就是對一系列的數據庫操作(比如插入一條或多條數據)會進行統一的提交或回滾操作,如果插入成功,那么
  一起成功,如果在數據操作的過程中發生異常(則為失敗),也會回滾之前所有的操作。

2.數據庫數據出現問題開發中為了避免這種情況一般都會進行事務管理
3.在JDBC中是通過Connection對象進行事務管理的,默認是自動提交事務,可以手工將自動提交關閉,通過commit方法進行提交,rollback方法
  進行回滾,如果不提交,則數據不會真正的插入到數據庫中。

4.Hibernate中是通過Transaction進行事務管理,處理方法與JDBC中類似。
5.Spring中也有自己的事務管理機制,一般是使用TransactionMananger進行管理,可以通過Spring的注入來完成此功能。

getCurrentSession跟openSession的區別:
  1.getCurrentSession自動關閉會話對象,通過上下文來判斷是否需要創建session對象,如果我們沒有創建session對象,則會自動幫我們創建一個session對象,如果已有session對
    象,則繼續使用同一個對象,當我們方法不在使用的時候會自動的幫我們關閉。
  2.openSession手動關閉會話對象,手動創建session對象,每次打開的時候都會調用一個新的對象。需要調用close()方法來關閉session對象。
本例用的是Spring注入來完成的事務管理機制(還是以水果系統為例)在原來的基礎上進行修改:
先來到我們的FruitDaoImpl類將我們原來的代碼刪掉或是注釋掉
package fruit.dao;

import java.util.Date;
import java.util.List;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.query.Query;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import fruit.entity.Fruit;
//給我們的dao類定義類注解
//並給出非單例模式
@Repository
@Scope("prototype")public class FruitDaoImpl implements FruitDao{
    //給FruitDao定義一個sessionFactory的變量
    //給FruitDao類注入SessionFaction類
    @Autowired
    private SessionFactory sf;
    
    @SuppressWarnings({ "rawtypes", "unchecked" })
    @Override
    public List showAllFruit() {
        
        Session session  = sf.getCurrentSession();
        
        //Session session = sf.openSession();
        //開啟事務
        //session.getTransaction().begin();
        //定義hql查詢語句(form Fruit)Fruit指的是實體類的類名而不是表名。
        Query<Fruit> query = session.createQuery("from Fruit");
        
        List<Fruit> allFruitList  = query.getResultList();
        
        
        /*提交事務並關閉session對象
         * session.getTransaction().commit();
        session.close();*/
    
        return allFruitList;
    }

    @Override
    public String delSingleFruit(Integer id) {
        Fruit fruit1=new Fruit();
        fruit1.setFruitName("蘋果");
        fruit1.setFruitType("仁果類");
        fruit1.setListed(new Date());
        fruit1.setSeason("秋季");
        //System.out.println("來了");
        //Session session=sf.openSession();
        Session session  = sf.getCurrentSession();
        //定義hql刪除語句
        @SuppressWarnings("unchecked")
        Query<Fruit> query= session.createQuery("from Fruit where id=:myid");
        
        query.setParameter("myid",id);
        List<Fruit> deleteList=query.getResultList();
        
        //判斷刪除的數據是否存在(存在則為1)存在則刪除,如果為0則什么也不做
        if(deleteList.size()==1){
            Fruit fruit=deleteList.get(0);
            session.delete(fruit);
            session.save(fruit1);
            //在控制台打印輸出
            System.out.println("刪除對象:"+fruit.getFruitName()+ " Id:"+fruit.getFruitType()+fruit.getListed()+fruit.getSeason());
            //這邊用到了事物管理(把這段代碼從dao刪除掉)
            /*session.getTransaction().begin();
            session.getTransaction().commit();
            session.close();*/
        }
        //session.close();
        return "deleteOK";
                
    }
}
applicationContext.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:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="    
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd  
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd  
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
            http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.2.xsd  
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd">

    <!-- 原理:自動注入processor解析器,用來解析注解 -->
    <!-- <context:annotation-config/> -->
    <context:component-scan base-package="fruit" />
    <!-- 引入外部屬性文件 -->
    <context:property-placeholder location="classpath:jdbc.properties" />

    <bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
        <!-- 注入連接池,包含了數據庫用戶名,密碼等等信息 -->
        <property name="dataSource" ref="myDataSource" />

        <!-- 配置Hibernate的其他的屬性 -->
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.format_sql">true</prop>
                <prop key="hibernate.connection.autocommit">false</prop>
                <!-- 開機自動生成表 -->
                <prop key="hibernate.hbm2ddl.auto">update</prop>
            </props>
        </property>
        
        <property name="mappingResources">
            <list>
                <value>fruit/entity/Fruit.hbm.xml</value>
            </list>
        </property>

    </bean>

    <bean id="myDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}" />
        <property name="jdbcUrl" value="${jdbc.url}" />
        <property name="user" value="${jdbc.user}" />
        <property name="password" value="${jdbc.password}" />
        <!-- 每300秒檢查所有連接池中的空閑連接 -->
        <property name="idleConnectionTestPeriod" value="300"></property>
        <!-- 最大空閑時間,900秒內未使用則連接被丟棄。若為0則永不丟棄 -->
        <property name="maxIdleTime" value="900"></property>
        <!-- 最大連接數 -->
        <property name="maxPoolSize" value="2"></property>

    </bean>
    <!-- 配置事務 -->
    <!-- 配置hibernate的局部事務管理,使用hibernateTransactionManager類 -->
    
    <bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
        <!-- 配置HibernateTransactionManager時需要注入的sessionFactory對象的引用 -->
        <property name="sessionFactory" ref="sessionFactory"></property>
    </bean>
    <!-- 配置通知Spring容器對注解@Transaction的Bean處理 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>
    
    <!-- 
    配置事務增強處理,指定事務管理器 
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="add*" propagation="REQUIRED" />
            <tx:method name="del*" propagation="REQUIRED" />
            <tx:method name="mod*" propagation="REQUIRED" />
            必須要配置開啟事務,不然getCurrentSession()會獲取不到
            <tx:method name="*" propagation="REQUIRED" read-only="true" />
        </tx:attributes>
    </tx:advice>

    <aop:config>
        配置一個切入點,匹配fruit.dao下所有類執行的方法
        <aop:pointcut id="interceptorPointCuts" expression="execution(* fruit.dao.*.*(..))" />
        指定在poincut切入點應用txAdvice事務增強處理
        <aop:advisor advice-ref="txAdvice" pointcut-ref="interceptorPointCuts" />
    </aop:config>  -->

</beans>

當我們添加這一段代碼的時候,需要去到我們的service,給我們的FruitServiceImpl添加注解@Transaction:

來到ServiceImpl類:
package fruit.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import fruit.dao.FruitDao;
import fruit.entity.Fruit;
//給我們的action類定義類注解
//並給出非單例模式

@Transactional(readOnly=true)
@Service
@Scope("prototype")
public class FruitServiceImpl implements FruitService{
    //定義一個dao的變量
    //給FruitService類注入FruitDao類
    @Autowired
    private FruitDao fd;
    
    @Override
    public List showAllFruit() {
        //在service這邊調用dao的showAllFruit方法
        List<Fruit> allFruitList=fd.showAllFruit();
        return allFruitList;
    }

    @Override
    public String delSingleFruit(Integer id) {
        //當可以刪除時,調用DAO直接刪除
        return fd.delSingleFruit(id);
    }
    
}

編譯結果如下:

來到這邊,說明我們的程序編譯是正常的,jsp頁面如下:

 最后再寫一下Spring基於aop/tx配置的聲明式事務管理跟@Transactional注解的區別:

  1.aop/tx配置聲明式事務管理耦合性低,可讀性低,表達較詳細,靈活性也高。(aop/tx配置聲明式事務管理代碼如下:)

<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
        <!-- 配置HibernateTransactionManager時需要注入的sessionFactory對象的引用 -->
        <property name="sessionFactory" ref="sessionFactory"></property>
    </bean>
    
    <!--配置事務增強處理,指定事務管理器 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="add*" propagation="REQUIRED" />
            <tx:method name="del*" propagation="REQUIRED" />
            <tx:method name="mod*" propagation="REQUIRED" />
            <!--必須要配置開啟事務,不然getCurrentSession()會獲取不到-->
            <tx:method name="*" propagation="REQUIRED" read-only="true" />
        </tx:attributes>
    </tx:advice>

    <aop:config>
        <!--配置一個切入點,匹配fruit.dao下所有類執行的方法-->
        <aop:pointcut id="interceptorPointCuts" expression="execution(* fruit.dao.*.*(..))" />
        <!--指定在poincut切入點應用txAdvice事務增強處理-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="interceptorPointCuts" />
    </aop:config>  

 

  2.@Transactional注解可讀性較高,內容分散不利於統一的管理和維護,耦合性較高。(.@Transactional注解代碼如下:)

  

  因為service要調用dao類的方法,所以在service類添加@Transactional注解。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM