Spring Data套装基础之MongoDB

拈花ヽ惹草 提交于 2020-12-26 11:31:53

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: {$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 mongodb

spring data mongodb查询

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!