如何以干净的方式在 Spring Data Redis 中实现事务?
Posted
技术标签:
【中文标题】如何以干净的方式在 Spring Data Redis 中实现事务?【英文标题】:How to implement transaction in Spring Data Redis in a clean way? 【发布时间】:2014-03-07 00:48:12 【问题描述】:我正在关注 here 上的 RetwisJ 教程。在这我不认为 Redis 事务被实现。例如,在下面的函数中,如果中间发生一些异常,数据将处于不一致的状态。 我想知道如何在 Spring Data Redis 中将如下功能实现为单个事务:
public String addUser(String name, String password)
String uid = String.valueOf(userIdCounter.incrementAndGet());
// save user as hash
// uid -> user
BoundHashOperations<String, String, String> userOps = template.boundHashOps(KeyUtils.uid(uid));
userOps.put("name", name);
userOps.put("pass", password);
valueOps.set(KeyUtils.user(name), uid);
users.addFirst(name);
return addAuth(name);
这里userIdCounter
、valueOps
和users
在构造函数中被初始化。我在文档中遇到过this(第 4.8 节),但我不知道如何将它放入这个函数中,其中一些变量在函数外部初始化(请不要告诉我必须在我需要交易的每一个功能!)。
PS:还有@Transaction
注解或事务管理器可用于Spring Data Redis吗?
更新:我尝试过使用MULTI
、EXEC
。我写的代码是为另一个项目编写的,但是当它应用于这个问题时,它将如下:
public String addMyUser(String name, String password)
String uid = String.valueOf(userIdCounter.incrementAndGet());
template.execute(new SessionCallback<Object>()
@Override
public <K, V> Object execute(RedisOperations<K, V> operations)
throws DataAccessException
operations.multi();
getUserOps(operations, KeyUtils.uid(uid)).put("name", name);
getUserOps(operations, KeyUtils.uid(uid)).put("pass", password);
getValueOps(operations).set(KeyUtils.user(name), uid);
getUserList(operations, KeyUtils.users()).leftPush(name);
operations.exec();
return null;
);
return addAuth(name);
private ValueOperations<String, String> getValueOps(RedisOperations operations)
return operations.opsForValue();
private BoundHashOperations<String, String, String> getUserOps(RedisOperations operations, String key)
return operations.boundHashOps(key);
private BoundListOperations<String, String> getUserList(RedisOperations operations, String key)
return operations.boundListOps(key);
请告知是否推荐这种使用MULTI
、EXEC
的方式。
【问题讨论】:
【参考方案1】:在 SD Redis 1.2 之前,您必须使用 TransactionSynchronisationManager 自行处理事务
上面的代码片段可能看起来像这样:
public String addUser(String name, String password)
String uid = String.valueOf(userIdCounter.incrementAndGet());
// start the transaction
template.multi();
// register synchronisation
if(TransactionSynchronisationManager.isActualTransactionActive())
TransactionSynchronisationManager.registerSynchronisation(new TransactionSynchronizationAdapter())
@Override
public void afterCompletion(int status)
switch(status)
case STATUS_COMMITTED : template.exec(); break;
case STATUS_ROLLED_BACK : template.discard(); break;
default : template.discard();
BoundHashOperations<String, String, String> userOps = template.boundHashOps(KeyUtils.uid(uid));
userOps.put("name", name);
userOps.put("pass", password);
valueOps.set(KeyUtils.user(name), uid);
users.addFirst(name);
return addAuth(name);
请注意,一旦在 multi 中,读取操作也将成为事务的一部分,这意味着您可能无法从 redis 服务器读取数据。
该设置可能与上述设置不同,因为您可能需要另外调用 WATCH
。此外,您还必须处理多个回调,不要多次发送MULTI
和/或EXEC
。
即将发布的 Spring Data Redis 1.3 RELEASE 将支持 Spring 托管事务,以处理 MULTi|EXEC|DISCARD
并在事务同步处于活动状态时允许读取操作(对现有键)。您已经可以试一试 BUILD-SNAPSHOT 并通过设置 template.setEnableTransactionSupport(true)
将其打开。
【讨论】:
您好,很久没有人问这个问题了。由于没有人回答,我想出了如何去做(在问题中更新)。但我不知道这是否是推荐的方法。 TransactionSynchronisationManager 是要走的路吗?它是否需要除此之外的任何其他配置?我在编写的代码中发现的优势是,如果我必须从 Redis 读取数据,我可以使用“模板”,因此它不会成为事务的一部分 使用SessionCallback
非常好(请参阅reference),因为这会在命令执行和之后释放它的时间绑定使用的连接。 TransactionSynchronisationManager
或在下一个版本 1.3 中的 template.setEnableTransactionSupport(true)
是用于处理对 redis 的多次调用,这些调用可能不在附近,而正在进行的外部事务处于活动状态。通常不必直接与 TSM 交互。
@ChristophStrobl:我有类似的情况。我已经搜索了文档,但我没有看到我们如何保证交易的任何参考。一旦抛出任何异常,正确配置的 RestTemplate 是否应该回滚?假设我们在 Spring Service 中有方法:@Transactional public void doSomth() redisTemplate.delete(...); redisTemplate.delete(...);
它是否应该正常回滚第一次删除第二次会失败?以上是关于如何以干净的方式在 Spring Data Redis 中实现事务?的主要内容,如果未能解决你的问题,请参考以下文章
使用spring jdbc时将长(+20行sql)外部化的干净方法? [关闭]