MongoDB事物使用简单总结

MongoDB事物使用简单总结

一、 说明

   工作时间繁忙,过了一个多月了好久才有时间做总结。。。。
   假如有写的不对的地方请告诉我,不胜感激涕零 !

1. 开发环境

Jdk :1.8
MongoDB:4.0.3
SpringBoot 2.0+

2. 开发场景说明

提供接口供对方调用。批量处理集合对象。每秒大概达到三千到五千左右。
 对象中有某些属性是唯一的(filedId),当同一个集合中有相同的field时,对某些属性更新替换,既更新时有些信息需要更新有些则不需要更新。
 数据集合中有某个属性(entityType),处理时根据该属性分集合保存。
 每次调用要么成功,要么失败既支持事物。

3. 其他说明

  1. MongoDB4.0之后才支持事物
  2. MongoDB 使用事物必须是复制集合。所以事先需要准备好复制集。
    搭建复制集教程可以参考:https://mp.csdn.net/postedit/83860908

二、 相应知识点

1. Mongodb事物简介

Mongodb的事务是依靠 Mongodb连接的客户端 session 实现,事务执行的流程大致是 建立 session,通过 session startTransaction 启动事务,如果一系列事务都完成,那么 commitTransaction 完成事务操作,并结束当前事务 session;如果一系列事务中有任意事件失败, 那么 abortTransaction 中止事务,内部将已完成的任务回退到修改之前,并结束当前事务 session。
整个事物处理过程如下:

      session = client.startSession();
      session.startTransaction();
      session.commitTransaction();
      session.abortTransaction();
      session.endSession();

2. 使用配置

  1. application.properties
# mongodb配置
spring.data.mongodb.uri=mongodb://192.168.101.110:27027/admin
## 库名称
spring.data.mongodb.dbname=file_info-test
  1. MongoDB config

@Configuration
public class MongoDBConifg {

    private MongoClient client;

    @Value("${spring.data.mongodb.uri}")
    private String mongodbURI;
    @Value("${spring.data.mongodb.dbname}")
    private String databaseName;

    private MongoDatabase mongoDatabase;

   // 设置MongoDB的一些配置
    @PostConstruct
    public void initMongoDB() {
        getLogger("org.mongodb.driver").setLevel(Level.SEVERE);
        CodecRegistry codecRegistry = fromRegistries(MongoClient.getDefaultCodecRegistry(), fromProviders(
                PojoCodecProvider.builder().register("com.mongodb.models").build()));
        CodecRegistry pojoCodecRegistry = fromRegistries(MongoClient.getDefaultCodecRegistry(),
                fromProviders(PojoCodecProvider.builder().automatic(true).build()));
        MongoClientOptions.Builder options = new MongoClientOptions.Builder().codecRegistry(pojoCodecRegistry);
        MongoClientURI uri = new MongoClientURI(mongodbURI, options);
        client = new MongoClient(uri);
        mongoDatabase = client.getDatabase(databaseName);
        mongoDatabase.withCodecRegistry(codecRegistry);
    }

    public MongoClient getClient() {
        return client;
    }

    public MongoDatabase getMongoDatabase() {
        return mongoDatabase;
    }

  1. 持久层使用 (ps : 为展示,都写在一个版面,在实际开发过程中不应该这么写。。。)
    本次使用updateOneModel 这个WriteModel有其他五种不同的Model实际开发过程中根据不用的业务场景选择选用。
    在这里插入图片描述
@Autowired
private MongoDBConifg mongoDBConifg;
public boolean bathInsertLayoutFile(List<LayoutFile> layoutFiles) throws MongoCommandException {
        MongoClient client = mongoDBConifg.getClient();
        ClientSession session = client.startSession();
        try {
            session.startTransaction(TransactionOptions.builder().writeConcern(WriteConcern.MAJORITY).build());
            Map<String, List<LayoutFile>> fileList = layoutFiles.stream().collect(Collectors.groupingBy(LayoutFile::getEntityType));
            for (String layoutFile : fileList.keySet()) {
                List<LayoutFile> temp = fileList.get(layoutFile);
                MongoCollection<LayoutFile> collection = mongoDBConifg.getMongoDatabase().getCollection(layoutFileUtils.getCollectionName(layoutFile), LayoutFile.class);
                repository.insetLayoutFileList(session, collection, temp);    // 入库动作
            }
            session.commitTransaction();
        } catch (MongoCommandException e) {
            session.abortTransaction();
            log.error("rollback transaction ,  insert failure  error message {}", e.getMessage(), e);
            return false;
        } catch (Exception e) {
            log.error(" insert failure, error message {}", e.getMessage(), e);
            session.abortTransaction();
            return false;
        } finally {
            session.close();
        }
        return true;
    }



public com.mongodb.bulk.BulkWriteResult insetLayoutFileList(ClientSession session, MongoCollection<LayoutFile> collection, List<LayoutFile> layoutFiles) {
        com.mongodb.bulk.BulkWriteResult bulkWriteResult = null;

        UpdateOptions updateOptions = new UpdateOptions().upsert(true);
        List<UpdateOneModel<LayoutFile>> updates = new ArrayList<UpdateOneModel<LayoutFile>>();
        //  构建数据
        for (LayoutFile layoutFile : layoutFiles) {
            Bson filter = new Document("fileId", layoutFile.getFileId());   // 根据fileId ,若是数据存在则更新,不存在插入
            if (log.isDebugEnabled()) {
                log.info("filedId:" + layoutFile.getFileId());
            }
            UpdateOneModel<LayoutFile> updateOneModel = new UpdateOneModel(filter, convertToBson(layoutFile), updateOptions);
            updates.add(updateOneModel);
        }
        try {
            bulkWriteResult = collection.bulkWrite(session, updates, new BulkWriteOptions().ordered(false));
        } catch (Exception e) {
            log.error("insetLayoutFileList  error message {}", e.getMessage(), e);
            throw e;
        }
        return bulkWriteResult;
    }

3. 遇到的问题以及处理过程

  1. 调试(debug)时提示session不存在
    原因是:MongoDB默认回话时间为一分钟,当我们进行业务处理时,超过一分钟之后当前session被回收所以报错。
    处理方式: 1) 简化处理逻辑尽可能提升处理速度,在session过期时间内处理完所有的事情。
    2) 设置session 过去时间、可以考虑写在MongoDB启动实例配置文件。
    ps : 一般开启MongoDB事物动作是在持久层做。不应该在开启MongoDB事物中业务处理。这样就可以不用担心session被回收而且也符合代码规范。假如上述两种方式都没能解决,可以往数据量太大,或者MongoDB提供计算能力方向考虑。。。酌情考虑做优化。
  2. 保存数据时提示:invalid Bson field name **
    既就是Java实体对象转换成Bson 之后别识别出来。或者Bson 类型转换不正确。
    处理方式((layoutFile:具体的某个实体对象)):
    (1)这种情况下适用于插入:
       String json = JSON.toJSONString(layoutFile);
       Document document = Document.parse(json);

(2)这种情况下适用于update

// 将对象转换,从新构造一个新的document
private Document convertToBson(LayoutFile layoutFile) {
           Document newdoc = new Document();
          newdoc.put("status", layoutFile.getStatus());
          newdoc.put("entityType", layoutFile.getEntityType());
          newdoc.put("updatetime", layoutFile.getUpdatetime());
          newdoc.put("dt", layoutFile.getDt());
          return new Document("$set", newdoc).
                                             append("$setOnInsert", new Document("createtime", layoutFile.getCreatetime())).  // 在第一次插入是赋值。
                                             append("$currentDate", new BasicDBObject("updateTime", true));    // 每次修改一条记录时会更新
}
  1. 在复制集合中,不能自动的创建集合,并且在给多个集合创建多个信息(如唯一索引)时,需要初始化集合,编写初始化脚本(一个文件)。执行插入数据是报错(第二次插入数据时)。报错信息大体如下: ** 具体某个集合DuplicateKey

    当时处理方法:也不知道什么原因。处理方式就是先执行创建集合的脚本。确保集合创建完成之后再进行创建索引信息。

五、 其他

待后续补充完整。。。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dream_bin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值