目录
spring-data-mongoDB%E4%BD%BF%E7%94%A8-toc" name="tableOfContents" style="margin-left:0px">spring-data-mongoDB使用
1.导入mongoDB依赖
2.编写配置文件
3.编写实体类,与mongoDB中的文档相对应,使用@Document注解
4.编写service层方法
一.实现保存方法
二.实现修改方法
三.实现删除方法
四.实现查询方法
项目使用mongoDB实现作业范围
1.实体类
2.业务实现
这里介绍以下spring-data-mongoDB的用法
spring-data-mongoDB%E4%BD%BF%E7%94%A8" name="spring-data-mongoDB%E4%BD%BF%E7%94%A8">spring-data-mongoDB使用
1.导入mongoDB依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId></dependency>
2.编写配置文件
spring:application:name: sl-express-mongodbdata:mongodb:host: 192.168.150.101port: 27017database: slauthentication-database: admin #认证数据库username: slpassword: "123321"auto-index-creation: true #自动创建索引
3.编写实体类,与mongoDB中的文档相对应,使用@Document注解
java">import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.bson.types.ObjectId;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.index.GeoSpatialIndexType;
import org.springframework.data.mongodb.core.index.GeoSpatialIndexed;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Document("sl_person") //指定表名,也就是指定集合的名字
public class Person {@Id // 标识为主键private ObjectId id;@Indexed //标识索引字段private String name;private int age;/*** 用户位置* x: 经度,y:纬度*/@GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE)private GeoJsonPoint location;//存储嵌套对象数据private Address address;
}
java">import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.mongodb.core.mapping.Document;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Document("sl_address") //指定表名,也就是指定集合的名字
public class Address {private String street;private String city;private String zip;
}
4.编写service层方法
因为mongoDB没有向mysql,neo4j一样提供持久层的接口方法,但是和redis一样有一个MongoTemplate 的bean对象,可以直接对数据库进行CRUD操作
java">public interface PersonService {/*** 新增数据** @param person 数据*/void savePerson(Person person);/*** 更新数据** @param person 数据*/void update(Person person);/*** 根据名字查询用户列表** @param name 用户名字* @return 用户列表*/List<Person> queryPersonListByName(String name);/*** 分页查询用户列表** @param page 页数* @param pageSize 页面大小* @return 用户列表*/List<Person> queryPersonPageList(int page, int pageSize);/*** 根据id删除用户** @param id 主键*/void deleteById(String id);
}
java">import org.springframework.data.mongodb.core.MongoTemplate;import javax.annotation.Resource;
import java.util.List;public class PersonServiceImpl implements PersonService {@Resourceprivate MongoTemplate mongoTemplate;@Overridepublic void savePerson(Person person) {}@Overridepublic void update(Person person) {}@Overridepublic List<Person> queryPersonListByName(String name) {return null;}@Overridepublic List<Person> queryPersonPageList(int page, int pageSize) {return null;}@Overridepublic void deleteById(String id) {}
}
一.实现保存方法
可以使用insert方法和save方法,但是save方法会直接覆盖原来的值
java"> @Overridepublic void savePerson(Person person) {mongoTemplate.save(person);}
测试
java">import org.bson.types.ObjectId;@ResourcePersonService personService;@Testvoid savePerson() {Person person = Person.builder().id(ObjectId.get()) //使用mongoDB提供的ObjectId生成id值.name("张三").age(20).location(new GeoJsonPoint(116.343847, 40.060539)).address(new Address("人民路", "上海市", "666666")).build();this.personService.savePerson(person);}
不使用ObjectId获取id也可以,mongoDB也会自己生成
java">@Testvoid savePerson() {Person person = Person.builder()//.id(ObjectId.get()).name("张三").age(20).location(new GeoJsonPoint(116.343847, 40.060539)).address(new Address("人民路", "上海市", "666666")).build();this.personService.savePerson(person);}
二.实现修改方法
java"> @Overridepublic void update(Person person) {//1.构建查询条件,定义要改哪些数据,这里定义了要改主键_id=person.getId的数据/*Query query = new Query();query.addCriteria(Criteria.where("_id").is(person.getId()));*///或者:Query query = Query.query(Criteria.where("_id").is(person.getId()));//2.设置要修改的值Update update = new Update();update.set("name",person.getName());update.set("age",person.getAge());update.set("location",person.getLocation());update.set("address",person.getAddress());//3.只修改一个mongoTemplate.updateFirst(query,update, Person.class);//根据Person.class提供的类型才能去操作哪一个集合}
测试
java"> @Testvoid update() {Person person = Person.builder().id(new ObjectId("67b7fb71ab81697aa2e8688e")).name("张三").age(22) //修改数据.location(new GeoJsonPoint(116.343847, 40.060539)).address(new Address("人民路", "上海市", "666666")).build();this.personService.update(person);}
三.实现删除方法
其实_id可以写出id,mongo也会当作主键去查
java"> @Overridepublic void deleteById(String id) {Query query = Query.query(Criteria.where("_id").is(id));mongoTemplate.remove(query, Person.class);}
测试
java">@Testvoid deleteById() {this.personService.deleteById("67b7fb71ab81697aa2e8688e");}
四.实现查询方法
java"> @Overridepublic List<Person> queryPersonListByName(String name) {Query query = Query.query(Criteria.where("name").regex(name));//根据正则表达式查询,类似模糊查询return mongoTemplate.find(query,Person.class);}
测试
java"> @Testvoid queryPersonListByName() {List<Person> personList = this.personService.queryPersonListByName("三");personList.forEach(System.out::println);}
五.实现分页查询
java">@Overridepublic List<Person> queryPersonPageList(int page, int pageSize) {//1.构建查询条件Query query = new Query();//根据age降序query.with(Sort.by(Sort.Direction.DESC,"age"));//构建分页条件,mongoDB默认页数从0开始,而一般前端都是从第一页,所以要减一query.with(PageRequest.of((page-1),pageSize));return mongoTemplate.find(query, Person.class);}
测试
java"> @Testvoid queryPersonPageList() {List<Person> personList = this.personService.queryPersonPageList(1, 10);personList.forEach(System.out::println);}
项目使用mongoDB实现作业范围
1.实体类
这里对id主键不进行json序列化,使用bid作为我们的业务id,并且对类型进行普通索引,使用type来区分快递机构和快递员,最后使用GeoJsonPolygon类型作为面积范围的闭合的多边形范围,并使用索引
java">@Data
@Document("sl_service_scope")
public class ServiceScopeEntity {@Id@JsonIgnoreprivate ObjectId id;/*** 业务id,可以是机构或快递员*/@Indexedprivate Long bid;/*** 类型 {@link com.sl.ms.scope.enums.ServiceTypeEnum}*/@Indexedprivate Integer type;/*** 多边形范围,是闭合的范围,开始经纬度与结束经纬度必须一样* x: 经度,y:纬度*/@GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE)private GeoJsonPolygon polygon;private Long created; //创建时间private Long updated; //更新时间
}
java">public enum ServiceTypeEnum {ORGAN(1, "机构"),COURIER(2, "快递员");/*** 类型编码*/private final Integer code;/*** 类型值*/private final String value;ServiceTypeEnum(Integer code, String value) {this.code = code;this.value = value;}public Integer getCode() {return code;}public String getValue() {return value;}public static ServiceTypeEnum codeOf(Integer code) {return EnumUtil.getBy(ServiceTypeEnum::getCode, code);}
}
2.业务实现
保存数据
java">/*** 保存或修改 作业范围* @param bid 业务id* @param type 类型* @param polygon 多边形坐标点* @return*/@Overridepublic Boolean saveOrUpdate(Long bid, ServiceTypeEnum type, GeoJsonPolygon polygon) {// TODO day08 新增作业范围// 1. 构建mongo查询条件 bid typeQuery query = Query.query(Criteria.where("bid").is(bid).and("type").is(type.getCode()));// 2. 根据条件查询对应作业范围ServiceScopeEntity serviceScopeEntity = mongoTemplate.findOne(query, ServiceScopeEntity.class);// 3. 如果作业范围为空 ==> 新建作业范围保存if(ObjectUtil.isEmpty(serviceScopeEntity)){serviceScopeEntity=new ServiceScopeEntity();serviceScopeEntity.setBid(bid);serviceScopeEntity.setType(type.getCode());serviceScopeEntity.setPolygon(polygon);serviceScopeEntity.setCreated(System.currentTimeMillis());serviceScopeEntity.setUpdated(System.currentTimeMillis());}else{// 4. 如果作业范围存在 ==> 修改多边形字段 和 updated时间serviceScopeEntity.setPolygon(polygon);serviceScopeEntity.setUpdated(System.currentTimeMillis());}try {mongoTemplate.save(serviceScopeEntity);return true;} catch (Exception e) {log.error("新增或者修改数据失败,{}",serviceScopeEntity);}// 5. 返回结果return false;}
GeoJsonPolygon类型的构造参数使用的是List<Point>,注意第一个节点数据需要与最后一个节点数据一样,因为要保持是闭合的多边形
java"> import org.springframework.data.geo.Point;@Testvoid saveOrUpdate() {List<Point> pointList = Arrays.asList(new Point(116.340064,40.061245),new Point(116.347081,40.061836),new Point(116.34751,40.05842),new Point(116.342446,40.058092),new Point(116.340064,40.061245));Boolean result = this.scopeService.saveOrUpdate(2L, ServiceTypeEnum.ORGAN, new GeoJsonPolygon(pointList));System.out.println(result);}
根据传入的一个点的坐标来判断在哪一个机构或者快递员中的范围中,可能会查出来多个,所以用集合
java"> @Overridepublic List<ServiceScopeEntity> queryListByPoint(ServiceTypeEnum type, GeoJsonPoint point) {// TODO day08 根据类型 和 坐标点查询有交集的作业范围 tips: intersects 查询传入坐标点和mongo库中多边形相交的数据Query query = Query.query(Criteria.where("type").is(type.getCode()).and("polygon").intersects(point));return mongoTemplate.find(query, ServiceScopeEntity.class);}@Overridepublic List<ServiceScopeEntity> queryListByPoint(ServiceTypeEnum type, String address) {// TODO day08 根据类型 和 详细地址 tips: eagleMapTemplate 查询坐标点GeoResult geoResult = eagleMapTemplate.opsForBase().geoCode(address);Coordinate location = geoResult.getLocation();return queryListByPoint(type,new GeoJsonPoint(location.getLongitude(),location.getLatitude()));}
java"> @Testvoid testQueryListByPoint() {GeoJsonPoint point = new GeoJsonPoint(116.344828,40.05911);List<ServiceScopeEntity> serviceScopeEntities = this.scopeService.queryListByPoint(ServiceTypeEnum.ORGAN, point);serviceScopeEntities.forEach(serviceScopeEntity -> System.out.println(serviceScopeEntity));}@Testvoid testQueryListByPoint2() {String address = "北京市昌平区金燕龙办公楼";List<ServiceScopeEntity> serviceScopeEntities = this.scopeService.queryListByPoint(ServiceTypeEnum.ORGAN, address);serviceScopeEntities.forEach(serviceScopeEntity -> System.out.println(serviceScopeEntity));}
可以发现这个坐标点被这个闭合的多边形包围
根据bid和类型查询
java"> public ServiceScopeEntity queryByBidAndType(Long bid, ServiceTypeEnum type) {// TODO day08 根据bid和类型查询作业范围Query query = Query.query(Criteria.where("bid").is(bid).and("type").is(type.getCode()));return mongoTemplate.findOne(query,ServiceScopeEntity.class);}
查询附近的人
java">/*** 查询附近的人的所有用户id** @param userId 用户id,中心点用户* @param metre 距离,单位:米* @return 附近的人*/@Overridepublic List<Long> queryNearUser(Long userId, Double metre) {//1、根据用户id,查询用户的位置信息Query query = Query.query(Criteria.where("userId").is(userId));UserLocation location = mongoTemplate.findOne(query, UserLocation.class);if (location == null) {return null;}//2、以当前用户位置绘制原点GeoJsonPoint point = location.getLocation();//3、绘制半径Distance distance = new Distance(metre / 1000, Metrics.KILOMETERS);//5、构建查询对象NearQuery nearQuery = NearQuery.near(point).maxDistance(distance);//6、执行查询,由近到远排序GeoResults<UserLocation> geoResults = mongoTemplate.geoNear(nearQuery, UserLocation.class);//7、获取结果对象,其中userLocationGeoResult.getDistance()可以获取目标点与中心点的位置return geoResults.getContent().stream().map(userLocationGeoResult -> userLocationGeoResult.getContent().getUserId()).collect(Collectors.toList());}