在描述問題之前先說明幾個前提,假設在Spring的配置文件中使用下面的方式配置了數據庫的事務:
1
2
3
4
|
<bean id= "transactionManager" class = "org.springframework.jdbc.datasource.DataSourceTransactionManager" > <property name= "dataSource" ref= "dataSource" /> </bean> <tx:annotation-driven transaction-manager= "transactionManager" /> |
現在有UserDao和SecurityService:
1
2
3
4
5
6
7
|
@Repository public class UserDao { public User getUser() { // query user from user table return queryObject( "select * from user order by id desc limit 1" ); } } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
@Service @Transactional public class SecurityService { @Autowired private UserDao userDao; public void checkUserInfo() { while ( true ) { User user = userDao.getUser(); if (user != null && "Tom" .equals(user.getName()) { System.out.println( "Tom is here" ); break ; } } } } |
在調用SecurityService#checkUserInfo()方法的過程中,通過userDao#getUser()方法獲取到的數據是不變的,即使這個時候新插入了一條name為Tom的數據循環也不會結束。另外將SecurityService上面的@Transactional注解去掉也無濟于事。
首先想到會不會是數據庫連接池的問題,換成了Spring自帶的也是如此;然后從JdbcTemplate里面直接調用了Connection對象,使用原始的JDBC方式操作數據庫,這個時候數據是實時變化的,于是想到應該是Spring的事務和當前操作線程進行綁定了。查看源代碼進入之后果然在DataSourceUtils#doGetConnection方法里面發現了Spring在每個線程的每個DataSource上創建了一個Connection并且與事務進行了綁定。因為tx:annotation-driven配置文件對所有的Service層(加了@Service注解的類)進行了事務綁定,所以無論是否使用@Transactional都在同一個線程中綁定了同一個Connection,只是不進行事務操作而已。
經過多次實驗和查找資料,最后終于找到了完美的解決方法:只要在上述的checkUserInfo方法中加上 @Transactional(propagation = Propagation.NOT_SUPPORTED) 注解就可以了。當然也可以獲取到Connection然后手工進行操作,也可以使用DateUtils包進行操作。