mongoDb查询中遇到的问题

李明 -
mongoDb查询中遇到的问题
问题一 springboot下mongoDb的查询

数据准备:


public class Student {

    @Id
    private Long id;
    private String name;
    /**
     * 关联班级ID
     */
    private Long classId;
}
public class StudentClass  {
    @Id
    private Long id;
    private String className;
    private Long schoolId;
}

按照学生姓名进行查询:

Query query = new Query();
    
//查找方式,表示根据name构造模糊查询
Pattern pattern = Pattern.compile("^.*" + name + ".*$", Pattern.CASE_INSENSITIVE);

//与表中name进行对应
Criteria criteria = Criteria.where("name").regex(pattern);
query.addCriteria(criteria)

//使用mongotemplate进行查询
mongoTemplate.find(query, Student.class);

根据学生查询class
可以看到学生实体中共并没有class字段,那么要怎么做到查询学生的时候连带class一起返回呢?

  LookupOperation lookup = LookupOperation.newLookup()
                //从表(关联的表)
                .from("studentClass")
                //主表中与从表相关联的字段
                .localField("classId")
                //从表与主表相关联的字段
                .foreignField("id")
                //查询出的从表集合 命名
                .as("class");

        Aggregation agg = Aggregation.newAggregation(lookup);

        mongoTemplate.aggregate(agg, "student", Map.class)

返回的数据样式:
图片.png
可以看到这样查询后会将从表查询到的数据添加到主表数据中,也就是说在mongoDb中并不需要在实体中加入关联实体,只需加入关联实体的Id即可。
如果还想要加入其他查询条件,只需像下面这样加入criteria即可

MatchOperation match = Aggregation.match(criteria);
Aggregation agg = Aggregation.newAggregation(lookup, match);
问题二 进行分页时出现如下报错
ConverterNotFoundException: No converter found capable of converting from type [java.util.Date] to type [java.sql.Timestamp]

报错原因:
再进行issue分页查询时出现如下报错,issue与其他实体的区别就是拥有java.sql.Timestamp类型字段。
但是进行保存操作时却没有问题,推测mongoDb在从数据库中获取时间时获取到的时间统一为java.util.Date格式。
即进行查询时返回的数据为java.util.Date.
所以要做的就是在查询到数据时先将其类型转化为对象的所需类型即java.sql.Timestamp
要实现此功能就需要进行如下操作
先声明类型转换器,表示接收到后进行类型转换

/**
 * mongpoDB时间类型转换器
 */
@ReadingConverter
public class TimestampConverter implements Converter<Date, Timestamp> {

  @Override
  public Timestamp convert(Date source) {
    return new Timestamp(source.getTime());
  }

}

再去congfig中引入此转换器

@Configuration
public class MongoConfig {

  @Bean
  public MongoCustomConversions mongoCustomConversions() {
    List<Object> converters = new ArrayList<>();
    converters.add(new TimestampConverter());
    return new MongoCustomConversions(converters);
  }
}
问题三 分页错误

开始时写的分页算法有错误,导致返回的page中的content还是总数据。即前台传入分页大小页号,后台仍是返回所有的数据。

  public Page<Issue> page(String name, Pageable pageable) {
    org.springframework.util.Assert.notNull(pageable, "传入的Pageable不能为null");
    //构造查询条件进行查询
    Query query = new Query();
       query.addCriteria(IssueCriteria.containingName(name))
            .addCriteria(IssueCriteria.belongToProject(projectId))
            .addCriteria(IssueCriteria.beClosed(closed));
    List<Project> Issues = mongoTemplate.find(query, Project.class);
    long total = mongoTemplate.count(query, Issue.class);

    return new PageImpl<Project>(Issues, pageable, total);
  }

很明显如上算法只会返回总的数据,所以进行了如下改进

package club.yunzhi.nonlycode.repository.criteria;

import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.core.query.Query;

/**
 * 添加页数查询条件
 * 跳过之前页内容,并将返回数限制为页面大小
 */
public class GetTotalPageQuery {

  public static Query getPageQuery(Query query, Pageable pageable) {
    Query newQuery = query;
    newQuery.skip((long) (pageable.getPageNumber() - 1) * pageable.getPageSize())
            .limit(pageable.getPageSize());
    return newQuery;
  }
}
  public Page<Issue> page(String name, Long projectId, Boolean closed, Pageable pageable) {
    org.springframework.util.Assert.notNull(pageable, "传入的Pageable不能为null");
    org.springframework.util.Assert.notNull(projectId, "传入的projectId不能为null");
    //构造查询条件进行查询
    Query query = new Query();
    query.addCriteria(IssueCriteria.containingName(name))
            .addCriteria(IssueCriteria.belongToProject(projectId))
            .addCriteria(IssueCriteria.beClosed(closed));
    //获取总数
    long total = mongoTemplate.count(query, Issue.class);

    Query pageQuery = GetTotalPageQuery.getPageQuery(query, pageable);
    //获取分页情况下具体页信息
    List<Issue> issues = mongoTemplate.find(pageQuery, Issue.class);
    return new PageImpl<Issue>(issues, pageable, total);
  }

注意:下面操作是直接对query的地址进行操作也就是说在调用完pageQuery之后原本传入的query与pagequery完全相同,也进行了分页配置即只返回限制部分数据所以在上面写法中构造完query后就对其进行count查询,如果将其放于最后则total恒等于page.size().

.skip((long) (pageable.getPageNumber() - 1) * pageable.getPageSize())
            .limit(pageable.getPageSize());

但是按照上面这样写还会出现新的问题,比如我们当前得到的数据总数为34条,每页大小为5,可得总共有七页,这在点击前六页时都是没问题的
图片.png
但是当点击第七页时总页数就会变为8页并且第八页为空
图片.png
并且在后台打断点显示每次返回的totak数据不变总是34但是在前台打断点时page的totalElements却显示错误如下(前五页没有问题)可得知page中首页尾页并没有进行设置导致出现问题。
图片.png
于是尝试新的方法,之后又发现query可直接接受pageble变量从而进行分页
query.with(pageable);
经过测试没有问题,但是pageNumber和前台对接出现问题,即前台分页为1时时传入的number为1,但是在根据mongoTemplate查询时首页number为0与使用reponsitory查询时规则不同,所以目前只能在前台出入pagenumber时进行操作,后期可根据需求再进行统一改动。

特别申明:本文内容来源网络,版权归原作者所有,如有侵权请立即与我们联系(cy198701067573@163.com),我们将及时处理。

Tags 标签

springbootmongodb

扩展阅读

加个好友,技术交流

1628738909466805.jpg