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_lbs
host: 192.168.2.9
port: 27017
username: admin
password: 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文档,类似关系型数据库中的表,不指定名称时默认为 hotPoint
public class HotPoint implements Serializable {
@Id
private String id;
@Indexed
private Integer userId;
// 自动创建2DSPHERE索引,可以用于球面地理位置计算
@GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE)
private Double[] pos;
private String pointName;
// 不需要被mongobd存储的字段可以加@Transient标识
@Transient
private 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 {
@Autowired
private MongoTemplate mongoTemplate;
@Override
public <T> T save(T t) {
mongoTemplate.save(t);
return t;
}
@Override
public <T> void deleteById(T t) {
mongoTemplate.remove(t);
}
@Override
public <T> void removeAll(Query query, Class<T> entityClass) {
mongoTemplate.findAllAndRemove(query, entityClass);
}
@Override
public <T> UpdateResult update(Query query, Update update, Class<T> entityClass) {
return mongoTemplate.updateMulti(query, update, entityClass);
}
@Override
public <T> UpdateResult updateFirst(Query query, Update update, Class<T> entityClass) {
return mongoTemplate.updateFirst(query, update, entityClass);
}
@Override
public <T> UpdateResult upsert(Query query, Update update, Class<T> entityClass) {
return mongoTemplate.upsert(query, update, entityClass);
}
@Override
public <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);
}
@Override
public <T> UpdateResult updateById(Integer id, T t, Class<T> entityClass) {
return this.updateById(String.valueOf(id), t, entityClass);
}
@Override
public <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
*/
@Override
public <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;
}
@Override
public <T> List<T> find(Query query, Class<T> entityClass) {
return mongoTemplate.find(query, entityClass);
}
@Override
public <T> T findById(Integer id, Class<T> entityClass) {
return this.findById(String.valueOf(id), entityClass);
}
@Override
public <T> T findById(String id, Class<T> entityClass) {
return mongoTemplate.findById(id, entityClass);
}
@Override
public <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();
}
@Override
public <T> T findOne(Query query, Class<T> entityClass) {
return mongoTemplate.findOne(query, entityClass);
}
@Override
public <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);
}
}
}
@Override
public <T> T findOne(Query query, Class<T> entityClass, String... excludeFields) {
setExcluedFields(query, excludeFields);
return findOne(query, entityClass);
}
@Override
public <T> long count(Query query, Class<T> entityClass) {
return mongoTemplate.count(query, entityClass);
}
@Override
public <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);
}
@Override
public <T> AggregationResults<T> aggregate(Aggregation aggregation, Class<?> inputType, Class<T> outputType) {
return mongoTemplate.aggregate(aggregation, inputType, outputType);
}
@Override
public <T> void insertAll(List<T> tList) {
mongoTemplate.insertAll(tList);
}
@Override
public <T> UpdateResult updateMulti(Query query, Update update, Class<T> entityClass) {
return mongoTemplate.updateMulti(query, update, entityClass);
}
@Override
public <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();
更多推荐
已为社区贡献4条内容
所有评论(0)