文章前面

关于版本

依赖

版本

springboot

2.0.8.RELEASE

mongodb

4.0.14

本内容只是为了介绍mongodb最基础的使用以及配置,作为一个知名的数据库,其存在相当多的高级用法,展开来介绍内容会相当多,当然本人并非相关领域的大神,下面内容只不过整理了自己日常使用的一些积累。是对自己经验的积累,也希望能帮助后来的同学

关于项目

本内容也是我尝试整理工作中接触过各种工具在springboot中使用的方法。下面介绍的所有方法都已经提供了测试用例。因为每个例子涉及代码较多,所以文章中只贴出了部分代码。全部的代码在这里:https://gitee.com/daifyutils/springboot-samples。

MongoDB表(集合)的定义

在MySQL中存储一类数据的集合体被叫做表(table),而在mongodb中被叫做集合(collections)。而mongodb中每一条数据被称为文档(document)。

于传统的MySQL需要提前在数据库中完成表创建不同,使用Spring Boot操作mongodb的时候不需要再数据库中创建集合结构,而通过将实体定义为mongodb实体来完成集合的创建。

定义实体需要的注解

Spring Data为了定义mongodb的集合结构,提供了下面几个注解

注解

说明

@Document

标识当前实体是一个mongodb实体

@Id

标识当前字段为主键,需要注意的是此注解只能存在一个

@Indexed

标识当前字段需要添加索引,添加索引后的字段,在进行查询操作的时候会提高速度

@CompoundIndex

标识一个联合索引

@Field

对当前字段的额外内容进行定义,主要是用来定义集合中字段实际名称

@Transient

标识此字段为java属性而非mongodb字段

@DBRef

将此字段同另外一个mongodb的文档进行关联

注解的使用

注解的使用

现在创建下面两个实体

import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.CompoundIndex;
import org.springframework.data.mongodb.core.index.CompoundIndexes;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;

import java.util.List;

@Data
@Document(collection = "Province")
@CompoundIndexes({
    @CompoundIndex(name = "userName_age_idx", def = "{'provinceName': 1, 'provinceCode': -1}")
})
public class Province {

    @Id
    private String id;

    private String provinceName;

    private String provinceCode;

    @DBRef
    private List<City> cities;
}
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;

@Data
@Document(collection = "City")
public class City {

    @Id
    private String id;

    private String cityName;

    @Indexed
    @Field(value = "code")
    private String cityCode;

    @Transient
    private String cityRemark;

}

关于上面的例子中使用的注解作用:

@Document

上面例子中使用了@Document定义了两个数据集合ProvinceCity

@Id

数据集合ProvinceCity中id字段被定义为主键。

@Indexed

数据集合City中将cityCode字段添加了索引。

@Field(value = “code”)

在定义mongodb字段的时候,不添加@Field的注解时保存到集合中的字段名和实体中的属性名保持一致,而数据集合City中cityCode字段添加了上面注解后实际保存到集合中的字段会被设置为code

@Transient

此字段标识的cityRemark只是作为java属性来使用,在将数据保存至mongodb的时候此内容并不会保存下来。

@DBRef

将此字段和另外一个mongodb文档进行关联,添加此注解和不添加此注解的主要区别在于。当不添加此注解的时候,此字段中的City内容会作为Province文档的一部分保存起来,而当添加此注解后Province文档中只会维持City的主键值。

当我们创建一些ProvinceCity数据后可以看到Province中数据内容如下:

mongodb 自增字段id mongodb创建字段_mongodb

@CompoundIndex

provinceNameprovinceCode字段作为一个联合索引。数字参数指定索引的方向,1为正序,-1为倒序。这里需要注意在使用@Indexed标识索引的时候,创建集合后会自动创建对应的索引,而使用@CompoundIndex标识索引的时候是在新增数据之后关联索引才产生。

mongodb简单的CRUD

通过Spring Data操作mongodb主要是使用两种数据操作工具,一种是MongoTemplate他是mongodb数据访问模板提供了基础的数据访问接口。而另外一种是创建一种继承MongoRepository的数据访问接口,MongoRepository是基于JPA规则的数据访问接口,它提供了基础的CRUD操作,虽然无法实现一些复杂的统计以及联表查询,但是因为提供了自定义方法使其对单表的操作上会非常简单。

基于MongoRepository的CRUD

保存&更新

MongoRepository的保存和更新都是使用save方法,而对于批量处理MongoRepository提供了saveAll方法

@Override
    public void saveOne(Product product) {
        productRepository.save(product);
    }

    @Override
    public void batchSave(List<Product> products) {
        productRepository.saveAll(products);
    }

删除

MongoRepository关于删除提供的是delete(实体对象)的方法,此方法需要将mongodb实体作为参数传递进来,很多时候我们的删除都是基于ID的,所以deleteById使用的频率会比较多一些。但是基于ID的删除MongoRepository并没有提供批量处理的方法,但是可以使用自定义方法创建一个deleteAllByIdIn方法来实现批量删除

@Override
    public void deleteOne(String id) {
        productRepository.deleteById(id);
    }

    @Override
    public void batchDelete(List<String> idList) {
        productRepository.deleteAllByIdIn(idList);
    }

查询

MongoRepository关于查询需要配合Example使用,此类会将mongodb实体中的内容转换为查询的条件来进行筛选,而对于需要进行分页或排序操作的查询,需要配合Pageable和Sort的使用来实现数据分页。

@Override
    public List<Product> queryByEntity(Product product) {

        Example<Product> of = Example.of(product);

        List<Product> all = productRepository.findAll(of);
        return all;
    }

    @Override
    public Page<Product> pageByEntity(Product product, int page, int size) {
        Example<Product> of = Example.of(product);
        Pageable pageable = PageRequest.of(page - 1, size);
        Page<Product> all = productRepository.findAll(of, pageable);

        return all;
    }

需要注意,有些时候我们创建自定义方法后需要进行分页查询,此时关于分页的内容并不需要在方法名称上做任何改动,只需要在最后添加入参Pageable,并将返回内容修改为Page<{mongodb数据实体}>此方法就可以使用分页参数并返回分页数据了。

基于MongoTemplate的CRUD

保存

MongoTemplate虽然很多查询条件需要用户自行设置,但是其也提供了基础的查询接口。比如MongoTemplate提供了save方法来保存数据。

@Override
    public void saveOneByTemplate(Product product) {
        mongoTemplate.save(product);
    }

更新

MongoTemplate针对数据更新提供了upsertupdateFirstupdateMulti等方法上面几种方法都是基于doUpdate的实现的。这里就需要使用者提供一个定位更新数据的Query

/**
     * 执行插入。
     * @param queryProduct
     * @param product
     */
    @Override
    public void updateOneByTemplate(Product queryProduct,Product product) {
        Query query = new Query();
        Criteria criteria = Criteria.byExample(Example.of(queryProduct));
        query.addCriteria(criteria);
        Update update = new Update();
        update.set("id",product.getId());
        update.set("productName",product.getProductName());
        update.set("productMoney",product.getProductMoney());
        mongoTemplate.updateMulti(query, update,Product.class);
    }

删除

MongoTemplate的提供了remove方法来进行删除,当然基于删除的操作也需要提供提供一个定位更新数据的Query。MongoTemplate也提供了一个findAllAndRemove发现并删除的接口,当然我实际开发中此接口使用的并不算多。

@Override
    public void deleteOneByTemplate(String id) {
        Query query = new Query();
        Criteria criteria = Criteria.where("id").is(id);
        query.addCriteria(criteria);
        mongoTemplate.remove(query,Product.class);
    }

    @Override
    public void batchDeleteByTemplate(List<String> idList) {
        Query query = new Query();
        Criteria criteria = Criteria.where("id").in(idList);
        query.addCriteria(criteria);
        mongoTemplate.remove(query,Product.class);
    }

查询

MongoTemplate的查询是使用findXXX来实现的,核心的内容依旧是Query。和使用MongoRepository不同,MongoTemplate没有提供一个简单的分页查询接口,需要根据先求总数然后获取分页数据。需要注意的是其分页参数需要在Query中设置,主要是skip(起始行数)以及limit(显示行数)

@Override
    public List<Product> queryByEntityByTemplate(Product product) {
        Query query = new Query();
        Criteria criteria = Criteria.byExample(Example.of(product));
        query.addCriteria(criteria);

        List<Product> products = mongoTemplate.find(query, Product.class);
        return products;
    }

    @Override
    public Page<Product> pageByEntityByTemplate(Product product, long startNum, int size) {
        Query query = new Query();
        Criteria criteria = Criteria.byExample(Example.of(product));
        query.addCriteria(criteria);
        long count = mongoTemplate.count(query, Product.class);
        // 分页参数
        query.skip(startNum).limit(size);
        List<Product> products = mongoTemplate.find(query, Product.class);
        Page page = new PageImpl(products, Pageable.unpaged(),count);
        return page;
    }

个人水平有限,上面的内容可能存在没有描述清楚或者错误的地方,假如开发同学发现了,请及时告知,我会第一时间修改相关内容,也希望大家看在这个新春佳节只能宅到家中埋头苦逼的码代码的情况下,能给我点一个赞。你的点赞就是我前进的动力。在这里也祝大家新春快乐。