MongoDB Helper

1,003 阅读6分钟

MongoDB Helper

上一篇的文章连接:Spring MongoDB工具类 - 掘金 (juejin.cn)

这个工具类的想法呢,就是能够根据对象的属性相关注解直接构造成最终一个完整的条件。而不是原始的那样要写很多条件代码。

当初写的时候呢,只是为了简单的满足一下业务需求,能够方便的更好操作MongoDB相关业务,就写了上一篇文章,然后这一年时间陆陆续续也接收到了许多小伙伴的好友请求需要这个工具类的源码,我也放在了github上面,github.com/sifan-hongc…

由于一直在忙工作上的事,也没有时间对这个小工具类进行优化,今天晚上呢,就折腾出一点时间来重构了。

之前的handler主要集内部的两个Enum上来获取,小伙伴们估计修改起来也麻烦,就把他重构成一个register的形式来使用。能够更好的添加相关注解的handler。由于MongoDB也不是很太熟悉,目前就只有下面的2个相关注解的register类。一个对key构造成条件的注解,一个是运算符的注解。

package com.hongcha.mongodb.core;

import java.lang.annotation.Annotation;
import java.util.Map;

/**
 * 条件相关注解的处理器注册
 */
public interface ConditionsAnnotationHandlerRegister {
    /**
     * Annotation 必须标准ConditionsAnnotation注解
     *
     * @param annotation
     * @param conditionsAnnotationHandler
     */
    void registerHandler(Class<? extends Annotation> annotation, ConditionsAnnotationHandler conditionsAnnotationHandler);

    /**
     * 返回的是一个copy,不是原注册Map
     *
     * @return
     */
    Map<Class<? extends Annotation>, ConditionsAnnotationHandler> getAllRegisteredHandler();

    ConditionsAnnotationHandler getHandler(Class<? extends Annotation> annotation);

}
package com.hongcha.mongodb.core;

import java.lang.annotation.Annotation;
import java.util.Map;

/**
 * 注册运算符相关注解的处理器注册
 */
public interface OperatorAnnotationHandlerRegister {
    /**
     * Annotation 必须标准OperatorAnnotation注解
     *
     * @param annotation
     * @param operatorAnnotationHandler
     */
    void registerHandler(Class<? extends Annotation> annotation, OperatorAnnotationHandler operatorAnnotationHandler);

    /**
     * 返回的是一个copy,不是原注册Map
     * @return
     */
    Map<Class<? extends Annotation>, OperatorAnnotationHandler> getAllRegisteredHandler();

    OperatorAnnotationHandler getHandler(Class<? extends Annotation> annotation);
}

也提供BaseService,能够快速的对单表进行操作

package com.hongcha.mongodb.core.service;

import com.hongcha.mongodb.core.Page;
import com.hongcha.mongodb.core.annotation.ConditionsAnnotation;
import com.hongcha.mongodb.core.annotation.OperatorAnnotation;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;

import java.util.List;

/**
 * mongodb的基础service接口
 * T 对应集合实体类
 * 根据ObjectParam构成条件的参数参考下面的注解以及标注
 *
 * @see ConditionsAnnotation
 * @see OperatorAnnotation
 */
public interface BaseMongoService<T> {
    /**
     * 根据id获取数据
     * @param id id
     * @return
     */
    T getById(String id);


    /**
     * 检查id是否存在,如果不存在那么就会抛出异常,
     *
     * @param id      id
     * @param message 提示信息
     * @return
     */
    T checkByIdAngGet(String id, String message);

    /**
     * 插入实体类到db
     *
     * @param t
     * @return
     */
    boolean insert(T t);


    /**
     * 往对应的集合中批量插入数据,注意批量的数据中不要包含重复的id
     *
     * @param list
     * @return
     */
    boolean insertBatch(List<T> list);


    /**
     * 根据id删除数据
     *
     * @param id
     * @return
     */

    boolean deleteById(String id);

    /**
     * 根据object构造条件删除数据
     *
     * @param objectParam
     * @param
     * @return
     */
    boolean delete(Object objectParam);

    /**
     * 根据原始Query删除
     *
     * @param query
     * @return
     */
    boolean delete(Query query);

    /**
     * 根据 object修改
     *
     * @param id
     * @param objectUpdate
     * @param
     * @return
     */
    boolean updateById(String id, Object objectUpdate);

    /**
     * 根据update修改
     *
     * @param id
     * @param update
     * @return
     */
    boolean updateById(String id, Update update);


    /**
     * @param objectParam  构成query的object
     * @param objectUpdate 构成update的object
     * @return
     */

    boolean update(Object objectParam, Object objectUpdate);

    /**
     * 根据原始Query和Update进行修改
     *
     * @param query
     * @param update
     * @return
     */

    boolean update(Query query, Update update);

    /**
     * 获取所有数据
     *
     * @return
     */
    List<T> select();

    /**
     * 根据对象生成条件查找符合条件的实体数据
     *
     * @param objectParam
     * @return
     */
    List<T> select(Object objectParam);


    /**
     * 根据对象来生产条件查找返回R类型数据
     *
     * @param objectParam
     * @param returnClass
     * @param <R>         接收的返回类型
     * @return
     */
    <R> List<R> select(Object objectParam, Class<R> returnClass);

    /**
     * 根据Query查找符合条件的数据
     *
     * @param query
     * @return
     */
    List<T> select(Query query);

    /**
     * 根据原生query来进行查找
     *
     * @param query
     * @param returnClass
     * @param <R>
     * @return
     */
    <R> List<R> select(Query query, Class<R> returnClass);


    T selectOne(Object objectParam);

    /**
     * 根据对象来生产条件查找返回R类型数据,获取第一个符合的
     *
     * @param objectParam
     * @param returnClass
     * @param <R>         接收的返回类型
     * @return
     */
    <R> R selectOne(Object objectParam, Class<R> returnClass);


    T selectOne(Query query);


    /**
     * 根据原生Query查找第一个
     *
     * @param query
     * @param returnClass
     * @param <R>
     * @return
     */
    <R> R selectOne(Query query, Class<R> returnClass);


    Page<T> page(Object objectParam, Page<T> page);


    /**
     * 分页方法,会将data填充到传入的page里面去
     *
     * @param objectParam 根据object构造条件
     * @param page        不能为空,调用前请填充好current和size
     * @param returnClass 返回的类型
     * @param <R>
     * @return
     * @see Page
     * @see Page#setCurrent(int)  当前页
     * @see Page#setSize(int)  条数
     */
    <R> Page<R> page(Object objectParam, Page<R> page, Class<R> returnClass);


    Page<T> page(Query query, Page<T> page);

    /**
     * 分页方法,会将data填充到传入的page里面去
     *
     * @param query       原生query构造,但是不要加skip和limit,该方法会自动判断
     * @param page        不能为空,调用前请填充好current和size
     * @param returnClass 返回的类型
     * @param <R>
     * @return
     * @see Page
     * @see Page#setCurrent(int)  当前页
     * @see Page#setSize(int)  条数
     */
    <R> Page<R> page(Query query, Page<R> page, Class<R> returnClass);


    long count();

    /**
     * 根据object生成条件查找count
     *
     * @param objectParam 条件
     * @return
     */
    long count(Object objectParam);

    /**
     * 根据Query生产count
     *
     * @param query
     * @return
     */
    long count(Query query);

    /**
     * 默认为T类型
     *
     * @param conditions
     * @param page
     * @return
     * @see #aggregatePage(List, Page, Class)
     */
    Page<T> aggregatePage(List<AggregationOperation> conditions, Page<T> page);

    /**
     * 不进行排序
     *
     * @param conditions
     * @param page
     * @param clazz
     * @param <R>
     * @return
     * @see #aggregatePage(List, Sort, Page, Class)
     */
    <R> Page<R> aggregatePage(List<AggregationOperation> conditions, Page<R> page, Class<R> clazz);

    /**
     * 对该集合的aggregate的聚合函数分页,conditions不要添加skip和limit以及sort,该方法会自动添加 page请先填充好current和size
     * 拿到的数据会填充到入参参数page的data中
     *
     * @param conditions
     * @param sort       排序 如果为null将不会添加
     * @param page
     * @param clazz      接受数据的类型
     * @return
     * @see Page
     * @see Page#setCurrent(int)  当前页
     * @see Page#setSize(int)  条数
     */
    <R> Page<R> aggregatePage(List<AggregationOperation> conditions, Sort sort, Page<R> page, Class<R> clazz);

    /**
     * 接受类型为T类型
     *
     * @param aggregation
     * @return
     * @see #aggregateData(Aggregation, Class)
     */
    List<T> aggregateData(Aggregation aggregation);

    /**
     * 对聚合函数返回的接受进行接受
     *
     * @param aggregation
     * @param outputType
     * @param <R>
     * @return
     * @see #aggregate(Aggregation, Class)
     */
    <R> List<R> aggregateData(Aggregation aggregation, Class<R> outputType);

    /**
     * 对BaseMongoServiceImpl的入参table进行聚合函数查找,返回AggregationResults
     *
     * @param aggregation
     * @param outputType  接受结果的类型
     * @param <R>
     * @return
     */
    <R> AggregationResults<R> aggregate(Aggregation aggregation, Class<R> outputType);


}

基本上BaseService覆盖了大部分简单的场景,也支持原生的写法。

目前支持的相关注解:

image.png

package com.hongcha.mongodb.starter;

import com.hongcha.mongodb.core.*;
import com.hongcha.mongodb.core.annotation.*;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.util.StringUtils;

import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;

@Configurable
public class MongoHelperAutoConfig implements InitializingBean {

    private static Map<Class<? extends Annotation>, ConditionsAnnotationHandler> DEFAULT_CONDITIONS = new HashMap<>();
    private static Map<Class<? extends Annotation>, OperatorAnnotationHandler> DEFAULT_OPERATOR = new HashMap<>();

    static {

        DEFAULT_CONDITIONS.put(Eq.class, (criteria, value) -> skipNullHandler(criteria, value, Criteria::is));
        DEFAULT_CONDITIONS.put(Gt.class, (criteria, value) -> skipNullHandler(criteria, value, Criteria::gt));
        DEFAULT_CONDITIONS.put(Gte.class, (criteria, value) -> skipNullHandler(criteria, value, Criteria::gte));
        DEFAULT_CONDITIONS.put(Lt.class, (criteria, value) -> skipNullHandler(criteria, value, Criteria::lt));
        DEFAULT_CONDITIONS.put(Lte.class, (criteria, value) -> skipNullHandler(criteria, value, Criteria::lte));
        DEFAULT_CONDITIONS.put(Ne.class, (criteria, value) -> skipNullHandler(criteria, value, Criteria::ne));
        DEFAULT_CONDITIONS.put(In.class, (criteria, value) -> skipNullHandler(criteria, value, Criteria::in));
        DEFAULT_CONDITIONS.put(Regex.class, (criteria, value) -> {
            if (value == null)
                return criteria;
            String strValue = value.toString();
            return skipNullHandler(criteria, strValue, Criteria::regex);
        });

    }

    static {

        DEFAULT_OPERATOR.put(AndOperator.class, ((criteriaLeft, criteriaRight) -> skipNullHandler(criteriaLeft, criteriaRight, Criteria::andOperator)));
        DEFAULT_OPERATOR.put(OrOperator.class, ((criteriaLeft, criteriaRight) -> skipNullHandler(criteriaLeft, criteriaRight, Criteria::orOperator)));
        DEFAULT_OPERATOR.put(NorOperator.class, ((criteriaLeft, criteriaRight) -> skipNullHandler(criteriaLeft, criteriaRight, Criteria::norOperator)));
    }

    @Autowired
    ConditionsAnnotationHandlerRegister conditionsAnnotationHandlerRegister;
    @Autowired
    OperatorAnnotationHandlerRegister operatorAnnotationHandlerRegister;

    private static <T> Criteria skipNullHandler(Criteria criteria, T value, BiFunction<Criteria, T, Criteria> biFunction) {
        if (value == null)
            return criteria;
        if (value instanceof String) {
            String s = (String) value;
            if (!StringUtils.hasText(s))
                return criteria;
        }
        if (value instanceof Collection) {
            Collection c = (Collection) value;
            if (c.isEmpty())
                return criteria;
        }
        return biFunction.apply(criteria, value);
    }

    @ConditionalOnMissingBean
    @Bean
    public ConditionsAnnotationHandlerRegister conditionsAnnotationHandlerRegister() {
        return new DefaultConditionsAnnotationHandlerRegister();
    }

    @ConditionalOnMissingBean
    @Bean
    public OperatorAnnotationHandlerRegister operatorAnnotationHandlerRegister() {
        return new DefaultOperatorAnnotationHandlerRegister();
    }

    @ConditionalOnMissingBean
    @Bean
    public MongoHelper mongoDBHelper() {
        return new MongoHelper();
    }


    @Override
    public void afterPropertiesSet() throws Exception {
        initConditionsAnnotationHandlerRegister(conditionsAnnotationHandlerRegister);
        initOperatorAnnotationHandlerRegister(operatorAnnotationHandlerRegister);
    }

    private void initOperatorAnnotationHandlerRegister(OperatorAnnotationHandlerRegister register) {
        DEFAULT_OPERATOR.forEach((annotation, handler) -> {
            if (register.getHandler(annotation) == null) {
                register.registerHandler(annotation, handler);
            }
        });
    }

    private void initConditionsAnnotationHandlerRegister(ConditionsAnnotationHandlerRegister register) {
        DEFAULT_CONDITIONS.forEach((annotation, handler) -> {
            if (register.getHandler(annotation) == null) {
                register.registerHandler(annotation, handler);
            }
        });
    }

}

如果想自定义添加或者覆盖,只需要引入相关register的bean来调用registerHandler即可扩展。

小伙伴们想使用的话,可以查看上一篇文章的demo内容,或者咨询我。

在使用中出现bug的话或者想增加新功能,也可微信联系我: 13480901614

觉得这个工具类不错的话,麻烦大家点个赞、GitHub点个星星。github.com/sifan-hongc…

谢谢观看!!!