MongoDB通用持久类MongoBaseDao

本文介绍了在MongoTemplate基础上创建通用MongoBaseDao的原因和方法,包括批量更新、删除操作的实现,以及如何通过泛型简化代码。MongoBaseDao提供了批量操作的便利,使得在不需复杂业务逻辑的场景下,其他持久类只需继承即可满足需求。此外,还讨论了Dao层与Service层的职责划分,以及MongoClient的配置。

MongoDB通用持久类MongoBaseDao

MongoTemplate已经给我们提供了丰富的api,为什么还需要通用持久层类?
第一,比较复杂的持久化方法(如:批量更新、批量删除),MongoTemplate没有开箱即用的api,是需要我们自定义的。
第二,定义通用持久层类支持泛型操作,可以简化整个持久层的代码量,对于简单的业务场景来说,下列持久类MongoBaseDao就可以满足要求。

public class MongoBaseDao<T extends Serializable & BaseEntity<ID>, ID> {
    private static final String _id = "_id";
    private static final String id = "id";

    @Autowired
    private MongoClient mongoClient;
	
	// 获取MongoDB操作模板对象
    public MongoTemplate getTemplate(String database) {
        return new MongoTemplate(mongoClient, database);
    }
	
	// 批量更新操作(如果id存在执行更新,如果id不存在执行插入)
    public void upsert(List<T> list, Class<T> tClass, String database){
        BulkOperations operations = getTemplate(database).bulkOps(BulkOperations.BulkMode.UNORDERED, tClass);
        operations.upsert(getPairList(list));
        operations.execute();
    }

    private List<Pair<Query, Update>> getPairList(List<T> list) {
        List<Pair<Query, Update>> pairList = new LinkedList<>();
        for (T t : list) {
            Pair<Query, Update> pair = getPair(t);
            pairList.add(pair);
        }
        return pairList;
    }

    @SneakyThrows
    private Pair<Query, Update> getPair(T t) {
        Query query = new Query();
        Update update = new Update();
        ObjectMapper objectMapper = new ObjectMapper();
        String json = objectMapper.writeValueAsString(t);
        Document document = objectMapper.readValue(json, Document.class);
        // 这里_id和id的使用注意区分,实体类定义的主键为id,Mongodb的主键为_id
        query.addCriteria(Criteria.where(_id).is(document.remove(id)));
        // 这里更新了除_id之外所有的字段,适合于先查询后更新的应用场景
        document.forEach(update::set);
        // 如果是未查询、仅更新某些字段,应排除你未设置值(为null)的其他字段,这时候需使用下列forEach函数
        /*document.forEach((key, value)->{
            if(ObjectUtils.isNotEmpty(value)){
                update.set(key, value);
            }
        });*/
        return Pair.of(query, update);
    }
	
	// 批量删除,适用于先查询后删除的使用场景
    public void remove(List<T> list, Class<T> tClass, String database){
        BulkOperations operations = getTemplate(database).bulkOps(BulkOperations.BulkMode.UNORDERED, tClass);
        List<Query> queryList = new LinkedList<>();
        for (T t : list) {
            queryList.add(Query.query(Criteria.where(_id).is(t.getId())));
        }
        operations.remove(queryList);
        operations.execute();
    }
	
	// 根据Id单个删除
    public void removeById(ID id, Class<T> tClass, String database){
        getTemplate(database).remove(Query.query(Criteria.where(_id).is(id)), tClass);
    }
	
	// 根据Id批量删除,适用于未查询就删除的使用场景
    public void removeById(List<ID> list, Class<T> tClass, String database){
        BulkOperations operations = getTemplate(database).bulkOps(BulkOperations.BulkMode.UNORDERED, tClass);
        List<Query> queryList = new LinkedList<>();
        for (ID id : list) {
            queryList.add(Query.query(Criteria.where(_id).is(id)));
        }
        operations.remove(queryList);
        operations.execute();
    }
	
	// 根据Id 批量查询
    public List<T> findById(List<ID> list, Class<T> tClass, String database){
        return getTemplate(database).find(Query.query(Criteria.where(_id).in(list)), tClass);
    }
}

以上api是MongoTemplate没有提供的,定义为持久层通用类后,其他持久类只需继承它即可。列如:

@Repository
public class UserDao extends MongoBaseDao<User, String> {
}

你没看错,其他持久类一般不用定义任何方法,因为MongoTemplate提供的api与MongoBaseDao提供的api已基本够用。
如果要使用MongoTemplate原生的api,使用userDao.getTemplate(database).remove(Object)等。
如果要使用MongoBaseDao自定义api,使用userDao.remove(list, Object.class, database)等。

你可能会说,要是我的业务非常复杂,需要一些其他的Dao方法呢?这个时候的确可以在诸如UserDao的持久类中定义一些其他的方法。但个人建议,既然是业务的复杂度导致的问题,新的方法应该定义在Service层而不是Dao层,Dao层只负责执行增删改查,不负责处理复杂业务,持久类可以继续保持空类状态,业务处理逻辑交给业务类。

类似的,实体类也可以定义一个通用接口。你也许已经注意到MongoBaseDao调用了BaseEntity,它用于统一约定实体类主键字段,从而支持泛型对象获取主键值,即支持批量删除方法中调用t.getId()的实现。
如下,通用实体接口BaseEntity,以及它的实现类User。

public interface BaseEntity<ID> {
    ID getId();
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable, BaseEntity<String> {
    @Id
    private String id;
    private String name;
    private Integer age;
}

最后,给出MongoClient的配置。你肯定已经注意到MongoBaseDao支持database数据库名传参获取对应的MongoTemplate对象。使用下面的配置类,SpringBoot中央配置文件便可以不用配置任何参数,实现数据库切换操作。当然,前提是你的Mongo数据库提前做好了权限设置。最简单的方式便是禁用MongoDB权限认证,具体设置方式因MongoDB安装方式的不同而不同,此处不再赘述。

@Configuration
public class MongoConfig {
    @Bean
    public MongoClient mongoClient() {
        return MongoClients.create("mongodb://localhost:27017");
    }
}
<think>好的,用户想了解MongoDB中BulkOperations的用法和示例。首先,我需要回忆一下Bulk Operations的基本概念,它们主要用于批量执行多个写操作,提高效率。根据用户提供的引用,特别是引用[3],里面提到了使用initializeOrderedBulkOp和initializeUnorderedBulkOp来实例化构建器,这应该是关键点。 接下来,要区分有序和无序批量操作的区别。有序操作会按顺序执行,遇到错误会停止;而无序操作会继续执行其他操作,最后报告错误。这在示例代码中可能需要体现出来。 然后,用户可能需要具体的操作示例,比如插入、更新、删除等。根据引用[1],db.collection.insert()可以插入单条或多条数据,但BulkOperations更适用于混合操作。引用[4]中的updateMany和$unset的例子可以用来构造批量更新操作。而引用[5]提到的条件构建问题,需要注意在Criteria中使用andOperator,这可能与Bulk操作中的查询条件有关。 另外,用户可能需要知道如何在Spring Data MongoDB中使用BulkOperations,比如使用BulkOperations接口和BulkMode。需要结合引用[2]中的聚合示例,不过聚合可能不直接相关,但可以联想到其他操作如插入、更新等。 需要注意的是,在Spring Boot中,BulkOperations的使用方式可能与原生MongoDB的语法不同,比如通过模板来获取BulkOperations实例,并添加多个操作后执行。同时,要提醒用户注意性能优化,比如批量操作的大小限制,错误处理方式,以及有序无序的选择场景。 最后,用户可能遇到的问题包括如何正确构建查询条件,特别是使用andOperator而不是链式ne()调用,这点在引用[5]中已经提到,需要特别强调避免错误用法。 总结回答结构:先介绍BulkOperations的作用,然后分点讲解使用方法,包括有序和无序的区别,示例代码(原生和Spring Data),最后给出注意事项和常见问题解决。</think>### MongoDB BulkOperations 使用方法详解 #### **1. BulkOperations 核心概念** BulkOperations 是 MongoDB 用于批量执行**插入、更新、删除**等写操作的高效工具,适用于需要一次性提交多个操作的场景,相比单条操作可减少网络开销[^3]。分两种模式: - **有序批量操作**:按添加顺序执行,遇到错误立即停止(`initializeOrderedBulkOp()`) - **无序批量操作**:并行执行所有操作,最后汇总错误(`initializeUnorderedBulkOp()`) --- #### **2. 原生 MongoDB 语法示例** ```javascript // 初始化有序批量操作 var bulk = db.users.initializeOrderedBulkOp(); // 添加操作:插入文档 bulk.insert({name: "张三", age: 25}); // 添加操作:更新条件(age>20的文档增加score字段) bulk.find({age: {$gt: 20}}).update({$set: {score: 100}}); // 添加操作:删除name为李四的文档 bulk.find({name: "李四"}).remove(); // 执行批量操作(返回执行结果) bulk.execute(); ``` *注意:无序操作将`initializeOrderedBulkOp`替换为`initializeUnorderedBulkOp`* --- #### **3. Spring Data MongoDB 集成示例** 在 Spring Boot 中通过 `MongoTemplate` 操作: ```java BulkOperations bulkOps = mongoTemplate.bulkOps(BulkMode.ORDERED, "users"); // 批量插入 bulkOps.insert(new User("王五", 30)); bulkOps.insert(new User("赵六", 28)); // 批量更新:将age<30的文档status设为"active" Query query = new Query(Criteria.where("age").lt(30)); Update update = new Update().set("status", "active"); bulkOps.updateMulti(query, update); // 批量删除:name为陈七的文档 bulkOps.remove(new Query(Criteria.where("name").is("陈七"))); // 执行操作(返回BulkWriteResult) BulkWriteResult result = bulkOps.execute(); ``` --- #### **4. 关键注意事项** 1. **性能优化**: - 单次批量操作建议控制在 **1000条以内**,避免内存溢出 - 使用无序模式(`UNORDERED`)时吞吐量更高,但无法保证操作顺序[^3] 2. **条件构建陷阱**: ```java // 错误:链式调用.ne()会导致条件覆盖 Criteria.where("classification_id").ne(null).ne(""); // 正确:使用andOperator合并条件[^5] Criteria.andOperator( Criteria.where("classification_id").ne(null), Criteria.where("classification_id").ne("") ); ``` 3. **内嵌文档操作**: ```java // 删除address.province字段(参考内嵌文档更新语法[^4]) Update update = new Update().unset("address.province"); bulkOps.updateMulti(new Query(), update); ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值