SpringBoot整合MongoDB及简单的操作

一,环境搭建

1)引入依赖

 <parent>
        <artifactId>spring-boot-parent</artifactId>
        <groupId>org.springframework.boot</groupId>
        <version>2.3.7.RELEASE</version>
  </parent>
 <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

2)添加配置

#springboot MongoDB配置
spring.data.mongodb.username=test
spring.data.mongodb.password=oudqBFGmGY8pU6WSs
spring.data.mongodb.authentication-database=admin
spring.data.mongodb.database=test
spring.data.mongodb.port=27017
spring.data.mongodb.host=localhost 
spring.data.mongodb.auto-index-creation=true

3)添加实体类

@Data
@Document("User")
public class User {

 @Id
 private String id;
 @Indexed
 private String name;
 private Integer age;
 private String email;
 private String createDate;
}

二,基于 MongoTemplate

1.常用方法

  • 查询User文档的全部数据
mongoTemplate.findAll(User.class)
  • 查询User文档id为id的数据
mongoTemplate.findById(<id>, User.class)
  • 根据query内的查询条件查询
mongoTemplate.find(query, User.class)

在这里插入图片描述

  • 修改
//如果数据存在就更新,否则插入新的数据
mongoTemplate.upsert(query, update, User.class)

在这里插入图片描述

  • 删除
mongoTemplate.remove(query, User.class)

在这里插入图片描述

  • 新增
mongoTemplate.insert(User)

在这里插入图片描述

2.Query对象

  1. 创建一个query对象(用来封装所有条件对象),再创建一个criteria对象(用来构建条件)
  2. 精准条件:criteria.and(“key”).is(“条件”)
    模糊条件:criteria.and(“key”).regex(“条件”)
  3. 封装条件:query.addCriteria(criteria)
  4. 大于(创建新的criteria):Criteria gt = Criteria.where(“key”).gt(“条件”)
    小于(创建新的criteria):Criteria lt = Criteria.where(“key”).lt(“条件”)
  5. Query.addCriteria(new Criteria().andOperator(gt,lt));
  6. 一个query中只能有一个andOperator()。其参数也可以是Criteria数组。
  7. 排序 :query.with(new Sort(Sort.Direction.ASC, "age"). and(new Sort(Sort.Direction.DESC, "date")))

3.测试

 @Autowired
 private MongoTemplate mongoTemplate;

添加:

	@Test
    public void createUser() {
        User user = new User();
        user.setAge(20);
        user.setName("test");
        user.setEmail("4932200@qq.com");
        User user1 = mongoTemplate.insert(user);
        System.out.println(user1);
    }

查询所有:

 @Test
    public void findUser() {
        List<User> userList =
        mongoTemplate.findAll(User.class);
        System.out.println(userList);
    }

根据id查询:

    @Test
    public void getById() {
        User user = 
		mongoTemplate.findById("5ffbfa2ac290f356edf9b5aa",
		User.class);
        System.out.println(user);
    }

条件查询:

  @Test
    public void findUserList() {
        Query query = new Query(Criteria
                .where("name").is("test")
                .and("age").is(20));
        List<User> userList = mongoTemplate.find(query, User.class);
        System.out.println(userList);
    }

模糊查询

  @Test
    public void findUsersLikeName() {
        String name = "est";
        String regex = String.format("%s%s%s", "^.*", name, ".*$");
        // 正则大小写不敏感
        Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
        Query query = new Query(Criteria.where("name").regex(pattern));
        List<User> userList = mongoTemplate.find(query, User.class);
        System.out.println(userList);
    }

分页查询:

 @Test
    public void findUsersPage() {
        String name = "est";
        int pageNo = 1;
        int pageSize = 10;

        Query query = new Query();
        String regex = String.format("%s%s%s", "^.*", name, ".*$");
        Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
        query.addCriteria(Criteria.where("name").regex(pattern));
        int totalCount = (int) mongoTemplate.count(query, User.class);
        List<User> userList = mongoTemplate.find(query.skip((pageNo - 1) * pageSize).limit(pageSize), User.class);

        Map<String, Object> pageMap = new HashMap<>();
        pageMap.put("list", userList);
        pageMap.put("totalCount",totalCount);
        System.out.println(pageMap);
    }

修改:

@Test
    public void updateUser() {
        User user = mongoTemplate.findById("5ffbfa2ac290f356edf9b5aa", User.class);
        user.setName("test_1");
        user.setAge(25);
        user.setEmail("493220990@qq.com");
        Query query = new Query(Criteria.where("_id").is(user.getId()));
        Update update = new Update();
        update.set("name", user.getName());
        update.set("age", user.getAge());
        update.set("email", user.getEmail());
        UpdateResult result = mongoTemplate.upsert(query, update, User.class);
        long count = result.getModifiedCount();
        System.out.println(count);
    }

删除:

Query query = 
new Query(Criteria.where("_id").is("5ffbfa2ac290f356edf9b5aa"));
        DeleteResult result = mongoTemplate.remove(query, User.class);
        long count = result.getDeletedCount();
        System.out.println(count);
    }

聚合查询:

		//1 根据医院编号 和 科室编号 查询
        Criteria criteria = Criteria.where("hosCode").is(hosCode).and("depCode").is(depCode);

        //2 根据工作日workDate期进行分组
        Aggregation agg = Aggregation.newAggregation(
                Aggregation.match(criteria),//匹配条件
                Aggregation.group("workDate")//分组字段
                        .first("workDate").as("workDate")
                        //3 统计号源数量
                        .count().as("docCount")
                        .sum("reservedNumber").as("reservedNumber")
                        .sum("availableNumber").as("availableNumber"),
                //排序
                Aggregation.sort(Sort.Direction.DESC,"workDate"),
                //4 实现分页
                Aggregation.skip((page-1)*limit),
                Aggregation.limit(limit)
        );
        //调用方法,最终执行
        AggregationResults<BookingScheduleRuleVo> aggResults =
                mongoTemplate.aggregate(agg, Schedule.class, BookingScheduleRuleVo.class);
        List<BookingScheduleRuleVo> bookingScheduleRuleVoList = aggResults.getMappedResults();
        //分组查询的总记录数
        Aggregation totalAgg = Aggregation.newAggregation(
                Aggregation.match(criteria),
                Aggregation.group("workDate")
        );
        AggregationResults<BookingScheduleRuleVo> totalAggResults =
                mongoTemplate.aggregate(totalAgg, Schedule.class, BookingScheduleRuleVo.class);

在这里插入图片描述
在这里插入图片描述
操作主要封装在Aggregation类中:
在这里插入图片描述
在这里插入图片描述

三,基于MongoRepository

1.实现

Spring Data提供了对mongodb数据访问的支持,我们只需要继承MongoRepository类,按照Spring Data规范就可以了。
在这里插入图片描述
在这里插入图片描述
注意:

  1. 不是随便声明的,而需要符合一定的规范
  2. 查询方法以find | read | get开头
  3. 涉及条件查询时,条件的属性用条件关键字连接
  4. 要注意的是:条件属性首字母需要大写
  5. 支持属性的级联查询,但若当前类有符合条件的属性则优先使用,而不使用级联属性,若需要使用级联属性,则属性之间使用_强制进行连接

2.添加Repository类

@Repository
public interface UserRepository extends 
MongoRepository<User, String> {

}

3.添加测试类

@Autowired
private UserRepository userRepository;

添加:

	@Test
    public void createUser() {
        User user = new User();
        user.setAge(20);
        user.setName("张三");
        user.setEmail("3332200@qq.com");
        User user1 = userRepository.save(user);
    }	

查询所有:

	@Test
    public void findUser() {
        List<User> userList = userRepository.findAll();
        System.out.println(userList);
    }

id查询:

@Test
    public void getById() {
        User user = userRepository.findById("5ffbfe8197f24a07007bd6ce").get();
        System.out.println(user);
    }

条件查询:

	 @Test
    public void findUserList() {
        User user = new User();
        user.setName("张三");
        user.setAge(20);
        Example<User> userExample = Example.of(user);
        List<User> userList = userRepository.findAll(userExample);
        System.out.println(userList);
    }

模糊查询:

 	@Test
    public void findUsersLikeName() {
        //创建匹配器,即如何使用查询条件
        ExampleMatcher matcher = ExampleMatcher.matching() //构建对象
                .withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING) //改变默认字符串匹配方式:模糊查询
                .withIgnoreCase(true); //改变默认大小写忽略方式:忽略大小写
        User user = new User();
        user.setName("三");
        Example<User> userExample = Example.of(user, matcher);
        List<User> userList = userRepository.findAll(userExample);
        System.out.println(userList);
    }

分页查询:

	@Test
    public void findUsersPage() {
        Sort sort = Sort.by(Sort.Direction.DESC, "age");
		//0为第一页
        Pageable pageable = PageRequest.of(0, 10, sort);
		//创建匹配器,即如何使用查询条件
        ExampleMatcher matcher = ExampleMatcher.matching() //构建对象
                .withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING) //改变默认字符串匹配方式:模糊查询
                .withIgnoreCase(true); //改变默认大小写忽略方式:忽略大小写
        User user = new User();
        user.setName("三");
        Example<User> userExample = Example.of(user, matcher);
		//创建实例
        Example<User> example = Example.of(user, matcher);
        Page<User> pages = userRepository.findAll(example, pageable);
        System.out.println(pages);
    }

修改:

@Test
    public void updateUser() {
        User user = userRepository.findById("5ffbfe8197f24a07007bd6ce").get();
        user.setName("张三_1");
        user.setAge(25);
        user.setEmail("883220990@qq.com");
        User save = userRepository.save(user);
        System.out.println(save);
    }

删除:

@Test
    public void delete() {
        userRepository.deleteById("5ffbfe8197f24a07007bd6ce");
    }

四.案例实战

1.地图找房

在这里插入图片描述

@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "tb_house")
public class House {

    @Id
    private ObjectId id; //主键id
    private String title; //房源标题
    private Double price; //总价
    @GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE)
    private GeoJsonPoint location; //x:经度 y:纬度

    @Indexed
    private Integer districtCode; //所属行政区
    @Indexed
    private ObjectId communityId; //所属小区
    @Indexed
    private Integer businessCircleCode; //商圈

    private Long created; //创建时间

}
@Service
public class HouseSearchService {

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 地图找房搜索服务
     *
     * @param maxLongitude 最大经度
     * @param minLongitude 最小经度
     * @param maxLatitude  最大纬度
     * @param minLatitude  最小纬度
     * @param zoom         地图缩放比例值
     * @return
     */
    public List<HouseResultVo> search(Double maxLongitude,
                                      Double minLongitude,
                                      Double maxLatitude,
                                      Double minLatitude,
                                      Double zoom) {

        //收集聚合查询条件
        List<AggregationOperation> operationList = new ArrayList<>();

        //在可视范围内搜索
        Box box = new Box(new double[]{maxLongitude, maxLatitude}, new double[]{minLongitude, minLatitude});
        MatchOperation matchOperation = Aggregation.match(Criteria.where("location").within(box));
        operationList.add(matchOperation);

        int type;
        GroupOperation groupOperation;
        //根据地图的缩放比例进行分组
        if (zoom < 13.5) { //2公里以上
            //按照行政区分组
            groupOperation = Aggregation.group("districtCode");
            type = 1;
        } else if (zoom < 15.5) { //200米以上
            //按照商圈分组
            groupOperation = Aggregation.group("businessCircleCode");
            type = 2;
        } else { //200以下
            //按照小区分组
            groupOperation = Aggregation.group("communityId");
            type = 3;
        }

        groupOperation = groupOperation.count().as("total")
                .avg("price").as("price");
        operationList.add(groupOperation);

        //生成最终的聚合条件
        Aggregation aggregation = Aggregation.newAggregation(operationList);

        //执行查询
        AggregationResults<HouseResultVo> aggregationResults = this.mongoTemplate.aggregate(aggregation, House.class, HouseResultVo.class);

        List<HouseResultVo> houseResultVoList = aggregationResults.getMappedResults();
        if (CollUtil.isEmpty(houseResultVoList)) {
            return Collections.emptyList();
        }

        //填充数据
        switch (type) {
            case 1: {
                //查询行政区数据
                for (HouseResultVo houseResultVo : houseResultVoList) {
                    District district = this.queryDistrictByCode(Convert.toInt(houseResultVo.getCode()));
                    houseResultVo.setName(district.getName());
                    houseResultVo.setLongitude(district.getLocation().getX());
                    houseResultVo.setLatitude(district.getLocation().getY());
                    //价格保留2位小数
                    houseResultVo.setPrice(NumberUtil.roundStr(houseResultVo.getPrice(), 2));
                }
                break;
            }
            case 2: {
                //查询商圈数据
                for (HouseResultVo houseResultVo : houseResultVoList) {
                    BusinessCircle businessCircle = this.queryBusinessCircleByCode(Convert.toInt(houseResultVo.getCode()));
                    houseResultVo.setName(businessCircle.getName());
                    houseResultVo.setLongitude(businessCircle.getLocation().getX());
                    houseResultVo.setLatitude(businessCircle.getLocation().getY());
                    //价格保留2位小数
                    houseResultVo.setPrice(NumberUtil.roundStr(houseResultVo.getPrice(), 2));
                }
                break;
            }
            case 3: {
                //查询小区数据
                for (HouseResultVo houseResultVo : houseResultVoList) {
                    Community community = this.queryCommunityById(new ObjectId(houseResultVo.getCode()));
                    houseResultVo.setName(community.getName());
                    houseResultVo.setLongitude(community.getLocation().getX());
                    houseResultVo.setLatitude(community.getLocation().getY());
                    //价格保留2位小数
                    houseResultVo.setPrice(NumberUtil.roundStr(houseResultVo.getPrice(), 2));
                }
                break;
            }
            default: {
                return Collections.emptyList();
            }
        }

        return houseResultVoList;
    }

    /**
     * 根据code查询行政区数据
     *
     * @param code
     * @return
     */
    private District queryDistrictByCode(Integer code) {
        Query query = Query.query(Criteria.where("code").is(code));
        return this.mongoTemplate.findOne(query, District.class);
    }

    /**
     * 根据code查询商圈数据
     *
     * @param code
     * @return
     */
    private BusinessCircle queryBusinessCircleByCode(Integer code) {
        Query query = Query.query(Criteria.where("code").is(code));
        return this.mongoTemplate.findOne(query, BusinessCircle.class);
    }

    /**
     * 根据code查询小区数据
     *
     * @return
     */
    private Community queryCommunityById(ObjectId id) {
        return this.mongoTemplate.findById(id, Community.class);
    }
}

2.运动健康

@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "tb_route")
@JsonIgnoreProperties(ignoreUnknown = true)
public class Route {

    @Id
    @JsonIgnore
    private ObjectId id; //主键id
    private String title; //路线标题
    @Indexed
    private Long userId; //创建路线的用户id
    private Boolean isShare; //是否投稿
    private Integer size; //轨迹点数量
    private Double distance; //此段轨迹的里程数,单位:米
    private String time; //运动时间,格式:mm:ss
    private Double speed; //平均速度,单位:km/h
    @Indexed
    private Long startTime; //创建路线时间戳
    private Long endTime; //结束路线时间戳
    private LocationPoint startPoint; //起点信息
    private LocationPoint endPoint; //终点信息
    private List<LocationPoint> points; //历史轨迹点列表

    //路线的位置,使用起点坐标作为路线坐标
    @GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE)
    @JsonIgnore
    private GeoJsonPoint location; //x:经度 y:纬度

    @JsonIgnore
    @Indexed
    private Integer status; //状态,1:开始,0:已结束

    @Transient //不存储到MongoDB,仅用于前端显示
    private Double routeDistance; //路线与我的距离
}

在这里插入图片描述

 /**
     * 创建路线
     *
     * @return
     */
    public String createRoute() {
        Route route = new Route();
        route.setId(ObjectId.get());
        route.setUserId(UserThreadLocal.get());
        route.setStatus(1);
        route.setIsShare(false); //默认不投稿
        route.setStartTime(System.currentTimeMillis());

        //将数据保存到MongoDB
        this.mongoTemplate.save(route);

        String routeId = route.getId().toString();
        //百度地图鹰眼服务中创建Entity
        Boolean bool = this.baiduService.createEntity(routeId);
        if (bool) {
            //成功
            return routeId;
        }

        //失败
        return null;
    }

在这里插入图片描述

 /**
     * 更新路线(结束运动)
     *
     * @param routeId 路线id
     * @param title   路线标题
     * @return
     */
    public Object updateRoute(String routeId, String title) {
        //判断路线是否已经结束,如果已经结束就不能再次结束
        Route route = this.queryRouteById(routeId);
        if (null == route) {
            return ErrorResult.builder()
                    .errCode("500").errMessage("结束运动失败,路线不存在.").build();
        }

        if (route.getStatus() == 0) {
            return ErrorResult.builder()
                    .errCode("501").errMessage("结束运动失败,该路线已经结束.").build();
        }

        //更新路线数据
        Update update = Update.update("title", title)
                .set("status", 0)
                .set("endTime", System.currentTimeMillis());

        UpdateResult updateResult = this.mongoTemplate.updateFirst(this.createQuery(routeId), update, Route.class);
        if (updateResult.getModifiedCount() == 1) {
            //查询百度地图鹰眼服务中的路线轨迹点,更新到路线数据中,异步的操作
            this.routeInfoService.updateRouteInfo(routeId, UserThreadLocal.get());

            //结束沿着路线骑行的关系
            Update runRouteUpdate = Update.update("status", 0)
                    .set("updated", System.currentTimeMillis());
            //条件
            Query runRouteQuery = Query.query(Criteria.where("routeId").is(routeId)
                    .and("userId").is(UserThreadLocal.get())
                    .and("status").is(1));
            UpdateResult runRouteUpdateResult = this.mongoTemplate.updateFirst(runRouteQuery, runRouteUpdate, RunRoute.class);

            //更新成功
            return runRouteUpdateResult.getModifiedCount() == 1;
        }

        return ErrorResult.builder()
                .errCode("502").errMessage("结束运动失败.").build();
    }

在这里插入图片描述

 /**
     * 更新路线数据,查询鹰眼服务中的轨迹数据,更新到Mongodb中
     *
     * @param routeId
     * @param userId
     * @return
     */
    @Async //异步执行
    public CompletableFuture<String> updateRouteInfo(String routeId, Long userId) {
        //当前是异步执行,是在一个新的线程中,所以需要将用户id存储到当前的线程中,baiduService中才能获取到用户id
        UserThreadLocal.set(userId);
        //根据路线id查询路线数据
        Route route = this.mongoTemplate.findById(new ObjectId(routeId), Route.class);

        Long startTime = route.getStartTime() / 1000; //开始时间,精确到秒
        Long endTime = route.getEndTime() / 1000; //结束时间,精确到秒

        //查询轨迹数据
        Route routeEntity = this.baiduService.queryEntity(routeId, startTime, endTime);
        if (null == routeEntity) {
            return CompletableFuture.completedFuture("error");
        }
        //计算运动时间
        String time;
        try {
            time = TimeUtils.formatTime(routeEntity.getEndPoint().getLocTime() - routeEntity.getStartPoint().getLocTime());
        } catch (Exception e) {
            time = "00:00";
        }

        //计算平均速度,每个点速度总和 / 轨迹点总数
        Double speed;
        try {
            speed = routeEntity.getPoints().stream().mapToDouble(LocationPoint::getSpeed).sum() / routeEntity.getSize();
            speed = NumberUtil.round(speed, 2).doubleValue();
        } catch (Exception e) {
            speed = 0.00;
        }

        //更新数据
        Update update = Update.update("size", routeEntity.getSize())
                .set("distance", NumberUtil.round(routeEntity.getDistance(), 2).doubleValue())
                .set("startPoint", routeEntity.getStartPoint())
                .set("endPoint", routeEntity.getEndPoint())
                .set("endPoint", routeEntity.getEndPoint())
                .set("points", routeEntity.getPoints())
                .set("location", new GeoJsonPoint(routeEntity.getStartPoint().getLongitude(),
                        routeEntity.getStartPoint().getLatitude()))
                .set("time", time)
                .set("speed", speed);

        //更新数据到MongoDB
        Query query = Query.query(Criteria.where("id").is(routeId));
        this.mongoTemplate.updateFirst(query, update, Route.class);

        return CompletableFuture.completedFuture("ok");
    }

在这里插入图片描述

  /**
     * 查询附近的路线
     *
     * @param longitude 当前用户所在位置的经度
     * @param latitude  当前用户所在位置的纬度
     * @param distance  查询的距离,单位:km,默认10km
     * @return
     */
    public Object queryNearRoute(Double longitude, Double latitude, Double distance) {

        //构造查询,附近搜索数据,条件:路线已经结束并且已经分享
        NearQuery nearQuery = NearQuery.near(longitude, latitude, Metrics.KILOMETERS)
                .maxDistance(distance)
                .query(Query.query(Criteria.where("isShare").is(true).and("status").is(0)));

        //geo的查询
        GeoResults<Route> geoResults = this.mongoTemplate.geoNear(nearQuery, Route.class);
        if (CollUtil.isEmpty(geoResults.getContent())) {
            //没有数据
            return Collections.emptyList();
        }
        return geoResults.getContent().stream()
                .map(result -> {
                    Route route = result.getContent();
                    //数据拷贝
                    NearRouteVo nearRouteVo = BeanUtil.toBeanIgnoreError(route, NearRouteVo.class);
                    nearRouteVo.setLongitude(route.getLocation().getX());
                    nearRouteVo.setLatitude(route.getLocation().getY());
                    nearRouteVo.setRange(NumberUtil.round(result.getDistance().getValue(), 2).doubleValue());//路线与我的距离
                    return nearRouteVo;
                }).collect(Collectors.toList());
    }
  /**
     * 按照时间范围查询路线(包含时间边界)
     *
     * @param userId
     * @param minDate
     * @param maxDate
     * @return
     */
    public List<Route> queryRouteListByDate(Long userId, Long minDate, Long maxDate) {
        Query query = Query.query(Criteria.where("endTime").gte(minDate)
                .and("status").is(0)
                .and("userId").is(userId)
                .andOperator(Criteria.where("endTime").lte(maxDate)));
        return this.mongoTemplate.find(query, Route.class);
    }
  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

活跃的咸鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值