Redis
經過multi
, exec
, 或discard
命令來提供事務支持,這些操做在RedisTemplate
中一樣是可用的。可是,RedisTemplate
默認使用RedisCallBack接口,並不能保證使用同一鏈接來執行同一事務中的全部操做(此時Transaction
是無效的)。html
又可是,Spring Data Redis
提供了SessionCallback接口,以便在須要保證同一鏈接
執行多個操做時使用,好比「須要使用Redis事務
時」。 咱們可以看到:java
public <T> T execute(SessionCallback<T> session) {
Assert.isTrue(initialized, "template not initialized; call afterPropertiesSet() before using it");
Assert.notNull(session, "Callback object must not be null");
RedisConnectionFactory factory = getConnectionFactory();
// bind connection
RedisConnectionUtils.bindConnection(factory, enableTransactionSupport);//第8行
try {
return session.execute(this);
} finally {
RedisConnectionUtils.unbindConnection(factory);
}
}
複製代碼
RedisTemplate.execute(SessionCallback<T> session)
方法的第8行
已經作了鏈接綁定
;
使用方式以下:redis
//execute a transaction
List<Object> txResults = redisTemplate.execute(new SessionCallback<List<Object>>() {
public List<Object> execute(RedisOperations operations) throws DataAccessException {
operations.multi();
operations.opsForSet().add("key", "value1");
// This will contain the results of all ops in the transaction
return operations.exec();
}
});
System.out.println("Number of items added to set: " + txResults.get(0));
複製代碼
在返回以前,RedisTemplate將使用它的value, hash key和hash value 序列化器來反序列化exec的全部結果。 另一個額外的exec方法,容許您爲事務結果傳遞自定義序列化器。spring
上面咱們可以看到,能夠經過SessionCallback綁定鏈接
,而且實現multi
, exec
,或discard
,從而支持Redis事務
,可是這樣就顯得很複雜並且Redis操做(opsXXX.X)
執行的位置也變得有侷限性(儘管不影響功能)。 然而,Spring
下咱們能夠更加簡單,只需兩步:bash
method
添加註解**@Transactional或者Xml配置**(< tx:method />),註冊事務切點。至關於調用了TransactionSynchronizationManager.setActualTransactionActive(true);RedisTemplate
實例的事務支持
(默認被禁用)/** Sample Configuration **/
@Configuration
public class RedisTxContextConfiguration {
@Bean
public StringRedisTemplate redisTemplate() {
StringRedisTemplate template = new StringRedisTemplate(redisConnectionFactory());
// explicitly enable transaction support
template.setEnableTransactionSupport(true);
return template;
}
}
複製代碼
redisTemplate實例 默認調用 execute(RedisCallback action),方法內容以下:session
public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline){
/** * 變量聲明等操做…… */
try {
if (enableTransactionSupport) {
// only bind resources in case of potential transaction synchronization
conn = RedisConnectionUtils.bindConnection(factory, enableTransactionSupport);
} else {
conn = RedisConnectionUtils.getConnection(factory);
}
/** * 其餘操做…… */
}
public static RedisConnection bindConnection(RedisConnectionFactory factory, boolean enableTransactionSupport) {
/** * 不用管…… */
RedisConnection conn = factory.getConnection();
RedisConnection connectionToBind = conn;
//redisTemplate開啓事務支持,同時transactionManager非只讀的實際事務被激活
if (enableTransactionSupport && isActualNonReadonlyTransactionActive()) {
connectionToBind = createConnectionProxy(conn, factory);
}
/** * 不用管…… */
return conn;
}
複製代碼
能夠看到,enableTransactionSupport = true 將會促使當前Thread
嘗試綁定RedisConnection
,僅當也 isActualNonReadonlyTransactionActive = true,鏈接纔會成功綁定。ui
鏈接綁定成功,同時將會觸發MULTI
。一旦MULTI
被調用:this
RedisConnection
將會排隊write操做
;readonly操做
,例如KEYS
將會被分發給一個全新的 (非Thread
綁定)的RedisConnection
;EXEC
或DISCARD
將交由SpringAOP的動態代理對象去調用:事務構建
過程當中沒有異常拋出
(默認RuntimeException
及其子類),則EXEC
被調用,執行命令隊列;DISCARD
,清除命令隊列。開啓事務支持後:spa
/** Usage Constrainsts **/
// executed on thread bound connection
template.opsForValue().set("foo", "bar");
// read operation executed on a free (not tx-aware)
connection template.keys("*");
// returns null as values set within transaction are not visible
template.opsForValue().get("foo");
複製代碼
上面的樣例代碼是Spring官網給出的,第三個顯然是WATCH
命令開啓樂觀鎖
後的結果。然而至少在本人正在使用的 spring-data-redis-1.8.10.RELEASE.jar中,代理
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.8.10.RELEASE</version>
</dependency>
複製代碼
WATCH
命令並無被使用,親測第三種
效果並不存在(你能夠根據本身的依賴版本嘗試一下),此處亮出代碼。
private static void potentiallyRegisterTransactionSynchronisation(RedisConnectionHolder connHolder, final RedisConnectionFactory factory) {
if (isActualNonReadonlyTransactionActive()) {
if (!connHolder.isTransactionSyncronisationActive()) {
connHolder.setTransactionSyncronisationActive(true);
RedisConnection conn = connHolder.getConnection();
conn.multi();//在此以前conn.watch()未被調用
TransactionSynchronizationManager.registerSynchronization(new RedisTransactionSynchronizer(connHolder, conn,
factory));
}
}
}
複製代碼
兩個
RedisTemplate實例兩個RedisTemplate
實例?
commands
要麼統一執行
,要麼都被清除
,維護數據完整性;command
當即執行,即時返回執行結果
而且更高效
;/** Sample Configuration **/
@Configuration
public class RedisTxContextConfiguration {
@Bean
public StringRedisTemplate redisTransactionTemplate() {
StringRedisTemplate template = new StringRedisTemplate(redisConnectionFactory());
// explicitly enable transaction support
template.setEnableTransactionSupport(true);
return template;
}
@Bean
public StringRedisTemplate redisTemplate() {
StringRedisTemplate template = new StringRedisTemplate(redisConnectionFactory());
return template;
}
}
複製代碼