JavaEE學習日誌(九十二): spring事務控制

JavaEE學習日誌持續更新----> 必看!JavaEE學習路線(文章總彙)

Spring

spring事務控制的api

  • org.springframework.orm.hibernate5.HibernateTransactionManager: 在hibernate環境下使用
  • org.springframework.jdbc.datasource.DataSourceTransactionManager: 在jdbcTemplate,mybatis(ibatis)環境下使用

事務的特性

  • 原子性:不可分割
  • 隔離性:事務之間的隔離級別
  • 一致性:要麼全部完成,要麼全部不完成
  • 持久性:一旦提交,持久化到數據庫中

事務的隔離級別

  • 讀未提交:read uncommited
    產生的問題:髒讀、幻讀、不可重複讀
  • 讀已提交:read commited
    產生的問題:幻讀,不可重複讀
    解決的問題:髒讀
  • 重複讀:repeatable read
    產生的問題:幻讀
    解決的問題:髒讀,不可重複讀
  • 串行化(序列化):serializable
    解決的問題:所有問題,隔離級別最高,效率最低

詳細看jdbc中的事務部分

數據庫支持的隔離級別
mysql:全支持,默認是repeatable read
oracle:read commited、serializable、read only(只讀) 默認是read commited

事務的傳播

  • REQUIRED:必要的,如果沒有事務,則新建事務;如果有事務,則加入事務中。spring中指定爲默認值
    增刪改

  • SUPPORTS:支持的,如果沒有事務,非事務執行;如果有事務,加入事務
    查詢

瞭解其他事務傳播
MANDATORY:可以使用當前事務,如果沒有事務,則拋出異常
REQUERS_NEW:新建一個事務,如果存在事務,則掛起事務
NOT_SUPPORTED:必須非事務執行,如果有事務則掛起
NEVER:非事務執行,如果存在事務,拋出異常
NESTED:有事務,嵌套執行;沒有事務,執行REQUIRED

是否爲只讀的事務

  • 如果是查詢,則爲只讀的事務,readOnly=true
  • 如果是增刪改,則爲非只讀的事務,readOnly=false

spring聲明式xml事務管理(推薦)

引入spring的事務管理jar包

<!--spring的事務管理jar包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

一、創建事務管理器對象

<!--創建事務管理器對象-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入數據源屬性:事務存在連接中,連接存在連接池中-->
        <property name="dataSource" ref="springDataSource"></property>
    </bean>

二、事務的增強:過濾方法是否需要攔截
有四個屬性

  • isolation:隔離級別,一般選擇默認
  • propagation:傳播的行爲
  • read-only:是否爲只讀的事務,增刪改選擇非只讀;查詢選擇只讀
  • timeout:超時時間,單位爲s,-1時爲永不超時

注意*:通配符配置,如find*,只要以find開頭即可

<tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--方法的過濾-->
        <tx:attributes>
            <!--指定需要攔截的方法
                isolation:隔離級別,一般選擇默認
                propagation:傳播的行爲
                read-only:是否爲只讀的事務,增刪改選擇非只讀;查詢選擇只讀
                timeout:超時時間,單位爲s,-1時爲永不超時

                *:通配符配置,如find*,只要以find開頭即可
            -->
            <!--增刪改-->
            <tx:method name="insert*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>
            <tx:method name="add*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>
            <tx:method name="update*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>
            <tx:method name="del*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>
            <tx:method name="delete*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>
            <tx:method name="save*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>

            <!--查詢-->
            <tx:method name="find*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true" timeout="-1"/>
            <tx:method name="select*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true" timeout="-1"/>
            <tx:method name="query*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true" timeout="-1"/>
            <tx:method name="get*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true" timeout="-1"/>

        </tx:attributes>
    </tx:advice>

可以將默認的屬性刪除掉

<tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--方法的過濾-->
        <tx:attributes>
            <!--指定需要攔截的方法
                isolation:隔離級別,一般選擇默認
                propagation:傳播的行爲
                read-only:是否爲只讀的事務,增刪改選擇非只讀;查詢選擇只讀
                timeout:超時時間,單位爲s,-1時爲永不超時

                *:通配符配置,如find*,只要以find開頭即可
            -->
            <!--增刪改-->
            <tx:method name="insert*" />
            <tx:method name="add*" />
            <tx:method name="update*" />
            <tx:method name="del*" />
            <tx:method name="delete*"/>
            <tx:method name="save*" />

            <!--查詢-->
            <tx:method name="find*"  propagation="SUPPORTS" read-only="true" />
            <tx:method name="select*"  propagation="SUPPORTS" read-only="true" />
            <tx:method name="query*"  propagation="SUPPORTS" read-only="true" />
            <tx:method name="get*"  propagation="SUPPORTS" read-only="true" />

        </tx:attributes>
    </tx:advice>

再簡化:可以將查詢或增刪改的其中一部分,用*來代替

<tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--方法的過濾-->
        <tx:attributes>
            <!--查詢-->
            <tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method>
            <tx:method name="select*" propagation="SUPPORTS" read-only="true"></tx:method>
            <tx:method name="query*" propagation="SUPPORTS" read-only="true"></tx:method>
            <tx:method name="get*" propagation="SUPPORTS" read-only="true"></tx:method>

            <!--其他配置事務,增刪改-->
            <tx:method name="*"></tx:method>
        </tx:attributes>
    </tx:advice>

三、配置切面

  • advice-ref:通知關聯對象
  • pointcut:切入點
<aop:config>
        <!--切面配置
            advice-ref:通知關聯對象
            pointcut:切入點
        -->
        <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.itheima.service..*.*(..))"></aop:advisor>
    </aop:config>

四、測試
業務層

@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    AccountDao accountDao;
    @Override
    public void transfer(String fromName, String toName, Float money) {
        //查詢轉賬的賬戶
        Account fromAccount = accountDao.findByName(fromName);
        //查詢接收的賬戶
        Account toAccount = accountDao.findByName(toName);
        //開始轉賬
        fromAccount.setMoney(fromAccount.getMoney()-money);
        toAccount.setMoney(toAccount.getMoney()+money);
        //持久化到數據庫
        accountDao.update(fromAccount);
        System.out.println(0/0);
        accountDao.update(toAccount);
    }
}

測試類

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class testJdbcTemplate {
    @Autowired
    AccountService accountService;
    @Test
    public void test(){
        accountService.transfer("aaa","ccc",500f);

    }

}

註解的事務管理

引入依賴和上面完全相同
一、創建事務管理器對象

<!--創建事務管理器對象-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入數據源屬性:事務存在連接中,連接存在連接池中-->
        <property name="dataSource" ref="springDataSource"></property>
    </bean>

二、開啓事務的註解管理和aop的自動代理

<!--開啓事務的註解管理-->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
    <!--開啓aop的註解,自動代理-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

三、標記註解

1、在業務層的類上標記註解@Transactional

會出現一個問題:類中所有的方法都會使用事務管理

2、刪除在類上的註解在方法上標記註解@Transactional:只有該方法按照事務執行

四、配置屬性

@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, readOnly = false, timeout = -1)
    public void transfer(String fromName, String toName, Float money) {
        //查詢轉賬的賬戶
        Account fromAccount = accountDao.findByName(fromName);
        //查詢接收的賬戶
        Account toAccount = accountDao.findByName(toName);
        //開始轉賬
        fromAccount.setMoney(fromAccount.getMoney()-money);
        toAccount.setMoney(toAccount.getMoney()+money);
        //持久化到數據庫
        accountDao.update(fromAccount);
        System.out.println(0/0);
        accountDao.update(toAccount);
    }

也可以省略

@Transactional()
    public void transfer(String fromName, String toName, Float money) {
        //查詢轉賬的賬戶
        Account fromAccount = accountDao.findByName(fromName);
        //查詢接收的賬戶
        Account toAccount = accountDao.findByName(toName);
        //開始轉賬
        fromAccount.setMoney(fromAccount.getMoney()-money);
        toAccount.setMoney(toAccount.getMoney()+money);
        //持久化到數據庫
        accountDao.update(fromAccount);
        System.out.println(0/0);
        accountDao.update(toAccount);
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章