package com.fdkankan.mongodb.service.impl; import cn.hutool.core.lang.UUID; import cn.hutool.core.util.ObjectUtil; import com.fdkankan.mongodb.base.BaseRequestMongo; import com.fdkankan.mongodb.base.MongoPageResult; import com.fdkankan.mongodb.service.MongodbBaseService; import com.mongodb.client.result.DeleteResult; import org.apache.commons.lang3.StringUtils; import org.bson.Document; import org.bson.types.ObjectId; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Sort; import org.springframework.data.geo.GeoResults; import org.springframework.data.mapping.MappingException; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.NearQuery; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.util.Pair; import org.springframework.stereotype.Component; import org.springframework.util.Assert; import javax.annotation.PostConstruct; import java.lang.reflect.ParameterizedType; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.stream.Collectors; /** * @author Xiewj * @date 2021/11/15 * Mongodb工具类 */ @Component public class MongodbBaseServiceImpl implements MongodbBaseService { public static MongodbBaseServiceImpl mongodbBaseService; @PostConstruct public void init() { mongodbBaseService = this; mongodbBaseService.mongoTemplate = this.mongoTemplate; } @Autowired private MongoTemplate mongoTemplate; private static final int FIRST_PAGE_NUM = 1; private static final String ID = "_id"; /** * 分页查询. * * @param query Mongo Query对象,构造你自己的查询条件. * @param page#mapper 映射器,你从db查出来的list的元素类型是entityClass, 如果你想要转换成另一个对象,比如去掉敏感字段等,可以使用mapper来决定如何转换. * @param page#pageSize 分页的大小. * @param page#pageNum 当前页. * @param page#lastId 条件分页参数, 区别于skip-limit,采用find(_id>lastId).limit分页. * 如果不跳页,像朋友圈,微博这样下拉刷新的分页需求,需要传递上一页的最后一条记录的ObjectId。 如果是null,则返回pageNum那一页. * @return PageResult,一个封装page信息的对象. */ @Override public MongoPageResult pageQuery(BaseRequestMongo page, Query query) { //分页逻辑 long total = mongoTemplate.count(query, getTClass()); final Integer pages = (int) Math.ceil(total / (double) page.getPageSize()); if (page.getPageNum() <= 0 || page.getPageNum() > pages) { page.setPageNum(FIRST_PAGE_NUM); } if (StringUtils.isNotBlank(page.getLastId())) { final Criteria criteria = new Criteria(UUID.randomUUID().toString()); if (page.getPageNum() != FIRST_PAGE_NUM) { criteria.and(ID).gt(new ObjectId(page.getLastId())); } query.limit(page.getPageSize()); query.addCriteria(criteria); } else { int skip = page.getPageSize() * (page.getPageNum() - 1); query.skip(skip).limit(page.getPageSize()); } List entityList=null; if (page.getSortBy().toUpperCase(Locale.ROOT).equals(Sort.Direction.DESC.name())){ query.with(Sort.by(Collections.singletonList(new Sort.Order(Sort.Direction.DESC, page.getOrderBy())))); }else { query.with(Sort.by(Collections.singletonList(new Sort.Order(Sort.Direction.ASC, page.getOrderBy())))); } entityList=mongoTemplate.find(query, getTClass()); MongoPageResult mongoPageResult = new MongoPageResult<>(); mongoPageResult.setTotal(total); mongoPageResult.setPages(pages); mongoPageResult.setPageSize(page.getPageSize()); mongoPageResult.setPageNum(page.getPageNum()); mongoPageResult.setList(entityList); return mongoPageResult; } @Override public long getCount(Query query) { return mongoTemplate.count(query, getTClass()); } @Override public T save(T t) { return mongoTemplate.save(t); } @Override public T insert(T t) { return mongoTemplate.insert(t); } @Override public DeleteResult delete(T t) { return mongoTemplate.remove(t); } @Override public DeleteResult deleteById(Object id) { T t = mongoTemplate.findById(id, getTClass()); if (ObjectUtil.isNotNull(t)){ return delete(t); } return null; } @Override public DeleteResult delete(Query query) { return mongoTemplate.remove(query, getTClass()); } @Override public T update(T t) { return mongoTemplate.save(t); } @Override public T findById(Object id) { return mongoTemplate.findById(id, getTClass()); } @Override public List findAll() { return mongoTemplate.findAll(getTClass()); } @Override public List find(Query query) { return mongoTemplate.find(query, getTClass()); } @Override public GeoResults geoNear(NearQuery query) { GeoResults geoResults = mongoTemplate.geoNear(query, getTClass()); return geoResults; } @Override public T findOne(Query query) { return mongoTemplate.findOne(query,getTClass()); } @Override public boolean exists(Query query) { return mongoTemplate.exists(query, getTClass()); } @Override public boolean exists(T t) { return exists(getIdQueryFor(t)); } @Override public boolean exitsById(Object id) { return exists(findById(id)); } @Override public long count(Query query) { return mongoTemplate.count(query, getTClass()); } @Override public Class getTClass() { Class tClass = (Class) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; return tClass; } @Override public Pair extractIdPropertyAndValue(Object object) { Assert.notNull(object, "Id cannot be extracted from 'null'."); Class objectType = object.getClass(); if (object instanceof Document) { return Pair.of("_id", ((Document) object).get("_id")); } else { MongoPersistentEntity entity = (MongoPersistentEntity) mongoTemplate.getConverter().getMappingContext().getPersistentEntity(objectType); if (entity != null && entity.hasIdProperty()) { MongoPersistentProperty idProperty = (MongoPersistentProperty) entity.getIdProperty(); return Pair.of(idProperty.getFieldName(), entity.getPropertyAccessor(object).getProperty(idProperty)); } else { throw new MappingException("No id property found for object of type " + objectType); } } } @Override public Query getIdQueryFor(Object object) { Pair id = this.extractIdPropertyAndValue(object); return new Query(Criteria.where((String) id.getFirst()).is(id.getSecond())); } }