@Transactional註解工作原理
阿新 • • 發佈:2018-12-25
一、配置好Bean例項
首先配置好DataSource和DataSourceTransactionManager這兩個類的Bean例項
二、添加註解
在測試類上新增@Transactional註解
三、工作原理
執行配置@Transactional註解的測試類的時候,具體會發生如下步驟
1)事務開始時,通過AOP機制,生成一個代理connection物件,並將其放入DataSource例項的某個與DataSourceTransactionManager相關的某處容器中。在接下來的整個事務中,客戶程式碼都應該使用該connection連線資料庫,執行所有資料庫命令[不使用該connection連線資料庫執行的資料庫命令,在本事務回滾的時候得不到回滾]
2)事務結束時,回滾在第1步驟中得到的代理connection物件上執行的資料庫命令,然後關閉該代理connection物件
根據上面所述,我們所使用的客戶程式碼應該具有如下能力:
1)每次執行資料庫命令的時候
如果在事務的上下文環境中,那麼不直接建立新的connection物件,而是嘗試從DataSource例項的某個與DataSourceTransactionManager相關的某處容器中獲取connection物件;在非事務的上下文環境中,直接建立新的connection物件
2)每次執行完資料庫命令的時候
如果在事務的上下文環境中,那麼不直接關閉connection物件,因為在整個事務中都需要使用該connection物件,而只是釋放本次資料庫命令對該connection物件的持有;在非事務的上下文環境中,直接關閉該connection物件
在org.springframework.jdbc.core.JdbcTemplate中的如下程式碼:
org.springframework.jdbc.datasource.DataSourceUtils.getConnection(getDataSource());
具有1)中所述能力
在org.springframework.jdbc.core.JdbcTemplate中的如下程式碼:
DataSourceUtils.releaseConnection(connection, getDataSource());
就有2)所述能力
因此,我們的客戶程式碼或者可以使用JdbcTemplate(它使用DataSourceUtils.getConnection(getDataSource())和DataSourceUtils.releaseConnection(connection, getDataSource())),或者可以直接使用DataSourceUtils.getConnection(getDataSource())和DataSourceUtils.releaseConnection(connection, getDataSource());
四、舉例
執行這個測試類的具體過程如下:
1)第1個事務
i、事務開始,生成一個代理connection物件,並將其放入DataSource例項的某個與DataSourceTransactionManager相關的某處容器中
ii、執行@Before註解標記的init方法和@Test標記的existTest方法,整個過程中都從DataSource例項中獲取第1步驟中得到的代理connection物件,使用該connection物件連線資料庫執行資料庫命令
iii、事務結束,回滾在第1步驟中得到的代理connection物件上執行的資料庫命令,然後關閉該代理connection物件
2)第2個事務
i、事務開始,生成一個代理connection物件,並將其放入DataSource例項的某個與DataSourceTransactionManager相關的某處容器中
ii、執行@Before註解標記的init方法和@Test標記的insertTest方法,整個過程中都從DataSource例項中獲取第1步驟中得到的代理connection物件,使用該connection物件連線資料庫執行資料庫命令
iii、事務結束,回滾在第1步驟中得到的代理connection物件上執行的資料庫命令,然後關閉該代理connection物件
首先配置好DataSource和DataSourceTransactionManager這兩個類的Bean例項
二、添加註解
在測試類上新增@Transactional註解
三、工作原理
執行配置@Transactional註解的測試類的時候,具體會發生如下步驟
1)事務開始時,通過AOP機制,生成一個代理connection物件,並將其放入DataSource例項的某個與DataSourceTransactionManager相關的某處容器中。在接下來的整個事務中,客戶程式碼都應該使用該connection連線資料庫,執行所有資料庫命令[不使用該connection連線資料庫執行的資料庫命令,在本事務回滾的時候得不到回滾]
2)事務結束時,回滾在第1步驟中得到的代理connection物件上執行的資料庫命令,然後關閉該代理connection物件
根據上面所述,我們所使用的客戶程式碼應該具有如下能力:
1)每次執行資料庫命令的時候
如果在事務的上下文環境中,那麼不直接建立新的connection物件,而是嘗試從DataSource例項的某個與DataSourceTransactionManager相關的某處容器中獲取connection物件;在非事務的上下文環境中,直接建立新的connection物件
2)每次執行完資料庫命令的時候
如果在事務的上下文環境中,那麼不直接關閉connection物件,因為在整個事務中都需要使用該connection物件,而只是釋放本次資料庫命令對該connection物件的持有;在非事務的上下文環境中,直接關閉該connection物件
在org.springframework.jdbc.core.JdbcTemplate中的如下程式碼:
org.springframework.jdbc.datasource.DataSourceUtils.getConnection(getDataSource());
具有1)中所述能力
在org.springframework.jdbc.core.JdbcTemplate中的如下程式碼:
DataSourceUtils.releaseConnection(connection, getDataSource());
就有2)所述能力
因此,我們的客戶程式碼或者可以使用JdbcTemplate(它使用DataSourceUtils.getConnection(getDataSource())和DataSourceUtils.releaseConnection(connection, getDataSource())),或者可以直接使用DataSourceUtils.getConnection(getDataSource())和DataSourceUtils.releaseConnection(connection, getDataSource());
四、舉例
現在有如下一個測試類:
import com.dslztx.service.FooterService; import com.dslztx.service.FooterServiceTest; import org.apache.log4j.Logger; import org.junit.Assert; import org.junit.Before; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.transaction.TransactionConfiguration; import org.springframework.transaction.annotation.Transactional; import javax.sql.DataSource; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath:applicationContext.xml"}) @Transactional @TransactionConfiguration(defaultRollback = true) public class FooterServiceTest { private final Logger logger = Logger.getLogger(FooterServiceTest.class); @Autowired protected DataSource dataSource; protected JdbcTemplate template; @Autowired FooterService footerService; @Before public void init() { template = new JdbcTemplate(dataSource); String sql1 = "INSERT INTO `Footer` (`ID`,`name`) VALUES (4992,'hello');"; template.update(sql1); String sql2 = "INSERT INTO `Footer` (`ID`,`name`) VALUES (4995,'world');"; template.update(sql2); } @Test public void existTest() { try { String footer1 = "footer1"; Assert.assertTrue(footerService.exist(footer1)); String footer2 = "do not exist"; Assert.assertFalse(footerService.exist(footer2)); } catch (Exception e) { logger.error("", e); Assert.fail(); } } @Test public void insertTest() { try { String footer = "footer"; String id = "1000"; footerService.insert(footer, id); } catch (Exception e) { logger.error("", e); Assert.fail(); } } }
執行這個測試類的具體過程如下:
1)第1個事務
i、事務開始,生成一個代理connection物件,並將其放入DataSource例項的某個與DataSourceTransactionManager相關的某處容器中
ii、執行@Before註解標記的init方法和@Test標記的existTest方法,整個過程中都從DataSource例項中獲取第1步驟中得到的代理connection物件,使用該connection物件連線資料庫執行資料庫命令
iii、事務結束,回滾在第1步驟中得到的代理connection物件上執行的資料庫命令,然後關閉該代理connection物件
2)第2個事務
i、事務開始,生成一個代理connection物件,並將其放入DataSource例項的某個與DataSourceTransactionManager相關的某處容器中
ii、執行@Before註解標記的init方法和@Test標記的insertTest方法,整個過程中都從DataSource例項中獲取第1步驟中得到的代理connection物件,使用該connection物件連線資料庫執行資料庫命令
iii、事務結束,回滾在第1步驟中得到的代理connection物件上執行的資料庫命令,然後關閉該代理connection物件