1:MongoDB-maven仓库

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

2:配置文件application.properties添加MongoDB配置

#MongoDB
spring.data.mongodb.uri=mongodb://127.0.0.1:27017/test-MongoDB

3:通过MongoTemplate 简单的实现 增删改


import com.alibaba.fastjson.JSON;
import com.base.utils.AssertUtils;
import com.base.dto.other.BaseMongoUpdateDTO;

import com.base.util.ObjectParseUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.mongodb.core.BulkOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Objects;

/**
 * author : wb
 * File Name: GenericMongoCrudApplication
 * Package Name: com.base.domain.mongo
 * Date: 2022/5/16 13:35
 * Copyright (c) 2022,All Rights Reserved.
 */
@Service
public class GenericMongoCrudApplication {

    private Logger logger = LoggerFactory.getLogger(GenericMongoCrudApplication.class);

    private MongoTemplate mongoTemplate;

    public GenericMongoCrudApplication(MongoTemplate mongoTemplate){
        this.mongoTemplate = mongoTemplate;
    }

    /**
     * @description: 根据ID修改数据
     * @param:
     * @return:
     **/
    public void modifyDataAccordingToId(BaseMongoUpdateDTO updateDTO){

        logger.info("变更数据入参:{}", JSON.toJSONString(updateDTO));

        String primaryKey =  ObjectParseUtils.parsePrimaryKey(updateDTO.getUpdateObj());
        AssertUtils.hasText(primaryKey,"缺少变更数据唯一标识");

        Object primaryVal = ObjectParseUtils.getFieldValueByName(primaryKey,updateDTO.getUpdateObj());
        AssertUtils.notNull(primaryVal,"缺少变更数据唯一标识");

        Query query = new Query();
        query.addCriteria(Criteria.where(primaryKey).is(primaryVal));
        logger.info("变更数据Query:{}", JSON.toJSONString(query));

        Update update = new Update();
        if(Objects.nonNull(updateDTO.getIncObj())){
            List<String> attrs = ObjectParseUtils.getFiledName(updateDTO.getIncObj());
            attrs.forEach(key->{
                Object obj =  ObjectParseUtils.getFieldValueByName(key,updateDTO.getIncObj());
                if(Objects.nonNull(obj)){
                    Integer num = (Integer)obj;
                    update.inc(key, num == 0 ? 1 : num);
                }
            });

        }

        if(Objects.nonNull(updateDTO.getUpdateObj())){
            List<String> attrs = ObjectParseUtils.getFiledName(updateDTO.getUpdateObj());
            attrs.forEach(key->{
                Object obj =  ObjectParseUtils.getFieldValueByName(key,updateDTO.getUpdateObj());
                String val  = JSON.toJSONString(obj);
                if(Objects.nonNull(obj) && !"null".equals(val)
                        && StringUtils.isNotEmpty(val)){
                    update.set(key, obj);
                }
            });
        }

        logger.info("变更数据Update:{}", JSON.toJSONString(update));
        this.mongoTemplate.updateFirst(query, update, updateDTO.getClazz());
    }

    /**
     * @description: 插入数据
     * @param:
     * @return:
     **/
    public void insertData(Object clazz) {
        logger.info("插入数据:{}",JSON.toJSONString(clazz));
        this.mongoTemplate.insert(clazz);
    }

    /**
     * @description: 删除消息数据
     * @param:  id
     **/
    public void delete(Query query,Class<?> clss) {
        this.mongoTemplate.remove(query,clss);
    }

    /**
     * @description 批量插入
     * @param list
     * @param clazz
     */
    public void batchInsert(List<?> list, Class<?> clazz){
        this.mongoTemplate.bulkOps(BulkOperations.BulkMode.UNORDERED,  clazz).insert(list).execute();
    }
}

4:调用封装简陋的通用类进行 增删改

@SpringBootTest
@RunWith(SpringRunner.class)
public class FailMqRecordApplicationTest {

    @Autowired
    private GenericMongoCrudApplication genericMongoCrudApplication;

    /**
     * 新增
     */
     @Test
    public void  testAdd(){
       MessageNoticePO po = new MessageNoticePO();
       //po.set   省略N步set操作 
       this.genericMongoCrudApplication.insertData(po);
    }
    

    /**
     * 批量新增
     */
     @Test
    public void  testBatchAdd(){
       List<MessageNoticePO> list = new ArrayList();
       //po.set   省略N步set操作 
       this.genericMongoCrudApplication.batchInsert(list,MessageNoticePO.class);
    }
    
     /**
      * 删除 
      */
     @Test
    public void delete(){
        Query query = new Query();
        Criteria criteria = new Criteria();
        criteria.and("_id").is(111);
        query.addCriteria(criteria);
        this.genericMongoCrudApplication.delete(query,MessageNoticePO.class);
    }

   /**
      * 修改
      */
     @Test
  public boolean modifyData(){
        BaseMongoUpdateDTO updateDTO = new BaseMongoUpdateDTO();
        updateDTO.setClazz(MessageNoticePO.class);
        MessageNoticePO no = new MessageNoticePO();
        no.setId(111);
        //省略N步要编辑的set操作
        updateDTO.setUpdateObj(no);
        updateDTO.setIncObj(new MessageNoticePO(retryCount));
        this.genericMongoCrudApplication.modifyDataAccordingToId(updateDTO);
    }

}

5:操作新增改的对象


import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.redis.core.index.Indexed;

import java.io.Serializable;
import java.util.Date;

/**
 * author : wb
 * File Name: MessageNoticePO
 * Package Name: com.base.domain.po.mongo
 * Date: 2022/5/18 13:43
 * Copyright (c) 2022,All Rights Reserved.
 */
@Document(collection = "message_notice")
public class MessageNoticePO implements Serializable {

    /**
     * '主键'
     */
    @Id
    private Long messageId;

    /**
     * 消息分组ID 
     **/
    @Field
    @Indexed
    private Long messageGroupId;

    //省略N个字段 以及set  get 
}

6:统一修改的入参对象


import java.io.Serializable;

/**
 * author : wb
 * File Name: BaseMongoUpdateDTO
 * Package Name: com.base.dto.other
 * Date: 2022/5/16 13:38
 * Copyright (c) 2022,All Rights Reserved.
 */
public class BaseMongoUpdateDTO<T> implements Serializable {

    /**
     *  要修改的属性+值
     */
    private T updateObj;

    /**
     *  要修改的具体操作对象
     */
    private Class<?> clazz;

    /**
     * 需要特殊操作的
     * 实现数字修改,添加or减少
     * 例如 原本数据库是 1  这里传递 表字段  num = 2 表示当前字段取数据库原有的自动+2
     */
    private T incObj;


    public Class<?> getClazz() {
        return clazz;
    }

    public void setClazz(Class<?> clazz) {
        this.clazz = clazz;
    }

    public T getUpdateObj() {
        return updateObj;
    }

    public void setUpdateObj(T updateObj) {
        this.updateObj = updateObj;
    }

    public T getIncObj() {
        return incObj;
    }

    public void setIncObj(T incObj) {
        this.incObj = incObj;
    }
}

7:查询实现简单简单阐述一下 MongoDB 对于 > < null  not null的实现

mongoDB大于小于符号对应:

> 大于 $gt
< 小于 $lt
>= 大于等于 $gte
<= 小于等于 $lte

要查询同一个时间多个约束可能出现的error:

org.springframework.data.mongodb.InvalidMongoDbApiUsageException:
Due to limitations of the com.mongodb.BasicDocument, you can't add a second 'createdDate' expression specified as 'createdDate:
Document{{$lt=2018-01-06}}'. Criteria already contains 'createdDate: Document{{$gte=2017-12-31}}'.

解决办法:
要查询同一个字段多个约束需要用andOperator:

Query query = new Query(
Criteria.where("ip").is(ip)
.andOperator(
Criteria.where("createdDate").lt(endDate),
Criteria.where("createdDate").gte(startDate)));

MongoDB中的null 和not null
 
1、查询全部数据
> db.foo.find()
{ "_id" : ObjectId("544db3565d92133398a80daa"), "a" : 1, "name" :null}
{ "_id" : ObjectId("544db3565d92133398a80daa"), "a" : 1, "name" : "ZZZZZ" }
{ "_id" : ObjectId("5448ac1d735969c5f8386958"), "a" : 4 }
{ "_id" : ObjectId("544854ee3966c0424b50b46d"), "b" : 2 }
{ "_id" : ObjectId("544855ce735969c5f8386952"), "pwd" : "1212", "username" : "zhangsan
{ "_id" : ObjectId("544855e8735969c5f8386953"), "username" : "lisi", "pwd" : "222" }
{ "_id" : ObjectId("5448ae4c735969c5f838695d"), "username" : "tom", "age" : 23 }



2、查询name值为null
查询name为null,会查询出name字段不存的数据,如下:

> db.foo.find({name:{$in:[null]}})

{ "_id" : ObjectId("544db3565d92133398a80daa"), "a" : 1, "name" : null }
{ "_id" : ObjectId("5448ac1d735969c5f8386958"), "a" : 4 }
{ "_id" : ObjectId("544854ee3966c0424b50b46d"), "b" : 2 }
{ "_id" : ObjectId("544855ce735969c5f8386952"), "pwd" : "1212", "username" : "zhangsan
{ "_id" : ObjectId("544855e8735969c5f8386953"), "username" : "lisi", "pwd" : "222" }

查询也可用:> db.foo.find({name:null})

除第一条记录外,其它记录不存在name字段


3、name字段加上 $exists:true
> db.foo.find({name:{$in:[null],$exists:true}})
{ "_id" : ObjectId("544db3565d92133398a80daa"), "a" : 1, "name" :null}

4、查询name为不为空时(not null )
> db.foo.find({name:{$ne:null}})
{ "_id" : ObjectId("544db3b45d92133398a80dab"), "a" : 1, "name" : "zzz" }

8:简陋封装的通用查询类


import com.alibaba.fastjson.JSON;
import com.base.search.Pager;
import com.base.dto.other.PageListDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.List;


/**
 * author : wb
 * File Name: GenericMongoQuery 
 * Package Name: com.base.domain.mongo
 * Date: 2022/5/17 15:24
 * Copyright (c) 2022,All Rights Reserved.
 */
@Service
public class GenericMongoQuery {

    private Logger logger = LoggerFactory.getLogger(GenericMongoQuery.class);

    private MongoTemplate mongoTemplate;

    public GenericMongoQuery(MongoTemplate mongoTemplate){
        this.mongoTemplate = mongoTemplate;
    }

    /**
     * @description: 根据主键id查询返回
     * @param
     **/
    public <T> T getOne(Long id,Class<T> returnClszz){
      return this.mongoTemplate.findById(id, returnClszz);
    }

    /**
     * @description: 根据主键query查询返回
     * @param
     **/
    public <T> T queryOne(Query query,Class<T> returnClszz){
        return this.mongoTemplate.findOne(query, returnClszz);
    }

    /**
     * @description: 根据主键query查询返回
     * @param
     **/
    public <T> List<T> findList(Query query,Class<T> returnClszz){
        return this.mongoTemplate.find(query,returnClszz);
    }

    /**
     * @description: 分页查询
     * @param query   mongo查询query
     * @param returnClszz  查询返回对象
     **/
    public <T> PageListDTO<T> page(Query query,
                                   Integer pageNum,
                                   Integer pageSize,
                                   Class<T> returnClszz) {

        if(pageNum != null && pageSize != null && pageSize > 0){
            //设置起始数
            query.skip((pageNum - 1) * pageSize);
            //设置查询条数
            query.limit(pageSize);
        }

        logger.info("mongo page param:{}", JSON.toJSONString(query));
        List<T> list = this.mongoTemplate.find(query,returnClszz);
        int count = count(query,returnClszz);

        PageListDTO<T> pageList = new PageListDTO();
        if(!CollectionUtils.isEmpty(list)){
            pageList.setData(list);
        }

        Pager pager = new Pager();
        pager.setRecordCount(count);
        pager.setCurrentPage(pageNum);
        pager.setPageSize(pageSize);

        if(pageSize != null && pageSize > 0){
            pager.setTotalPage(count % pageSize == 0 ? 1 : count / pageSize + 1);
        }
        pageList.setPager(pager);

        return pageList;
    }

    /**
     * @description:  统计总条数
     * @param query
     * @param collectionClass
     **/
    public Integer count(Query query, Class<?> collectionClass){
        logger.info("count param:{}",JSON.toJSONString(query));
        return (int) this.mongoTemplate.count(query, collectionClass);
    }

}

9:测试调用

@SpringBootTest
@RunWith(SpringRunner.class)
public class FailMqRecordApplicationTest {

    
    @Autowired
    private GenericMongoQuery genericMongoQuery;


  /**
   * 按ID查询
   */
  @Test
  public MessageNoticePO getIdMessageNotice() {
        return  this.genericMongoQuery.getOne(11, MessageNoticePO.class);
  }

  /**
   * 按对象查询
   */
  @Test
  public MessageNoticePO queryMessageNotice() {
     Query query = new Query();    query.addCriteria(Criteria.where(ObjectParseUtils.getBeanFunName(MessageNoticeQuery::getMessageId)).is(messageId));
        return  this.genericMongoQuery.queryOne(query, MessageNoticePO.class);
  }  


 /**
   * 按传递的状态in + 名字查询
   */
  @Test
 public List<MessageNoticePO> queryMessageNoticeByQueueName(String queueName){
        Query query = new Query();
        ArrayList<Integer> states = new ArrayList<>();
        states.add(1);
        states.add(2);
        query.addCriteria(Criteria.where(ObjectParseUtils.getBeanFunName(MessageNoticeQuery::getQueueName)).is(queueName)             .and(ObjectParseUtils.getBeanFunName(MessageNoticeQuery::getState)).in(states));
    return this.genericMongoQuery.findList(query,MessageNoticePO.class);
 }

 /**
   * 查询指定属性是null  并且状态是指定值的
   */
@Test
public List<MessageNoticePO> queryMessageNoticeList(){
        Query query = new Query();
        Object obj = null;
        Criteria criteria = new Criteria();
        criteria.andOperator(new Criteria()
                .andOperator( Criteria.where(ObjectParseUtils.getBeanFunName(MessageNoticeQuery::getMessageId)).in(obj), Criteria.where(ObjectParseUtils.getBeanFunName(MessageNoticeQuery::getRetryCount)).in(obj)));
        criteria.and(ObjectParseUtils.getBeanFunName(MessageNoticeQuery::getState)).is(1);
        query.addCriteria(criteria);
   return this.genericMongoQuery.findList(query,MessageNoticePO.class);
 }   
 
 /**
   * 分页查询
   */
@Test
  public PageList<MessageNoticePO> queryMessageNoticePageList(MessageNoticeQuery recordQuery){
        Query query =  new Query();

        Criteria criteria = new Criteria();
        if(recordQuery.isMessageIdIsNotNull()){
            String messageId =  ObjectParseUtils.getBeanFunName(MessageNoticeQuery::getMessageId);
            criteria.andOperator(
                    Criteria.where(messageId).exists(true),
                    Criteria.where(messageId).ne(true));
        }

        if(recordQuery.getState() != null){
            criteria.and(ObjectParseUtils.getBeanFunName(MessageNoticeQuery::getState)).is(recordQuery.getState());
        }

        if(recordQuery.getRequestType() != null){
            criteria.and(ObjectParseUtils.getBeanFunName(MessageNoticeQuery::getRequestType)).is(recordQuery.getRequestType());
        }

        if(StringUtils.isNotEmpty(recordQuery.getQueueName())){
            Pattern pattern = Pattern.compile("^.*" + recordQuery.getQueueName() + ".*$", Pattern.CASE_INSENSITIVE);
            criteria.and(ObjectParseUtils.getBeanFunName(MessageNoticeQuery::getQueueName)).regex(pattern);
        }

        if(StringUtils.isNotEmpty(recordQuery.getRequestKey())){
            Pattern pattern = Pattern.compile("^.*" + recordQuery.getRequestKey() + ".*$", Pattern.CASE_INSENSITIVE);
            criteria.and(ObjectParseUtils.getBeanFunName(MessageNoticeQuery::getRequestKey)).regex(pattern);
        }

        query.addCriteria(criteria);

        if(recordQuery.isCreateTimeSort()){
            query.with(new Sort(Sort.Direction.DESC, ObjectParseUtils.getBeanFunName(MessageNoticeQuery::getCreateTime)));
        }

        if(recordQuery.isUpdateTimeSort()){
            query.with(new Sort(Sort.Direction.DESC, ObjectParseUtils.getBeanFunName(MessageNoticeQuery::getLastUpdateTime)));
        }

        PageListDTO<MessageNoticePO> list = this.genericMongoQuery.page(query,
                recordQuery.getPageNum(),
                recordQuery.getPageSize(),
                IFailMqRecordPO.class);
        return 转换通用查询返回的分页数据给业务调用方(list);
    }  
}

10:新增改查都用的ObjectParseUtils 工具类


import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;

/**
 * author : wb
 * File Name: ObjectParseUtils
 * Package Name: com.base.util
 * Date: 2022/5/16 15:06
 * Copyright (c) 2022,All Rights Reserved.
 */
public class ObjectParseUtils {

    /**
     * @description: 解析主鍵
     **/
    public static String parsePrimaryKey(Object o){
        try{
            Field[] fields = o.getClass().getDeclaredFields();
            String primaryKey = "";
            cas:for(Field fl:fields){
                for(Annotation a :fl.getAnnotations()){
                   if(a instanceof org.springframework.data.annotation.Id){
                       primaryKey += fl.getName();
                       break cas;
                   }
                }
            }
            return primaryKey;
        }catch (Exception e){
           throw new BusinessValidateException("数据操作失败");
        }
    }


    /**
     * @description: 解析对象的属性
     * @param:  o
     * @return:  list
     **/
    public static List<String> getFiledName(Object o) {
        try{
            Field[] fields = o.getClass().getDeclaredFields();
            String[] fieldNames = new String[fields.length];
            for (int i = 0; i < fields.length; i++) {
                fieldNames[i] = fields[i].getName();
            }
            return Arrays.asList(fieldNames);
        }catch (Exception e){
            return new ArrayList<>();
        }
    }

    /**
     * @description:  解析对象属性对应的值
     * @param:  fieldNmae
     * @param:  o
     * @return:  object
     **/
    public static Object getFieldValueByName(String fieldName, Object o) {
        try {
            Method method = getFieldMethodByName(fieldName,o);
            if(method == null){
                throw new BusinessValidateException("操作异常");
            }

            return method.invoke(o, new Object[] {});
        } catch (Exception e) {
            throw new BusinessValidateException("操作异常");
        }
    }

    /**
     * 返回方法体
     */
    private static Method getFieldMethodByName(String fieldName, Object o){
        try{
            String firstLetter = fieldName.substring(0, 1).toUpperCase();
            String getter = "get" + firstLetter + fieldName.substring(1);
            return o.getClass().getMethod(getter, new Class[] {});
        }catch (Exception e){
            return null;
        }
    }

    /**
     * @description:  解析对象属性对应的值
     * @param:  fieldNmae
     * @param:  o
     * @return:  object
     **/
    public static Integer getFieldIntValueByName(String fieldName, Object o) {
        Integer num = null;
        try {
            Method method = getFieldMethodByName(fieldName,o);
            if(method != null){
                num = (Integer)method.invoke(o, new Object[] {});
            }
        } catch (Exception e) {
        }
        return num;
    }

    /**
     * 根据匿名函数,获取bean 属性名称 <br>
     * <li>BeanMapUtils.getBeanFunName(UserInfoDO::getCreateTime) = createTime</li>
     * <li>BeanMapUtils.getBeanFunName(UserInfoDO::isOk) =  ok</li>
     *
     * @date 2021-06-04 15:54
     */
    public static <T> String getBeanFunName(Property<T, ?> property) {
        try {
            Method declaredMethod = property.getClass().getDeclaredMethod("writeReplace");
            declaredMethod.setAccessible(Boolean.TRUE);
            SerializedLambda serializedLambda = (SerializedLambda) declaredMethod.invoke(property);
            String method = serializedLambda.getImplMethodName();

            String attr;
            if (method.startsWith("get")) {
                attr = (char) (method.charAt(3) + 32) + method.substring(4);
            } else if (method.startsWith("is")) {
                attr = (char) (method.charAt(2) + 32) + method.substring(3);
            } else {
                throw new IllegalStateException("无法处理的方法名" + method);
            }
            return attr;
        } catch (ReflectiveOperationException e) {
            throw new RuntimeException(e);
        }
    }

    public interface Property<T, R> extends Function<T, R>, Serializable {
    }

}

11:查询入参对象


import java.io.Serializable;
import java.util.Date;


public class MessageRecordQuery implements Serializable {
    
    private String id;

    private String userId;
   
    private String queueName;
   
    private Byte requestType;
    
    private String requestKey;

    private Byte state;

    /**
     * messageId 是否拼接Mongo $ne
     */
    private boolean messageIdIsNotNull;

    private boolean createTimeSort;

    private boolean updateTimeSort;

    private String messageId;

    private Date lastUpdateTime;
   
    private Date createTime;

    private Integer retryCount;
    
    /**
     * 页码.
     */
    private Integer pageNum;

    /**
     * 每页大小.
     */
    private Integer pageSize;

    /**
     * 排序条件.
     */
    private String orderBy;
  
    //省略N set get
}

12:分页用到的 PageListDTO


import com.base.search.Pager;

import java.io.Serializable;
import java.util.List;

/**
 * author : wb
 * File Name: PageListDTO
 * Package Name: com.base.dto.other
 * Date: 2022/5/17 15:59
 * Copyright (c) 2022,All Rights Reserved.
 */
public class PageListDTO<T> implements Serializable {

    public List<T> data;

   public Pager pager;

    public List<T> getData() {
        return data;
    }

    public void setData(List<T> data) {
        this.data = data;
    }

    public Pager getPager() {
        return pager;
    }

    public void setPager(Pager pager) {
        this.pager = pager;
    }
}

13: 分页用到的Pager


/**
 * 分页信息,主要用于查询返回的对象.
 *
 */
public class Pager extends PagerCondition {

    /**
     * serialVersionUID.
     */
    private static final long serialVersionUID = 1L;

    /**
     * 总记录数
     */
    private Integer recordCount;

    /**
     * 总页数
     */
    private Integer totalPage;

    public Pager() {
        super();
    }

    public Pager(Integer currentPage, Integer pageSize, Integer recordCount) {
        super(currentPage, pageSize);
        this.recordCount = recordCount;
        Integer totalPageSize = recordCount / pageSize;
        Integer remailder = recordCount % pageSize;
        // 如果总记录数与每页显示条数的余数大于0,总页数加1
        if (remailder > 0) {
            totalPageSize = totalPageSize + 1;
        }
        totalPage = totalPageSize;

    }

    /**
     * 分页查询时使用
     */
    public Pager(PagerCondition pageCondition, Integer recordCount) {
        setCurrentPage(pageCondition.getCurrentPage());
        setPageSize(pageCondition.getPageSize());
        setRecordCount(recordCount);
        setTotalPage((recordCount + getPageSize() - 1) / getPageSize());
    }

    public Integer getRecordCount() {
        return recordCount;
    }

    public void setRecordCount(Integer recordCount) {
        this.recordCount = recordCount;
    }

    public Integer getTotalPage() {
        return totalPage;
    }

    public void setTotalPage(Integer totalPage) {
        this.totalPage = totalPage;
    }
}

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐