利用MongoTemplate 简单实现MongoDB 增删改查

Posted wb54979

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了利用MongoTemplate 简单实现MongoDB 增删改查相关的知识,希望对你有一定的参考价值。

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;
    

以上是关于利用MongoTemplate 简单实现MongoDB 增删改查的主要内容,如果未能解决你的问题,请参考以下文章

利用MongoTemplate 简单实现MongoDB 增删改查

MongoDB Lifecycle 事件访问 MongoTemplate

MongoTemplate 排序不适用于 LocalDateTime

使用 mongoTemplate 在 spring-data-mongo Java 中进行 Mongo 聚合查询

spring-data-mongo的MongoTemplate开发

Mongodb的 mongoTemplate 内嵌文档怎么进行查询?