文章目录
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呢?
- 如果一个字段有org.springframework.data.annotation.Id注解,它会被当做mongodb的id
- 如果没有注解,但是字段名称是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方法的区别:
- insert只是插入数据,如果插入数据的id已经存在,则抛出异常
- save可以插入或修改数据,如果插入数据id已经存在,则执行更新操作