最近有一个需求, 用户信息表内有一个唯一索引用户 id, 希望在插入时如果不存在该用户的信息则插入, 否则更新, 看了网上的方法, 似乎 MongoTemplate 并没有提供这项功能 (有类似的功能但只对主键 id 有效), 所以我选择了先删除, 再插入, 由此引发了我对事务的需求
修改 MongoDB 配置
MongoDB 的事务只能在开启副本集的时候才能使用,Windows 上的 MongoDB 安装后默认是单副本,所以我们首先需要将其转换为多副本.
找到 MongoDB 的安装目录, 在 bin
目录下有一个 mongod.cfg
配置文件, 在文件中增加配置:
replication:
replSetName: rs0
然后重启 MongoDB, 以管理员身份运行终端:
net stop mongodb
net start mongodb
此时可以连上 MongoDB, 但是无法读取其中的数据, 所以需要初始化将其配置为副本集的成员, 在 MongoDB 的命令行界面执行以下命令 (这里使用 IDEA 提供的命令行):
# 初始化
rs.initiate()
# 查看状态
rs.status()
在 SpringBoot 中开启事务
先在配置文件中增加一项自定义配置, 这是为了兼容性
spring:
data:
mongodb:
# uri: mongodb://localhost/tcm
host: localhost
port: 27017
database: test
auto-index-creation: true
transactionEnabled: true # 自定义配置, 打开事务
写配置类, 配置事务
@Configuration
public class MongoTransactionConfiguration {
@Bean
@ConditionalOnProperty(name = "spring.data.mongodb.transactionEnabled", havingValue = "true")
MongoTransactionManager mongoTransactionManager(MongoDatabaseFactory factory) {
return new MongoTransactionManager(factory);
}
}
然后在需要的方法上面加上 @Transactional
注解就可以开启事务啦
测试结果
@Override
@Transactional(rollbackFor = Exception.class)
public UserInfo saveUserInfo(UserInfo userInfo) {
userInfo.setUpdateTime(DateUtil.getNowTime());
Query q = Query.query(Criteria.where("userId").is(userInfo.getUserId()));
UserInfo info = mongoTemplate.findOne(q, UserInfo.class);
if (info != null) {
userInfo.setId(info.getId());
mongoTemplate.remove(q, UserInfo.class);
int i = 1 / 0; // 报错测试事务效果
}
return mongoTemplate.save(userInfo);
}
可以看到报错以后执行事务回滚, 并没有删除原来的 userId
为 1 的数据