iT邦幫忙

2022 iThome 鐵人賽

DAY 17
0
Software Development

這些年,我們早該學會的Spring Framework系列 第 28

Day28 - Declarative transaction management (4)

  • 分享至 

  • xImage
  •  

Review

昨日我們講解了@Transactional的屬性noRollbackFor(rollbackFor)能讓原本Unchecked Exception(Checked Exception)能達到transaction的rollback(no rollback)

今日我們將介紹@Transactional當中另一個重要的屬性Isolation

Transaction Concurrency Problems

在transaction concurrent的時候若發生一個在讀取一個在修改就會產生讀取上的問題,因此需要將concurrent的transaction隔離開來。那麼transaction concurrent會產生什麼樣的問題呢

1.髒讀(Dirty read)

Transaction1可以讀到Transaction2未提交的數據
[1]TX01 將某條balance從100修改為1000。
[2]TX02 讀取Tx01更新後的數值1000。
[3]TX01 rollback,balance回復到100。
[4]TX02 讀取到的1000為無效值。

2.不可重複讀(Unrepeatable read)

Transaction1重複讀取同一數據會得到不一致的結果
[1]TX01 讀取balance為1000
[2]TX02 將balance修改為900。
[3]TX01 再次讀取balance,和第一次讀取的值不同。

3.幻讀(Phantom reads)

Transaction1讀取多筆資料,重複讀取得到新插入的資料
[1]TX01 讀取ACCOUNT資訊有10筆資料
[2]TX02 向ACCOUNT插入新的資料5筆。
[3]TX01 再次讀取ACCOUNT資訊讀到15筆資料

Isolation level

資料庫透過Isolation level 設定可以解決concurrent的問題

Dirty read Unrepeatable read Phantom reads
READ UNCOMMITTED V V V
READ COMMITTED X V V
REPEATABLE READ X X V
SERIALIZABLE X X X

@Transactional

isolation屬性

  • READ_UNCOMMITTED
  • READ_COMMITTED
  • REPEATABLE_READ
  • SERIALIZABLE

測試dirty read

DB連線設定

# dbconfig.properties
jdbc.user=sa
jdbc.password=sa
# 需透h2 server mode的連線方式,connection連線數才不會受限
jdbc.jdbcUrl=jdbc:h2:tcp://localhost/~/test
jdbc.driverClass=org.h2.Driver
<?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:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd">
    <!-- 加載外部文件   -->
    <context:property-placeholder location="classpath:dbconfig.properties"></context:property-placeholder>
    <context:component-scan base-package="com.swj"></context:component-scan>
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
        <property name="driverClass" value="${jdbc.driverClass}"></property>
    </bean>
    <!-- 配置JdbcTemplate   -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
    </bean>
    <!-- transaction management   -->
    <bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <tx:annotation-driven transaction-manager="tx"></tx:annotation-driven>
</beans>
@Repository
public class AccountDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Transactional(readOnly = true,isolation = Isolation.READ_UNCOMMITTED)
    //@Transactional(readOnly = true,isolation = Isolation.READ_COMMITTED)
    public Integer queryAccountBalance(String accountId) {
        String sql = "SELECT BALANCE FROM ACCOUNT WHERE ACCOUNT_ID = ?";
        Integer balance = jdbcTemplate.queryForObject(sql, Integer.class,accountId);

        return balance;
    }
}
@Service
public class AccountService {
    @Autowired
    private AccountDao accountDao;

    public Integer queryAccount(String accountId){
        return accountDao.queryAccountBalance(accountId);
    }
}
@Test
public void testDay28() throws FileNotFoundException {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("bean28.xml");
    AccountService service = ioc.getBean(AccountService.class);
    Integer balance = service.queryAccount("1");
    System.out.println(balance);
}

UPDATE 資料庫數據未提交
https://ithelp.ithome.com.tw/upload/images/20221014/20128084Z8GhPLSztF.jpg
Result
https://ithelp.ithome.com.tw/upload/images/20221014/20128084VvYmYezkon.jpg
若dao中的isolation level修改為READ_COMMITTED,讀到的值則為1000

Reference


上一篇
Day27 - Declarative transaction management (3)
下一篇
Day29 - Declarative transaction management (5)
系列文
這些年,我們早該學會的Spring Framework30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言