Spring Data套装基础之MongoDB

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方法

注解方法说明
Queryskip(int)跳过多少个文档,主要用于分页
Querywith(Sort)排序
Querylimit(int)限制返回文档个数,主要用于分页
Fieldfields()定义结果中返回哪些字段
QueryaddCriteria(Criteria)在查询中添加附件查询条件

4.3 Criteria方法

方法说明
lt小于
gt大于
ne不等
inin查询
is字段精确匹配,等于
lte小于等于
gte大于等于
all作用于数组,全部包含,{ lang: { $all: [ “Python” , “Java” ] }}
andand关系
mod取模,mod m 等于n,db.cname.find( { status: { $mod: [4, 0]}})
ninnot in
notnot
size作用于数组,数组的大小满足指定值
typeCreates 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}}})
orOperatoror操作
norOperatornor操作
andOperatorand操作

地理位置相关查询:

方法说明
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查询

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值