springboot+mongodb
NoSQL ? MongoDB !
·
本篇简述springboot集成mongodb (单机版,不作分片)
mongo可以直接存取结构较复杂的json数据类型的文档,支持聚合统计等复杂计算,并具有简单的搜索功能,还提供了LBS功能。

准备工作:
-
搭建springboot脚手架并成功运行,可参考历史分享springboot+mybatis
-
启动mongodb服务(搭建配置mongodb后续会在运维章节另行讲述)
-
登录连接mongo,创建库test_lbs及用户admin并授权验证admin123
1. maven添加mongodb依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId></dependency><dependency><groupId>org.mongodb</groupId><artifactId>mongo-java-driver</artifactId></dependency><dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-mongodb</artifactId></dependency>
2. mongodb 配置封装
2.1 yml
spring:data:mongodb:database: test_lbshost: 192.168.2.9port: 27017username: adminpassword: admin123
2.2 mongo document
import lombok.Data;import org.springframework.data.mongodb.core.mapping.Document;import org.springframework.data.annotation.Id;import org.springframework.data.annotation.Transient;@Data@Document // 自动创建mongo文档,类似关系型数据库中的表,不指定名称时默认为 hotPointpublic class HotPoint implements Serializable {@Idprivate String id;@Indexedprivate Integer userId;// 自动创建2DSPHERE索引,可以用于球面地理位置计算@GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE)private Double[] pos;private String pointName;// 不需要被mongobd存储的字段可以加@Transient标识@Transientprivate double scope; // 距离(单位:km)}
2.3 mongoService 通用操作类封装
service接口类:
import com.mongodb.client.result.UpdateResult;import org.springframework.data.mongodb.core.aggregation.Aggregation;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;public interface MongoService {/*** 保存一个对象到mongodb* @param t* @param <T>* @return*/<T> T save(T t);/*** 根据id删除对象* @param t* @param <T>*/<T> void deleteById(T t);/*** 删除所有* @param query* @param entityClass* @param <T>*/<T> void removeAll(Query query, Class<T> entityClass);/*** 通过条件查询更新数据* @param query* @param update* @param entityClass* @param <T>* @return*/<T> UpdateResult update(Query query, Update update, Class<T> entityClass);/*** 更新第一条满足条件的文档* @param query* @param update* @param entityClass* @param <T>*/<T> UpdateResult updateFirst(Query query, Update update, Class<T> entityClass);/*** 新增或插入* @param query* @param update* @param entityClass* @param <T>* @return*/<T> UpdateResult upsert(Query query, Update update, Class<T> entityClass);/*** 构建update* @param t* @param <T>* @return*/<T> Update buildBaseUpdate(T t);/*** 根据id进行更新* @param id* @param t* @param entityClass* @param <T>* @return*/<T> UpdateResult updateById(String id, T t, Class<T> entityClass);/*** 根据id进行更新* @param id* @param t* @param entityClass* @param <T>* @return*/<T> UpdateResult updateById(Integer id, T t, Class<T> entityClass);<T> UpdateResult updateById(Integer id, Update update, Class<T> entityClass);/*** 通过条件查询实体(集合)* @param query* @param entityClass* @param <T>* @return*/<T> List<T> find(Query query, Class<T> entityClass);/*** 通过主键查询实体* @param id* @param entityClass* @param <T>* @return*/<T> T findById(Integer id, Class<T> entityClass);<T> T findById(String id, Class<T> entityClass);<T> List<T> findByIdIn(Iterable<Integer> ids, Class<T> entityClass);/*** 通过一定的条件查询一个实体* @param query* @param entityClass* @param <T>* @return*/<T> T findOne(Query query, Class<T> entityClass);/*** 通过条件查询实体(集合)* @param query* @param excludeFields 排除返回字段* @return*/<T> List<T> find(Query query, Class<T> entityClass, String... excludeFields);/*** 通过条件查询实体* @param query* @param excludeFields 排除返回字段* @return*/<T> T findOne(Query query, Class<T> entityClass, String... excludeFields);/*** 总记录数* @param query* @param entityClass* @return*/<T> long count(Query query, Class<T> entityClass);/*** 获取分页数据* @param currentPage* @param pageSize* @param query* @return*/<T> PagerResponse<T> pageInfo(int currentPage, int pageSize, Query query, Class<T> entityClass);/*** 聚合查询* @param aggregation* @param inputType* @param outputType* @param <T>* @return*/<T> AggregationResults<T> aggregate(Aggregation aggregation, Class<?> inputType, Class<T> outputType);/*** 批量插入* @param list* @param <T>*/<T> void insertAll(List<T> list);/*** 批量更新* @param query* @param update* @param entityClass* @param <T>* @return*/<T> UpdateResult updateMulti(Query query, Update update, Class<T> entityClass);/*** 去重查询* @param key* @param value* @param query* @param entityClass* @param <T>* @return*/<T> List<T> queryForDistinct(String key, Object value, Query query, Class<T> entityClass);}
service实现类:
import com.mongodb.BasicDBObject;import com.mongodb.Block;import com.mongodb.client.DistinctIterable;import com.mongodb.client.result.UpdateResult;import lombok.extern.slf4j.Slf4j;import org.apache.commons.collections.CollectionUtils;import org.bson.conversions.Bson;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.mongodb.core.MongoTemplate;import org.springframework.data.mongodb.core.aggregation.Aggregation;import org.springframework.data.mongodb.core.aggregation.AggregationResults;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.lang.reflect.Field;import java.util.ArrayList;import java.util.Collections;import java.util.List;@Slf4j@Service // 需确保该service组件能被springboot扫描到public class MongoServiceImpl implements MongoService {@Autowiredprivate MongoTemplate mongoTemplate;@Overridepublic <T> T save(T t) {mongoTemplate.save(t);return t;}@Overridepublic <T> void deleteById(T t) {mongoTemplate.remove(t);}@Overridepublic <T> void removeAll(Query query, Class<T> entityClass) {mongoTemplate.findAllAndRemove(query, entityClass);}@Overridepublic <T> UpdateResult update(Query query, Update update, Class<T> entityClass) {return mongoTemplate.updateMulti(query, update, entityClass);}@Overridepublic <T> UpdateResult updateFirst(Query query, Update update, Class<T> entityClass) {return mongoTemplate.updateFirst(query, update, entityClass);}@Overridepublic <T> UpdateResult upsert(Query query, Update update, Class<T> entityClass) {return mongoTemplate.upsert(query, update, entityClass);}@Overridepublic <T> UpdateResult updateById(String id, T t, Class<T> entityClass) {Query query = new Query();query.addCriteria(Criteria.where("_id").is(id));Update update = this.buildBaseUpdate(t);return update(query, update, entityClass);}@Overridepublic <T> UpdateResult updateById(Integer id, T t, Class<T> entityClass) {return this.updateById(String.valueOf(id), t, entityClass);}@Overridepublic <T> UpdateResult updateById(Integer id, Update update, Class<T> entityClass) {Query query = new Query(Criteria.where("_id").is(String.valueOf(id)));return this.updateFirst(query, update, entityClass);}/*** 根据vo构建构建更新条件Update** @param t* @param <T>* @return*/@Overridepublic <T> Update buildBaseUpdate(T t) {Update update = new Update();Field[] fields = t.getClass().getDeclaredFields();for (Field field : fields) {field.setAccessible(true);try {Object value = field.get(t);if (value != null) {update.set(field.getName(), value);}} catch (Exception e) {log.error("异常信息:{}", e.getMessage());}}return update;}@Overridepublic <T> List<T> find(Query query, Class<T> entityClass) {return mongoTemplate.find(query, entityClass);}@Overridepublic <T> T findById(Integer id, Class<T> entityClass) {return this.findById(String.valueOf(id), entityClass);}@Overridepublic <T> T findById(String id, Class<T> entityClass) {return mongoTemplate.findById(id, entityClass);}@Overridepublic <T> List<T> findByIdIn(Iterable<Integer> ids, Class<T> entityClass) {if(ids != null){List<String> strIds = new ArrayList<>();ids.forEach(id -> strIds.add(String.valueOf(id)));if(CollectionUtils.isNotEmpty(strIds)){return mongoTemplate.find(new Query(Criteria.where("_id").in(strIds)), entityClass);}}return Collections.emptyList();}@Overridepublic <T> T findOne(Query query, Class<T> entityClass) {return mongoTemplate.findOne(query, entityClass);}@Overridepublic <T> List<T> find(Query query, Class<T> entityClass, String... excludeFields) {setExcluedFields(query, excludeFields);return find(query, entityClass);}/*** 排除MongoDB查询返回的一些字段** @param query* @param excludeFields*/private void setExcluedFields(Query query, String... excludeFields) {if (null != query && null != excludeFields) {for (String field : excludeFields) {query.fields().exclude(field);}}}@Overridepublic <T> T findOne(Query query, Class<T> entityClass, String... excludeFields) {setExcluedFields(query, excludeFields);return findOne(query, entityClass);}@Overridepublic <T> long count(Query query, Class<T> entityClass) {return mongoTemplate.count(query, entityClass);}@Overridepublic <T> PagerResponse<T> pageInfo(int currentPage, int pageSize, Query query, Class<T> entityClass) {if (currentPage == 0) {currentPage = 1;}if (pageSize == 0) {pageSize = 10;}long count = this.count(query, entityClass);long totalPage = count % pageSize > 0 ? count / pageSize + 1 : count / pageSize;int skip = (currentPage - 1) * pageSize;List<T> list = this.find(query.skip(skip).limit(pageSize), entityClass);return new PagerResponse<>(currentPage, pageSize, (int) totalPage, count, list);}@Overridepublic <T> AggregationResults<T> aggregate(Aggregation aggregation, Class<?> inputType, Class<T> outputType) {return mongoTemplate.aggregate(aggregation, inputType, outputType);}@Overridepublic <T> void insertAll(List<T> tList) {mongoTemplate.insertAll(tList);}@Overridepublic <T> UpdateResult updateMulti(Query query, Update update, Class<T> entityClass) {return mongoTemplate.updateMulti(query, update, entityClass);}@Overridepublic <T> List<T> queryForDistinct(String key, Object value, Query query, Class<T> entityClass) {List<T> result = new ArrayList<>();Bson bson = new BasicDBObject(key, value);DistinctIterable<T> distinctIterable = mongoTemplate.getCollection(mongoTemplate.getCollectionName(entityClass)).distinct(key, bson, entityClass);T first = distinctIterable.first();result.add(first);distinctIterable.forEach((Block<T>) result::add);return result;}}
3. 使用示例
3.1 在具体需要的类中注入mongoService直接调用封装的API
@Autowired private MongoService mongoService;
更新文档:
HotPoint point = mongoService.findById(pointId, HotPoint.class);if(point != null){Update update = Update.update("pointName", pointName);mongoService.updateById(pointId, update, HotPoint.class);}
LBS查询:
/*** 球面弧长1000米对应的弧度:弧长/地球半径* BigDecimal.valueOf(1000).divide(BigDecimal.valueOf(6378245.0), 5, BigDecimal.ROUND_HALF_UP)*/public static final double ONE_KILOMETER_RADIAN = 0.00016;// 2公里距离public static final double DISTANCE_2KM = 2.0;// 构建查询条件 (注意mongo在存储id字段时,会自动加上前缀下划线)Query query = new Query(Criteria.where("_id").gte(10));// LBS定位点Point point = new Point(longitude, latitude);// 弧长转换为弧度double maxDistance = DISTANCE_2KM * ONE_KILOMETER_RADIAN;// 基于附近2公里查询query.addCriteria(Criteria.where("pos").nearSphere(point).maxDistance(maxDistance));// 排序 (注意追加排序后,会打乱LBS默认的由近及远排序)query.with(Sort.by(Sort.Direction.DESC, "id"))List<HotPoint> points = mongoService.find(query, HotPoint.class)
聚合查询:
Point point = new Point(longitude, latitude);// 弧长转换为弧度double maxDistance = DISTANCE_2KM * ONE_KILOMETER_RADIAN;NearQuery nearQuery = NearQuery.near(point).maxDistance(maxDistance);// 从id>=10开始查询Criteria criteria = Criteria.where("_id").gte(10);TypedAggregation<HotPoint> aggregation =Aggregation.newAggregation(HotPoint.class,Aggregation.geoNear(nearQuery.spherical(true).query(new Query(criteria)), "distance"),Aggregation.group("userId").first("userId").as("userId"),Aggregation.skip((pageNum - 1) * pageSize),Aggregation.limit(pageSize));// 聚合查询附近热点位置(分组去重) // HotPoint为查询入参类型,UserPoint为查询结果返回映射封装类型AggregationResults<UserPoint> result = mongoService.aggregate(aggregation, HotPoint.class, UserPoint.class);List<UserPoint> hotPoints = result.getMappedResults();
更多推荐


所有评论(0)