本篇試驗在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.
沒有留言:
張貼留言