• spring mongo data api learn


    1 索引

    1.1 单列索引

    @Indexed
    @Field(value = "delete_flag")
    private Boolean deleteFlag = false;

    @Indexed属性:name定义索引名称、unique是否为唯一索引,默认false

    1.2 组合索引

    @Document(collection = "#{T(com.nd.social.common.handler.TenantHandler).getTablePrefix().concat('block_content')}")
    @CompoundIndexes(
            @CompoundIndex(name = "idx_bc_t_sc_s", def = "{'tenant':1,'scope.code':1,'sourceId':1}", unique = true)
    )
    @TypeAlias("BlockContent")
    
    @CompoundIndexes(
            @CompoundIndex(name = "_ui_s_df_idx_", def = "{'userId':1, 'status':1, 'deleteFlag':1}", unique = true)
    )

     2 注意

    在自定义接口实现中使用数据库中的字段名作为查询条件,而不是实体类的属性名

    如果进行+1操作 尽量使用inc 避免并发问题

    3 排序

        private static final Sort SORT_BY_CREATE_TIME_DESC =
                new Sort(Sort.Direction.DESC, "createAt");
        List<Idea> finByUserIdAndDeleteFlagFalse(String userId, Sort sort);
     命名查询:OrderBy...Desc/ASC
        List<Idea> findByUserIdAndStatusInAndViewAtLessThanAndDeleteFlagFalseOrderByCreateAtDesc(String userId, List<IdeaStatus> status, long viewAt);

    4 分页

    1 offset/limit

        private Pageable getPageable(SearchVo condition) {
            int limit = condition.getLimit();
            int offset = condition.getOffset();
            return new PageRequest(offset / limit, limit, SORT_BY_CREATE_TIME_DESC);
        }
        private int offset = 0;
        private int limit = 15;
    
        public int getOffset() {
            return offset;
        }
    
        public void setOffset(int offset) {
            this.offset = offset;
        }
    
        public int getLimit() {
            return limit <= 0 ? 15 : limit;
        }
    
        public void setLimit(int limit) {
            this.limit = limit;
        }

    2 page/size

    page 页码,请求第几页数据(默认值:1) 可选
    size 每页数量(默认值:30) 可选
    new PageRequest(page - 1, size)

    3 计算总页数

    pageVo.setTotalPage(size == 0 ? 1 : (int) Math.ceil((double) total / (double) size));  

    4 结果集示例

    public class PageVo<T> {
        // 总数
        private long totalCount;
        // 总页数
        private int totalPage;
        // 页码
        private int page;
        // 单页数量
        private int size;
        // 结果列表
        private List<T> items;
    
        public List<T> getItems() {
            return items;
        }
    
        public void setItems(List<T> items) {
            this.items = items;
        }
    
        public long getTotalCount() {
            return totalCount;
        }
    
        public void setTotalCount(long totalCount) {
            this.totalCount = totalCount;
        }
    
        public int getTotalPage() {
            return totalPage;
        }
    
        public void setTotalPage(int totalPage) {
            this.totalPage = totalPage;
        }
    
        public int getPage() {
            return page;
        }
    
        public void setPage(int page) {
            this.page = page;
        }
    
        public int getSize() {
            return size;
        }
    
        public void setSize(int size) {
            this.size = size;
        }
    }
    public class Items<T> {
    
        // 结果列表 可以是Set/List
        private Collection<T> items;
    
        // 如果不需要 可以设置为items的size值
        private long totalCount;
    
        public static <T> Items<T> of(Collection<T> list) {
            Items<T> items = new Items<>();
            items.setItems(list);
            items.setTotalCount(list.size());
            return items;
        }
    
        public static <T> Items<T> of(Collection<T> list, long totalCount) {
            Items<T> items = new Items<>();
            items.setItems(list);
            items.setTotalCount(totalCount);
            return items;
        }
    
        public Collection<T> getItems() {
            return items;
        }
    
        public void setItems(Collection<T> items) {
            this.items = items;
        }
    
        public long getTotalCount() {
            return totalCount;
        }
    
        public void setTotalCount(long totalCount) {
            this.totalCount = totalCount;
        }
    
    }

    5 打印mongo NoSql语句

    显示操作mongo的语句,log4j.properties里面加入:

    1 log4j.logger.org.springframework.data.mongodb.core=DEBUG, mongodb
    2 
    3 log4j.appender.mongodb=org.apache.log4j.ConsoleAppender
    4 log4j.appender.mongodb.Target=System.out
    5 log4j.appender.mongodb.Threshold=DEBUG
    6 log4j.appender.mongodb.ImmediateFlush=true
    7 log4j.appender.mongodb.layout=org.apache.log4j.PatternLayout
    8 log4j.appender.mongodb.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %5p %X{RequestId} - %m%n

    原因:
    在mongo的底层实现中,如MongoTemplate中,判断了是否日志级别为debug,是的时候会打印语句出来,例如

     1 private static final Logger LOGGER = LoggerFactory.getLogger(MongoTemplate.class);
     2 
     3 protected void executeQuery(Query query, String collectionName, DocumentCallbackHandler dch,
     4             CursorPreparer preparer) {
     5 
     6         Assert.notNull(query);
     7 
     8         DBObject queryObject = queryMapper.getMappedObject(query.getQueryObject(), null);
     9         DBObject sortObject = query.getSortObject();
    10         DBObject fieldsObject = query.getFieldsObject();
    11 
    12         if (LOGGER.isDebugEnabled()) {
    13             LOGGER.debug("Executing query: {} sort: {} fields: {} in collection: {}", serializeToJsonSafely(queryObject),
    14                     sortObject, fieldsObject, collectionName);
    15         }
    16 
    17         this.executeQueryInternal(new FindCallback(queryObject, fieldsObject), preparer, dch, collectionName);
    18 }

    6 注解查询

    1 一个方法命名查询中同一个属性不能出现2次 可以使用@Query注解查询
    2 @Query:
      value 查询语句
      count 作为统计的查询 返回int值
      delete 作为删除语句并返回删除后的文档集合
      fields 限定需要返回哪些字段

    示例:

    @Query(count = true, value = "{'$and':[{'tenant':?3},{'reportStatus':?0}," +
                " {'dealTime':{'$gte':?1}}, {'dealTime':{'$lte':?2}}]}")
    int countByStatusAndDealTimeBetween(ReportStatus status, Date begin, Date end, long tenant);
    
    @Query("{'$and':[{'userId':?0},{'deleteFlag':false}," +
            "{'$or':[{'content':{'$regex':?1}},{'location':{'$regex':?1}},{'createAtStr':{'$regex':?1}}]}]}")
    List<Idea> findByKeyWord(String userId, String key, Pageable pageable);
     1 {
     2     '$and': [
     3         {
     4             'userId': ?0
     5         },
     6         {
     7             'deleteFlag': false
     8         },
     9         {
    10             '$or': [
    11                 {
    12                     'content': {
    13                         '$regex': ?1
    14                     }
    15                 },
    16                 {
    17                     'location': {
    18                         '$regex': ?1
    19                     }
    20                 },
    21                 {
    22                     'createAtStr': {
    23                         '$regex': ?1
    24                     }
    25                 }
    26             ]
    27         }
    28     ]
    29 }

    7 MongoOptions/MongoTemplate

    public <T> T findOne(Query query, Class<T> entityClass) 
    public boolean exists(Query query, Class<?> entityClass)
    public <T> List<T> find(Query query, Class<T> entityClass)
    public <T> T findById(Object id, Class<T> entityClass)
    
    public <T> T findAndModify(Query query, Update update, Class<T> entityClass)
    public <T> T findAndModify(Query query, Update update, FindAndModifyOptions options, Class<T> entityClass)
    public class FindAndModifyOptions {
        boolean returnNew;   // 是否返回更新后的值
        boolean upsert;      // 没有找到是否插入 
        boolean remove;      // 找到是否删除
    }
    
    public <T> T findAndRemove(Query query, Class<T> entityClass)
    public long count(Query query, Class<?> entityClass) 
    
    public void insert(Object objectToSave) 
    public void insert(Collection<? extends Object> batchToSave, Class<?> entityClass)
    public void insertAll(Collection<? extends Object> objectsToSave)
    
    public void save(Object objectToSave) 保存/修改
    
    public WriteResult upsert(Query query, Update update, Class<?> entityClass) 
    public WriteResult updateFirst(Query query, Update update, Class<?> entityClass)
    public WriteResult updateMulti(Query query, Update update, Class<?> entityClass)
    
    public WriteResult remove(Object object)
    public WriteResult remove(Query query, String collectionName)
    
    
    public <T> List<T> findAll(Class<T> entityClass)
    public <T> List<T> findAllAndRemove(Query query, Class<T> entityClass)

      public DB getDb()
      DBCollection getCollection(String collectionName); DBCollection 中包含各种CRUD操作以及对集合本身的定义操作(索引、命名)
      public String getCollectionName(Class<?> entityClass)

    public <T> MapReduceResults<T> mapReduce(String inputCollectionName, String mapFunction, String reduceFunction, Class<T> entityClass)
    public <T> MapReduceResults<T> mapReduce(String inputCollectionName, String mapFunction, String reduceFunction,MapReduceOptions mapReduceOptions, Class<T> entityClass) 
    
    public <T> GroupByResults<T> group(String inputCollectionName, GroupBy groupBy, Class<T> entityClass)
    
    
    public <O> AggregationResults<O> aggregate(TypedAggregation<?> aggregation, Class<O> outputType)
    public <O> AggregationResults<O> aggregate(Aggregation aggregation, Class<?> inputType, Class<O> outputType)

    distinct方法:

     public List<String> distinctUserId() {
         return mongoTemplate.getCollection("ideas").distinct("user_id");
     }
    
     public List<String> distinctLocation(String userId) {
         DBObject query = Query.query(Criteria.where("user_id").is(userId)).getQueryObject();
         return mongoTemplate.getCollection("ideas").distinct("location", query);
     }

    Sort

    private final List<Order> orders;
    
    public Sort and(Sort sort) {
      if (sort == null) {
        return this;
      }
      ArrayList<Order> these = new ArrayList<Order>(this.orders);
      for (Order order : sort) {
         these.add(order);
      }
      return new Sort(these);
    }

    Query

     1     private Sort sort;
     2     private int skip;
     3     private int limit;
     4 
     5     public Query skip(int skip) {
     6         this.skip = skip;
     7         return this;
     8     }
     9 
    10     public Query limit(int limit) {
    11         this.limit = limit;
    12         return this;
    13     }
    14 
    15         public Query with(Pageable pageable) {
    16 
    17         if (pageable == null) {
    18             return this;
    19         }
    20 
    21         this.limit = pageable.getPageSize();
    22         this.skip = pageable.getOffset();
    23 
    24         return with(pageable.getSort());
    25     }
    26 
    27     public Query with(Sort sort) {
    28 
    29         if (sort == null) {
    30             return this;
    31         }
    32 
    33         for (Order order : sort) {
    34             if (order.isIgnoreCase()) {
    35                 throw new IllegalArgumentException(String.format("Gven sort contained an Order for %s with ignore case! "
    36                         + "MongoDB does not support sorting ignoreing case currently!", order.getProperty()));
    37             }
    38         }
    39 
    40         if (this.sort == null) {
    41             this.sort = sort;
    42         } else {
    43             this.sort = this.sort.and(sort);
    44         }
    45 
    46         return this;
    47     }
    48 
    49 
    50 
    51 private final Map<String, CriteriaDefinition> criteria = new LinkedHashMap<String, CriteriaDefinition>();
    52 
    53     public static Query query(CriteriaDefinition criteriaDefinition) {
    54         return new Query(criteriaDefinition);
    55     }
    56 
    57     public Query() {}
    58 
    59 
    60     public Query(CriteriaDefinition criteriaDefinition) {
    61         addCriteria(criteriaDefinition);
    62     }
    63 
    64 
    65     public Query addCriteria(CriteriaDefinition criteriaDefinition) {
    66 
    67         CriteriaDefinition existing = this.criteria.get(criteriaDefinition.getKey());
    68         String key = criteriaDefinition.getKey();
    69 
    70         if (existing == null) {
    71             this.criteria.put(key, criteriaDefinition);
    72         } else {
    73             throw new InvalidMongoDbApiUsageException("Due to limitations of the com.mongodb.BasicDBObject, "
    74                     + "you can't add a second '" + key + "' criteria. " + "Query already contains '"
    75                     + existing.getCriteriaObject() + "'.");
    76         }
    77 
    78         return this;
    79     }

    Criteria

        private String key;
        private List<Criteria> criteriaChain;
        private LinkedHashMap<String, Object> criteria = new LinkedHashMap<String, Object>();
        private Object isValue = NOT_SET;
    
        public static Criteria where(String key) {
            return new Criteria(key);
        }
    public Criteria and(String key) { return new Criteria(this.criteriaChain, key); } is ne lt lte gt gte in/all nin mod size exits type not regex in: 包含其中一个即可 all:全部包含才可以 查询时要明确这多个值主键的关系是什么样的

    public Criteria orOperator(Criteria... criteria)
    public Criteria andOperator(Criteria... criteria)
    public Criteria norOperator(Criteria... criteria)

    8 案例

    1 按照创建时间查找上一条下一条记录

     public Idea findIdeaNearTo(String userId, long createAt, boolean isNext) {
            Criteria criteria = Criteria.where("user_id").is(userId).and("delete_flag").is(false);
            Query query;
            if (isNext) {
                query = new Query(criteria).with(new Sort(Sort.Direction.ASC, "create_at"));
                criteria.and("create_at").gt(createAt);
            } else {
                query = new Query(criteria).with(new Sort(Sort.Direction.DESC, "create_at"));
                criteria.and("create_at").lt(createAt);
            }
            return mongoTemplate.findOne(query, Idea.class);
     }
    next:
    { "user_id" : "2107164232" , "delete_flag" : false , "create_at" : { "$gt" : 1474600921000}}
    pre:
    { "user_id" : "2107164232" , "delete_flag" : false , "create_at" : { "$lt" : 1474600921000}}
    

    2 orOperator / andOperator

        public List<Idea> find(String userId, IdeaStatus status, OriginalityType type, long createAtFrom, long createAtTo) {
            Criteria criteria = Criteria.where("user_id").is(userId)
                    .and("delete_flag").is(false)
                    .and("status").in(status);
            if (type == null) {
                criteria.orOperator(
                        Criteria.where("originality_type").exists(false), // 字段是否存在 exists
                        Criteria.where("originality_type").size(0));      // 字段是数组,大小 size
            } else {
                criteria.and("originality_type").in(type);
            }
            criteria.andOperator(
                    Criteria.where("create_at").gte(createAtFrom),
                    Criteria.where("create_at").lt(createAtTo)
            );
            return mongoTemplate.find(new Query(criteria), Idea.class);
        } 
     1 {
     2     "user_id": "290536",
     3     "delete_flag": false,
     4     "status": {
     5         "$in": [
     6             "originality"
     7         ]
     8     },
     9     "$or": [
    10         {
    11             "originality_type": {
    12                 "$exists": false
    13             }
    14         },
    15         {
    16             "originality_type": {
    17                 "$size": 0
    18             }
    19         }
    20     ],
    21     "$and": [
    22         {
    23             "create_at": {
    24                 "$gte": 1445788800000
    25             }
    26         },
    27         {
    28             "create_at": {
    29                 "$lt": 1446393600000
    30             }
    31         }
    32     ]
    33 }

     注意:一个字段有多个条件限制,需要使用多个Criteria实现。各个Criteria之间使用orOperator或andOperator链接。

     案例2

     1  public Items<Idea> listOriginalities(String userId, SearchVo condition, Pageable pageable) {
     2         List<Criteria> orCriteriaList = new ArrayList<>();
     3         if (condition.getStatus() == null || condition.getStatus().size() == 0) {
     4             orCriteriaList.add(Criteria.where("status").in(Arrays.asList(IdeaStatus.originality)));
     5         } else {
     6             for (IdeaStatus status : condition.getStatus()) {
     7                 orCriteriaList.add(Criteria.where("status").all(Arrays.asList(status, IdeaStatus.originality)));
     8             }
     9         }
    10         Criteria criteria = Criteria.where("userId").is(userId).and("deleteFlag").is(false)
    11                 .orOperator(orCriteriaList.toArray(new Criteria[0]));
    12 
    13         if (!CollectionUtils.isEmpty(condition.getTag())) {
    14             criteria.and("tags").in(condition.getTag());
    15         }
    16         Query query = query(criteria).with(pageable);
    17         Query countQuery = query(criteria);
    18         return Items.of(mongoTemplate.find(query, Idea.class), mongoTemplate.count(countQuery, Idea.class));
    19     }
    {
        "user_id": "2107164232",
        "delete_flag": false,
        "$or": [
            {
                "status": {
                    "$all": [
                        "discussing",
                        "originality"
                    ]
                }
            },
            {
                "status": {
                    "$all": [
                        "considering",
                        "originality"
                    ]
                }
            }
        ]
    }
    

    localhost:9088/v0.1/ideas/originality?tag=系统默认&status=discussing,considering

    要求status必须是originality,若条件中包含status,必须是其中之一或全是。

    public Criteria orOperator(Criteria... criteria) 

    把条件中每一个状态拆分与originality组成一个查询条件,这样就得到一组查询条件,每个条件是or的关系。

    3 更新或保存  更新返回旧值

     public BlockContent addIfNotExists(BlockContent blockContent, long tenant) {
            Query query = Query.query(where("tenant").is(tenant)
                    .and("sourceId").is(blockContent.getSourceId())
                    .and("scope.code").is(blockContent.getScope().getCode()));
    
            Update update = getUpdate(blockContent);
    
            return operations.findAndModify(
                    query, update,
                    options().upsert(true).returnNew(false),
                    BlockContent.class
            );
        }
    返回null 保存
    返回旧值 更新 使用他的id

    3 更新语句避免 n+1操作

        public void setBlockStatusAndRecoverTime(Set<String> ids, long tenant, Date recoverTime) {
            Query query = Query.query(
                    where("_id").in(ids)
                            .and("tenant").is(tenant)
                            .and("blockStatus").is(BlockStatus.BLOCKED));
    
            operations.updateMulti(
                    query,
                    Update.update("blockStatus", BlockStatus.RECOVER)
                            .set("recoverTime", recoverTime),
                    BlockContent.class
            );
        }

    对于一样的修改动作,尤其更新少量字段时候(deleteFlag,dealTime,status),使用一条更新语句更新多个字段(updateMulti),而不是先查询,修改后保存

  • 相关阅读:
    网页解析Jsoup简单使用
    ios开发中加载的image无法显示
    数据懒加载
    ijkplayer
    ijkplayer的一些优化
    Ambiguous expansion of macro weakify和Ambiguous expansion of macro strongify的警告
    xcode11新项目删除main.storyboard 两种方法
    iOS
    iOS
    iOS 12中获取WiFi的SSID
  • 原文地址:https://www.cnblogs.com/wihainan/p/6014883.html
Copyright © 2020-2023  润新知