MongoDB项目实战1-连表相关

一.连表相关

1.一对多(实体类注解的形式)

例如部门类型对部门

部门类型实体类

@Data
@Document(collection = "departmentType")
public class DepartmentTypeEntity implements Serializable {
    @Id
    private String id;
    private String name;

    /**
     * 下属部门
     */
    @DBRef
    private List<DepartmentEntity> departmentEntityList;

    @LastModifiedDate
    private Long updateTime;
    @CreatedDate
    private Long createTime;
}

部门实体类

@Data
@Document(collection = "department")
public class DepartmentEntity implements Serializable {
    @Id
    private String id;
    private String name;

    /**
     * 所属部门类型
     */
    @DBRef
    private DepartmentTypeEntity departmentType;

    @LastModifiedDate
    private Long updateTime;
    @CreatedDate
    private Long createTime;
}

@DBRef    连表的注解,两个实体类中连表只能选其一,就是如果在一个实体用了这个连表,那么它对应的表就不能用了,不然在查询的时候会死循环

根据你的需求去选择相对应的方式,如果你需要查出部门类型和他的下属所有部门可以用在one的实体上加上many的List集合,如果你需要在查部门时查到它的所属部门类型,就在many加上one的实体,构建连接

在我们添加一个新的部门时,如果用的many列表的形式

// 保存部门实体到数据库
departmentRepository.save(departmentEntity);

// 获取相应的部门类型实体
DepartmentTypeEntity departmentTypeEntity = departmentTypeRepository.findById(Id);
List<DepartmentEntity> departmentEntityList = departmentTypeEntity.getDepartmentEntityList();
if (departmentEntityList == null) {
departmentEntityList = new ArrayList<>();
}
departmentEntityList.add(departmentEntity);

// 将新的列表设置回 departmentTypeEntity 对象中
departmentTypeEntity.setDepartmentEntityList(departmentEntityList);
departmentTypeRepository.save(departmentTypeEntity);

// 重新保存部门实体,以确保关联引用保存到数据库
departmentRepository.save(departmentEntity);

另一种

// 保存部门实体到数据库
departmentRepository.save(departmentEntity);
// 获取相应的部门类型实体
DepartmentTypeEntity departmentTypeEntity = departmentTypeRepository.findById(Id);
// 设置部门实体与部门类型实体之间的关联引用
departmentEntity.setDepartmentType(departmentTypeEntity);
departmentTypeRepository.save(departmentTypeEntity);
// 重新保存部门实体,以确保关联引用保存到数据库
departmentRepository.save(departmentEntity);

 2.Aggregation 聚合操作(连表或其他操作)

做这些复杂操作是因为需求要求是根据上级的上级的排序字段进行排序,还有一种方法就是把这些字段都拿出来,一级一级放到实体,这就有一个问题,在更新操作的时候一级一级的把排序字段更新,如果是最上级的,被修改了,要遍历所有下级的下级。。好多下级的话会很慢(这样用Aggregation 连表数据量大的话也慢,最后是限制条件了),解决办法就是异步的形式,做一个开关更新就让它自己去慢慢遍历更新,这边先返回结果,我的想法是用mq,如果各位友友有新的想法欢迎讨论

Aggregation 是 MongoDB 的聚合框架,用于对数据进行复杂的数据转换和处理。可以使用 Aggregation 来执行类似于 SQL 中的 GROUP BY、JOIN 等操作。

public Map<String, Object> pageQueryDep(List<Criteria> criteriaList, String depTypeId, PageRequest pageRequest) {
        // 构建基本查询条件
        Criteria criteria = new Criteria();
        if (criteriaList != null && !criteriaList.isEmpty()) {
            criteria.andOperator(criteriaList.toArray(new Criteria[0]));
        }
        List<DepartmentItem> resultList = new ArrayList<>();
        Criteria depId = new Criteria();
        // 连部门类型表
        LookupOperation lookupOperation11 = LookupOperation.newLookup()
                // 被关联表
                .from("DepartmentTypeEntity")
                // 主表关联字段
                .localField("departmentType.$id")
                // 被关联字段
                .foreignField("_id")
                // 别名
                .as("depType");

        ProjectionOperation project11 = Aggregation.project("name","sid","departmentType","isSource","level","info","updateTime","createTime")
                .and("depType._id").as("depTyId")
                .and("depType.level").as("depTypeLevel");
        ProjectionOperation project12 = Aggregation.project("name","sid","departmentType","isSource","level","info","updateTime","createTime","depTypeLevel")
                .and(ConvertOperators.Convert
                        .convertValueOf("$depTyId").to("string")).as("depTyId");

        if (!StringUtils.isSpace(depTypeId)) {
            depId = Criteria.where("depTyId").in(depTypeId);
        }
        MatchOperation matchOperation = Aggregation.match(depId);
        AggregationOperation matchOperation1 = Aggregation.match(criteria);
        Aggregation aggregation2 = Aggregation.newAggregation(matchOperation1,
                lookupOperation11, project11, Aggregation.unwind("depTyId"),
                project12,matchOperation,
                Aggregation.sort(Sort.by(Sort.Order.asc("depTypeLevel"), Sort.Order.asc("level"))),
                Aggregation.skip(pageRequest.getOffset()),
                Aggregation.limit(pageRequest.getPageSize())
        );
        Aggregation aggregationCount = Aggregation.newAggregation(matchOperation1,
                lookupOperation11, project11, Aggregation.unwind("depTyId"),
                project12,matchOperation,
                Aggregation.group().count().as("totalCount"));
        // 执行聚合查询并获取结果
        List<CountResult> countResult = mongoTemplate.aggregate(aggregationCount, "DepartmentEntity", CountResult.class).getMappedResults();
        // 获取总记录数
        long totalCount = 0;
        if (!countResult.isEmpty()) {
            totalCount = countResult.get(0).getTotalCount();
            resultList = mongoTemplate.aggregate(aggregation2, "DepartmentEntity", DepartmentEntity.class).getMappedResults();
        }
        // 构建并返回结果
        Map<String, Object> pageResult = new LinkedHashMap<>();
        pageResult.put("pageCount", pageRequest.getPageSize());
        pageResult.put("lineCount", totalCount);
        pageResult.put("list", resultList);
        return pageResult;
    }

*拆分详解

Criteria criteria = new Criteria();
if (criteriaList != null && !criteriaList.isEmpty()) {
    criteria.andOperator(criteriaList.toArray(new Criteria[0]));
}

这部分首先创建了一个空的查询条件 Criteria,然后在满足特定条件的情况下,使用 andOperator 方法将多个条件进行逻辑 AND 运算,以构建更复杂的查询条件。

criteriaList.toArray(new Criteria[0]) 这段代码是将一个 List<Criteria> 转换为一个 Criteria 数组,使用 new Criteria[0] 作为参数,它的目的实际上只是为了提供一个表示 Criteria 类型的空数组。这是一个常见的技巧,目的是在不需要的情况下避免创建一个新的数组对象。

LookupOperation lookupOperation11 = LookupOperation.newLookup()
        .from("DepartmentTypeEntity")
        .localField("departmentType.$id")
        .foreignField("_id")
        .as("depType");

这部分使用 LookupOperation 执行 MongoDB 的聚合操作 $lookup,将当前集合中的 departmentType.$id 字段与 DepartmentTypeEntity 集合的 _id 字段关联。(连表操作相当于join)

ProjectionOperation project11 = Aggregation.project("name","departmentType","level","info","updateTime","createTime")
        .and("depType._id").as("depTyId")
        .and("depType.level").as("depTypeLevel");
ProjectionOperation project12 = Aggregation.project("name","departmentType","level","info","updateTime","createTime","depTypeLevel")
        .and(ConvertOperators.Convert
                .convertValueOf("$depTyId").to("string")).as("depTyId");

这两个 ProjectionOperation 部分进行投影操作,选择需要的字段,其中 and 方法用于将其他字段添加到投影中,这里还包括了一个类型转换操作。类型转换是因为mongo的id是它自己的类型,你通过这个去查询id对不上查不到,要把它转换成我们能查到的那个obj的_id,也就是数据库里显示的_id.

MatchOperation matchOperation = Aggregation.match(depId);
AggregationOperation matchOperation1 = Aggregation.match(criteria);

这两个部分分别使用 MatchOperation 进行匹配操作,通过传入的 depId 条件和 criteria 条件筛选出符合要求的文档。

这里为什么把查询条件分成两块,是因为mongo的聚合操作是按顺序执行的,你传进来的条件必须是已经整合的投影里的字段,所以顺序很重要

Aggregation aggregation2 = Aggregation.newAggregation(matchOperation1,
        lookupOperation11, project11, Aggregation.unwind("depTyId"),
        project12,matchOperation,
        Aggregation.sort(Sort.by(Sort.Order.asc("depTypeLevel"), Sort.Order.asc("level"))),
        Aggregation.skip(pageRequest.getOffset()),
        Aggregation.limit(pageRequest.getPageSize())
);
Aggregation aggregationCount = Aggregation.newAggregation(matchOperation1,
        lookupOperation11, project11, Aggregation.unwind("depTyId"),
        project12,matchOperation,
        Aggregation.group().count().as("totalCount"));

这部分创建了两个 Aggregation 对象,分别为实际的查询操作和总记录数的查询操作,包括了之前的所有操作,还有排序、分页等。

List<CountResult> countResult = mongoTemplate.aggregate(aggregationCount, "DepartmentEntity", CountResult.class).getMappedResults();
long totalCount = 0;
if (!countResult.isEmpty()) {
    totalCount = countResult.get(0).getTotalCount();
    resultList = mongoTemplate.aggregate(aggregation2, "DepartmentEntity", DepartmentEntity.class).getMappedResults();
}

这部分使用 mongoTemplate.aggregate 方法执行聚合查询,并获取查询结果。首先执行了总记录数的查询,然后根据结果是否为空来判断是否执行实际的数据查询。

以上就是你提供的代码中每一块的主要作用解释。这些操作一起构建了一个复杂的MongoDB聚合查询,涵盖了数据关联、投影、条件筛选、排序和分页等操作。最终,你可以从 resultList 中获取实际查询的数据,从 totalCount 中获取总记录数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值