文章目录

  • 1. 简介
  • 2. 实体类
  • 3. Repository方式
  • 3.1 MongoRepository
  • 3.2 QueryByExampleExecutor
  • 4. MongoTemplate方式
  • 4.1 查询文档方法
  • 4.2 Query方法
  • 4.3 Criteria方法
  • 4.4 示例
  • 5. 文档资料


1. 简介

Spring Data MongoDB属于Spring Data套装中的一个工具,提供了对MongoDB数据库操作的封装。

相对于直接使用MongoDB的驱动,Spring Data MongoDB可能更有优势,不管是简单还是复杂的操作。

对于简单的操作Spring Data MongoDB甚至基本都不用写什么代码。

对于复杂的操作Spring Data MongoDB在抽象层做得更好,更方便维护。

2. 实体类

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;

import java.util.List;

@Document(collection = "student")
public class Student {

    @Id
    private String id;

    private String name;

    private Integer age;

    @DBRef
    private List<Teacher> teachers;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public List<Teacher> getTeachers() {
        return teachers;
    }

    public void setTeachers(List<Teacher> teachers) {
        this.teachers = teachers;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", teachers=" + teachers +
                '}';
    }
}

MongoDB的所有的文档都必须要有一个id。

Spring Data MongoDB咋确认类的哪一个字段是id呢?

  1. 如果一个字段有org.springframework.data.annotation.Id注解,它会被当做mongodb的id
  2. 如果没有注解,但是字段名称是id,那么它会被当做mongodb的id

注解

说明

@Id

标识文档ID,唯一

@DBRef

一对多关系,不使用内嵌文档方式,而是分开存储时使用,添加的时候并不会添加关联文档,只会在查找的时候根据id查找

@Indexed

在该字段上创建索引,@Indexed(unique = true)

@Document

改类为MongoDB文档,@Document(collection=“mongodb”)

@Transient

不保存字段

@CompoundIndex

复合索引,类上,@CompoundIndex(name = “age_idx”, def = “{‘name’: 1, ‘age’: -1}”),1表示升序,-1表示降序

@GeoSpatialIndexed

字段为地理信息索引

@PersistenceConstructor

构造函数,用于数据库获取的数据实例化为对象

3. Repository方式

Spring Data MongoDB是Spring Data套装中的一个,那当然可以使用Repository的方式。

首先,如果使用注解还是需要@EnableMongoRepositories,指定要扫描Repository的包。

@Configuration
@EnableMongoRepositories(basePackages = {"vip.mycollege.mongodb.repository"})
public class MongodbConfig {
}

3.1 MongoRepository

基本操作,只需要继承MongoRepository就可以了

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.repository.MongoRepository;
import vip.mycollege.mongodb.entity.Student;

public interface StudentRepository extends MongoRepository<Student,String> {
    Page<Student> findByNameLike(String name, Pageable pageable);
}

不需要实现类,增删改查都可以直接用,还可以通过遵循命名规范定义接口方法,那也不需要具体实现方法。

下面是测试代码:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.test.context.junit4.SpringRunner;
import vip.mycollege.mongodb.entity.Student;
import vip.mycollege.mongodb.entity.Teacher;

import javax.annotation.Resource;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

@RunWith(SpringRunner.class)
@SpringBootTest
public class StudentRepositoryTest {

    @Resource
    private StudentRepository studentRepository;

    @Test
    public void findByUserNameLike(){
        PageRequest pageRequest = PageRequest.of(0, 5);
        Page<Student> page = studentRepository.findByNameLike("tim", pageRequest);
        System.out.println(page.getTotalPages());
        page.getContent().forEach(System.out::println);
    }

    @Test
    public void findAll(){
        List<Student> students = studentRepository.findAll();
        students.forEach(System.out::println);
    }

    @Test
    public void saveAll(){
        List<Student> users = getUsers(10);
        studentRepository.saveAll(users);
    }
}

3.2 QueryByExampleExecutor

当然也可以使用QueryByExampleExecutor方式

import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.repository.query.QueryByExampleExecutor;
import vip.mycollege.mongodb.entity.Student;

public interface StudentQueryByExampleExecutor extends MongoRepository<Student,String>, QueryByExampleExecutor<Student> {
}

继承MongoRepository是为了让Spring Data为这个接口生成代理类。

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.domain.ExampleMatcher.GenericPropertyMatchers;
import org.springframework.test.context.junit4.SpringRunner;
import vip.mycollege.mongodb.entity.Student;

import javax.annotation.Resource;
import java.util.Iterator;

@RunWith(SpringRunner.class)
@SpringBootTest
public class StudentQueryByExampleExecutorTest {

    @Resource
    private StudentQueryByExampleExecutor studentQueryByExampleExecutor;

    @Test
    public void example(){
        Student student = new Student();
        student.setName("Amy");
        Example<Student> example = Example.of(student);

//        查找所有名字是Amy的学生
        Iterator<Student> iterator = studentQueryByExampleExecutor.findAll(example).iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
        System.out.println("-------------");
    }

    @Test
    public void ExampleMatcher(){
        Student student = new Student();
        student.setName("a");
        student.setAge(25);

        ExampleMatcher matcher = ExampleMatcher.matching()
                .withMatcher("age", GenericPropertyMatchers.exact())
                .withMatcher("name", GenericPropertyMatchers.startsWith().ignoreCase());

        Example<Student> example = Example.of(student,matcher);

        // 查找name以a或者A开头,年龄为25的学生
        Iterator<Student> iterator = studentQueryByExampleExecutor.findAll(example).iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
}

4. MongoTemplate方式

当然,如果想要更灵活的查询,还可以使用MongoTemplate。

4.1 查询文档方法

方法

说明

find

查找指定文档列表

findAll

查找所有文档

findOne

查找第一个文档

findById

通过ID查找文档

findAndRemove

查找并删除第一个文档

4.2 Query方法

注解

方法

说明

Query

skip(int)

跳过多少个文档,主要用于分页

Query

with(Sort)

排序

Query

limit(int)

限制返回文档个数,主要用于分页

Field

fields()

定义结果中返回哪些字段

Query

addCriteria(Criteria)

在查询中添加附件查询条件

4.3 Criteria方法

方法

说明

lt

小于

gt

大于

ne

不等

in

in查询

is

字段精确匹配,等于

lte

小于等于

gte

大于等于

all

作用于数组,全部包含,{ lang: { $all: [ “Python” , “Java” ] }}

and

and关系

mod

取模,mod m 等于n,db.cname.find( { status: { $mod: [4, 0]}})

nin

not in

not

not

size

作用于数组,数组的大小满足指定值

type

Creates a criterion using the $type operator

regex

正则表达式匹配

exists

是否存在

elemMatch

作用于数组,数组中所有元素匹配,db.cname.find({scores: {KaTeX parse error: Expected '}', got 'EOF' at end of input: elemMatch:{gte: 90, $lt: 100}}})

orOperator

or操作

norOperator

nor操作

andOperator

and操作

地理位置相关查询:

方法

说明

near

某个点从近到远的坐标

within

在指定的圆或者长方形内

withinSphere

在指定的圆内

nearSphere

在某个点附近

minDistance

最小距离

maxDistance

最大距离

4.4 示例

首先,需要配置MongoTemplate:

@Bean
public MongoClient mongoClient() {
    return MongoClients.create("mongodb://localhost:27017");
}

public @Bean
MongoTemplate mongoTemplate() {
    return new MongoTemplate(mongoClient(), "test");
}

测试:

import com.mongodb.bulk.BulkWriteResult;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.mongodb.core.BulkOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.test.context.junit4.SpringRunner;
import vip.mycollege.mongodb.entity.Student;

import javax.annotation.Resource;
import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest
public class MongoTemplateTest {

    @Resource
    private MongoTemplate mongoTemplate;

    @Test
    public void update(){
        Criteria criteria = Criteria.where("name").is("tim");
        Query query = Query.query(criteria);
        Update update = Update.update("age", 35);
        mongoTemplate.updateFirst(query, update, Student.class);
    }

    @Test
    public void save(){
        Student student = new Student();
        student.setId("10001");
        student.setAge(20);
        student.setName("tim");
        // 插入数据,如果id存在则更新
        mongoTemplate.save(student);
    }

    @Test
    public void insert(){
        Student student = new Student();
        student.setId("10002");
        student.setName("allen");
        student.setAge(25);
        //插入数据,如果id已经存在则抛出异常
        mongoTemplate.insert(student);
    }

    @Test
    public void remove(){
        Student student = new Student();
        student.setId("10003");
        //删除指定数据
        mongoTemplate.remove(student);
    }

    @Test
    public void bulk(){
        //批量操作
        BulkOperations bulkOperations = mongoTemplate.bulkOps(BulkOperations.BulkMode.UNORDERED, Student.class);
        Student student = new Student();
        student.setAge(20);
        bulkOperations.insert(student);

        student = new Student();
        student.setAge(100);
        bulkOperations.insert(student);

        BulkWriteResult result = bulkOperations.execute();
        System.out.println(result.getInsertedCount());
    }

    @Test
    public void find(){
        Criteria criteria = Criteria.where("name").is("tim");
        Query query = Query.query(criteria);
        // 在student集合中查找所有名字为tim的学生
        List<Student> students = mongoTemplate.find(query, Student.class);
        students.forEach(System.out::println);
    }

    @Test
    public void query(){
        // 查找所有名字为tim并且年龄小于30的学生
        Criteria criteria = Criteria.where("name").is("tim").and("age").gt(30);
        Query query = Query.query(criteria);
        List<Student> students = mongoTemplate.query(Student.class)
                .matching(query)
                .all();
        students.forEach(System.out::println);
    }

    @Test
    public void findById(){
        // 查找id为1的学生
        Student student = mongoTemplate.findById("1", Student.class);
        System.out.println(student);
    }

    @Test
    public void findAll(){
        // 获取student集合中所有数据
        List<Student> students =  mongoTemplate.findAll(Student.class);
        students.forEach(System.out::println);
    }

    @Test
    public void collection(){
        // 获取所用集合名称
        mongoTemplate.getCollectionNames();
        // 检查集合是否存在
        mongoTemplate.collectionExists(Student.class);
        // 创建集合
        mongoTemplate.createCollection(Student.class);
        // 删除集合
        mongoTemplate.dropCollection(Student.class);
        //获取集合,不存在则创建
        mongoTemplate.getCollection("hello");
    }
}

save方法与insert方法的区别:

  1. insert只是插入数据,如果插入数据的id已经存在,则抛出异常
  2. save可以插入或修改数据,如果插入数据id已经存在,则执行更新操作

5. 文档资料

spring data mongodbspring data mongodb查询