1. 程式人生 > 資料庫 >Spring Data套裝基礎之MongoDB

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. 文件資料