網頁

2021/1/6

Spring Boot @Transactional Propagation.REQUIRES_NEW with one connection

本篇試驗在Spring Boot的資料庫連線池(connection pool)設定僅一條連線,搭配@Transactional(propagation = Propagation.REQUIRES_NEW)的效果。

本篇由來是同事誤以為@Transactional(propagation = Propagation.REQUIRES_NEW)會開啟另一個執行緒(thread)及新的連線進行交易,而原先的連線交易會繼續執行。為了證明兩個交易都是在同個執行緒執行,第一個交易會等待第二個交易結束才會繼續。


範例環境:

  • Spring Boot 2.3.2.RELEASE
  • Spring Data JPA
  • H2 Database
  • Lombok

Spring Boot在Spring Data JPA預設使用的連線池為HikariCP,在application.properties修改連線池連線數為1如下。

application.properties

# 修改HikariCP連線池最大連線數=1
spring.datasource.hikari.maximum-pool-size=20
# 設定連線逾時時間=5秒
spring.datasource.hikari.connection-timeout=5000

@Transactional(propagation = Propagation.REQUIRES_NEW)效果為若已經存在交易則先暫停並另開新的交易。

ItemDao.addItem()設定為@Transactional(propagation = Propagation.REQUIRES_NEW)

ItemDao

package com.abc.demo.dao;

import com.abc.demo.entity.Item;
import com.abc.demo.repository.ItemRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class ItemDao {

    @Autowired
    private ItemRepository itemRepository;

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void addItem(Item item) {
        System.out.println("addItem start...");

        itemRepository.save(item);

        System.out.println("addItem end...");
    }

}

則在下面DemoService.createItem()中呼叫ItemDao.addItem()會因新交易無法取得連線而發生逾時錯誤。

DemoService

package com.abc.demo.service;

import com.abc.demo.dao.ItemDao;
import com.abc.demo.entity.Item;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;

@Service
public class DemoService {

    @Autowired
    private ItemDao itemDao;

    @Transactional
    public void createItem(String itemName) {
        System.out.println("createItem start...");

        Item item = new Item();
        item.setName(itemName);
        item.setCreateDate(new Date());

        itemDao.addItem(item); // 無法取得連線

        System.out.println("createItem end...");
    }

}

在連線池中的連線數只有一條的情況下,DemoService.createItem()開啟第一次交易並取得唯一的一條連線,而ItemDao.addItem()設為@Transactional(propagation = Propagation.REQUIRES_NEW)所以會取得另一條連線開啟新的交易,但連線池中的連線已經用完了,所以導致連線逾時錯誤如下。

Caused by: java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 5000ms.


沒有留言:

張貼留言