代码编织梦想

一 连接数据库、获取 ReactiveMongoTemplate实例

通过spring 容器化方式实例化

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

    @Bean
    public ReactiveMongoDatabaseFactory reactiveMongoDatabaseFactory(MongoClient mongoClient) {
        ReactiveMongoDatabaseFactory mongo = new SimpleReactiveMongoDatabaseFactory(mongoClient,"mytest");
        return mongo;
    }

    @Bean
    public  ReactiveMongoTemplate reactiveMongoTemplate(ReactiveMongoDatabaseFactory mongoClient) {
        return new ReactiveMongoTemplate(mongoClient);
    }

}

测试

@Autowired
private ReactiveMongoTemplate template;
    
@Test
public void test(){
    Mono<Person> personMono = template.insert(new Person("张三", 40));
    personMono.doOnNext(System.out::println)
                .block();
}

二  ReactiveMongoTemplate常用API

2.1 基本使用

    @Test
    public void test3(){
        template
                .query(Person.class) // 指定映射域对象
                .inCollection("person1") // 指定要查询集合
                .as(PersonType.class) //结果类型映射
                .matching(Query.query(Criteria.where("name").is("李四"))) //匹配文档
                .all()  //获取查询的所有文档   .first 获取查询的第一个文档    .one 检测结果集合是否只有一个或0个文档,返回文档
                .doOnNext(System.out::println)
                .blockLast();
    }

2.2 索引操作API

    @Test
    public void test(){
        // 创建索引
        template
                .indexOps(Person.class)
                .ensureIndex(new Index().on("name", Sort.Direction.DESC).unique().sparse())
                .block();

        // 修改索引
        template
                .indexOps(Person.class)
                .alterIndex("name_-1", IndexOptions.unique())
                .block();

        // 删除索引
        template
                .indexOps(Person.class)
                .dropIndex("name_-1")
                .block();

        // 获取索引信息
        template
                .indexOps(Person.class)
                .getIndexInfo()
                .doOnNext(System.out::println)
                .blockLast();
    }

2.3 集合操作API

    @Test
    public void test()  {
        // 获取集合名称列表
        template
                .getCollectionNames()
                .doOnNext(System.out::println)
                .blockLast();

        // 根据映射类型获取集合名称
        System.out.println(template.getCollectionName(Person.class));

        // 获取集合对像
        template
                .getMongoDatabase()
                .map(db -> db.getCollection("person"))
                .doOnNext(System.out::println)
                .block();

        // 获取集合对象
        Document document = new Document();
        document.put("name","王五");
        document.put("id","1");
        document.put("age",30);

        template
                .getCollection("person")
                .doOnNext(documentMongoCollection -> documentMongoCollection.insertMany(List.of(document)))
                .block();

        // 创建集合
        template
                .createCollection("student")
                .doOnNext(System.out::println)
                .block();

        // 删除集合
        template
                .dropCollection("student")
                .block();

        // 检查集合是否存在
        template
                .collectionExists("student")
                .doOnNext(System.out::println)
                .block();
    }

2.4 数据插入API

    @Test
    public void test(){

        // 保存数据 save需要遍历列表,一个个插入。
        template
                .save(new Person("1","王五",30))
                .block();
        
        //插入数据 insert可以一次性插入一个列表,而不用遍历,效率高
        template
                .insert(new Person("1","王五",30))
                .block();

        // 批量插入  insertAll和bulkOps的服务器性能相同。但是,bulkOps不会发布生命周期事件。
        template
                .insert(List.of(new Person("tom",10),new Person("jack",10)), Person.class)
                .blockLast();
        template
                .insertAll(List.of(new Person("tom1",10),new Person("jack1",10)))
                .blockLast();
        template
                .bulkOps(BulkOperations.BulkMode.ORDERED, Person.class)
                .insert(List.of(new Person("tom2",10),new Person("jack2",10)))
                .execute()
                .block();

    }

注意:

id属性
1 插入或保存时,如果未设置 id 属性值,则假定其值将由数据库自动生成。因此,要成功自动生成 ObjectId ,类中 Id 属性或字段的类型必须是 String 、 ObjectId 或 BigInteger 。
2 用 @Id 注释的属性或字段映射到 _id 字段或者提供名为 id 的属性
保存目标集合
1 默认情况下是类名小写对应的集合
2 @Document("xxx") 类上的注解指定的集合
3 通过 MongoTemplate 的方法调用的最后一个参数来指定集合名称

2.5 数据修改API

    @Test
    public void test8(){
        template
                .update(Person.class)
                .matching(Criteria.where("name").is("tom4"))
                .apply(new Update().inc("age",100))
                .all()  // .first 只更新查到的第一个    .all 更新所有查到的数据  .upsert 有时更新,没时插入
                .block();

        // 更新匹配第一条数据
        template
                .updateFirst(Query.query(Criteria.where("age").lt(100)),new Update().inc("age",100), Person.class)
                .block();

        // 更新所有匹配数据 使用 $inc 修饰符更新
        template
                .updateMulti(Query.query(Criteria.where("age").lt(100)),new Update().inc("age",100), Person.class)
                .block();
        // 更新插入匹配数据
        template
                .upsert(Query.query(Criteria.where("age").lt(100)),new Update().inc("age",100), Person.class)
                .block();

        // 聚合管道更新
        AggregationUpdate update = Aggregation.newUpdate()
                .set("average").toValue(ArithmeticOperators.valueOf("age").avg());

        template.update(Person.class)
                .apply(update)
                .all()
                .block();

        // 替换文档
        template
                .replace(Query.query(Criteria.where("name").is("jack2")),new Person("jack3333",30), ReplaceOptions.replaceOptions().upsert())
                .block();

        // 查找和修改 FindAndModifyOptions.options().returnNew(true) 返回修改后的值,不设置返回修改前的值
        template
                .findAndModify(Query.query(Criteria.where("name").is("jack222")),new Update().inc("age",1000), FindAndModifyOptions.options().returnNew(true),Person.class)
                .doOnNext(System.out::println)
                .block();

        // 和上面等价
        template
                .update(Person.class)
                .matching(Criteria.where("name").is("tom4"))
                .apply(new Update().inc("age",100))
                .withOptions(FindAndModifyOptions.options().returnNew(true))
                .findAndModify()  // .first 只更新查到的第一个    .all 更新所有查到的数据  .upsert 有时更新,没时插入
                .block();

        // findAndReplace()同理操作
    }

 2.6 数据删除API

    @Test
    public void test9(){
        template.remove(Person.class);

        template.remove(Query.query(Criteria.where("name").is("tom")), "person");

        // 一次性删除
        template.remove(new Query().limit(3), "person");

        // 文档不会批量删除,而是逐个删除
        template.findAllAndRemove(Query.query(Criteria.where("name").is("tom")), "person");

        // 文档不会批量删除,而是逐个删除
        template.findAllAndRemove(new Query().limit(3), "person");

    }

2.7 数据查询API

1 基本查询

    @Test
    public void test(){
        // QBC
        template
                .find(Query.query(Criteria.where("name").is("张三")), Person.class)
                .doOnNext(System.out::println)
                .blockLast();

        template
                .query(Person.class)
                .matching(Query.query(Criteria.where("name").is("张三").and("age").is(130)))
                .all()
                .doOnNext(System.out::println)
                .blockLast();

        // 纯 JSON 字符串创建查询实例
        template
                .find(new BasicQuery("{name:'张三'}","{name:1,age:1}"), Person.class)
                .doOnNext(System.out::println)
                .blockLast();
    }

2 Criteria 查询条件

    @Test
    public void test(){
        // Criteria 查询条件
        // 使用 $all 运算符创建条件,包含指定数组所有项
        template
                .query(Person.class)
                .matching(Query.query(Criteria.where("cityId").all(3,4)))
                .all()
                .doOnNext(System.out::println)
                .blockLast();

        // and 运算
        template
                .query(Person.class)
                .matching(Query.query(Criteria.where("cityId").all(3,4).and("name").is("张三1")))
                .all()
                .doOnNext(System.out::println)
                .blockLast();

        template
                .query(Person.class)
                .matching(Query.query(Criteria.where("cityId").all(3,4).andOperator(Criteria.where("name").is("张三1"))))
                .all()
                .doOnNext(System.out::println)
                .blockLast();
    }

其他查询条件见:

Querying Documents

3 Query 查询方法

    @Test
    public void test12(){
        // Query 查询方法
        Query query = new Query();
        query.addCriteria(Criteria.where("age").gt(30));
        query.fields().include("name","age");
        query.skip(2);
        query.limit(5);
        query.with(Sort.by("age").ascending());
        query.withHint("{name:1}"); //指定索引查询
        query.cursorBatchSize(100); //定义了每个响应批次中要返回的文档数

        // 使用聚合表达式进行字段投影
        // 使用本地表达式
        query.fields().project(MongoExpression.create("'$toUpper' : '$name'")).as("name");
        // 使用 AggregationExpression
        query.fields().project(StringOperators.valueOf("name").toUpper()).as("name");
        // 使用 SpEL 和 AggregationExpression 来调用表达式函数
        query.fields().project(AggregationSpELExpression.expressionOf("toUpper(name)")).as("name");
        template
                .query(Person.class)
//                .distinct("age") // 查询不重复值
                .as(PersonType.class)   // 结果投影
                .matching(query)
                .all()
                .doOnNext(System.out::println)
                .blockLast();

    }
注意:  @Field("xxx") 加在类属性上可以进行对应字段映射

三 ReactiveMongo Repository

3.1 如何定义相关存储库接口

1. 要定义特定于域类的存储库接口。接口必须扩展 Repository 并键入到域类和 ID 类型

@Document("user")   // 指定域类到存储库映射,不指定是为类名小写
class User { … }

interface MyBaseRepository<T, ID> extends Repository<User, Long> {

  Optional<T> findById(ID id);

  <S extends T> S save(S entity);
}

2. 如果要公开该域类的 CRUD 方法,可以扩展 ReactiveCrudRepository 或其变体之一,而不是 Repository 。

interface MyBaseRepository<T, ID> extends ReactiveCrudRepository <User, Long> { … }

这样就提供了CRUD 功能的方法

3 如果应用程序中的许多存储库应具有相同的方法集,可以定义自己的基接口,然后从中继承

@NoRepositoryBean
interface MyBaseRepository<T, ID> extends Repository<T, ID> {

  Optional<T> findById(ID id);

  <S extends T> S save(S entity);
}

interface UserRepository extends MyBaseRepository<User, Long> {
  User findByEmailAddress(EmailAddress emailAddress);
}
@NoRepositoryBean
interface MyBaseRepository<T, ID> extends CrudRepository<T, ID> { … }

interface AmbiguousUserRepository extends MyBaseRepository<User, Long> { … }

注意:这样的接口必须用 @NoRepositoryBean 进行注释。这可以防止 Spring Data 尝试直接创建它的实例而失败,因为它无法确定该存储库的实体。 

3.2 基本使用

1 创建实体类

@Document("person")
public class Person {
    @Id   // 指定对应id字段
    private String id;
    private String name;
    @Field("age") //指定映射字段
    private int age1;
    private List<Integer> cityId;
    @Version
    private Long version;
    private Instant timestamp;
    ... set and get
}

2 创建仓库

public interface PersonRepository extends ReactiveMongoRepository<Person, ObjectId{}

3 测试

    @Autowired
    private PersonRepository repository;

    @Test
    public void test() {
        repository
                .findAll()
                .doOnNext(System.out::println)
                .blockLast();
    }

3.3 定义查询方法

除了已经提供的查询方法,还可以从方法名称自定义创建查询

@Repository
public interface PersonRepository extends ReactiveMongoRepository<Person, ObjectId> {
    Flux<Person> findByName(String name);
    Flux<Person> findByNameAndAge1(String name,int age);
    Flux<Person> findByNameIgnoreCase(String name);
    Flux<Person> findByVersionOrderByAge1Desc(int age);
    Flux<Person> findFirstByVersionOrderByAge1Desc(int age);
    Flux<Person> findByNameOrAge1(String name,int age);
    Flux<Person> findByNameLikeIgnoreCase(String name);
    Flux<Person> findByAge1LessThan(int age);

    Flux<Person> findByAge1(int age,Pageable pageable);
    Flux<Person> findByAge1(int age,Sort sort);
    Flux<Person> findByAge1(int age, Sort sort, Limit limit);
}
    @Test
    public void test() {
        repository.findByName("张三1").doOnNext(System.out::println).blockLast();
        // and
        repository.findByNameAndAge1("张三1",30).doOnNext(System.out::println).blockLast();
        // 忽略大小写
        repository.findByNameIgnoreCase("Tom").doOnNext(System.out::println).blockLast();
        // 降序排序
        repository.findByVersionOrderByAge1Desc(4).doOnNext(System.out::println).blockLast();
        // 查询第一个
        repository.findFirstByVersionOrderByAge1Desc(4).doOnNext(System.out::println).blockLast();
        // or
        repository.findByNameOrAge1("张三",140).doOnNext(System.out::println).blockLast();
        // like
        repository.findByNameLikeIgnoreCase("T").doOnNext(System.out::println).blockLast();
        // <
        repository.findByAge1LessThan(140).doOnNext(System.out::println).blockLast();
        
        // 分页 Pageable.unpaged() 不分页
        repository.findByAge1(110, Pageable.ofSize(2).withPage(0)).doOnNext(System.out::println).blockLast();
        repository.findByAge1(110, PageRequest.of(1,2,Sort.by("name").descending())).doOnNext(System.out::println).blockLast();

        // 排序 Sort.unsorted() 不排序
        repository.findByAge1(110, Sort.by("name").descending().and(Sort.by("version").ascending())).doOnNext(System.out::println).blockLast();
        // 类型安全的 API 定义排序表达式
        Sort.TypedSort<Person> personTypedSort = Sort.sort(Person.class);
        repository.findByAge1(110, personTypedSort.by(Person::getAge1).descending().and(personTypedSort.by(Person::getName).ascending())).doOnNext(System.out::println).blockLast();
        // 限制 Limit.unlimited() 不加限制
        repository.findByAge1(110, Sort.by("name").descending(),Limit.of(3)).doOnNext(System.out::println).blockLast();

    }

其他定义规则见: Repository query keywords

3.4 基于JSON的查询方法和字段限制

@Repository
public interface PersonRepository extends ReactiveMongoRepository<Person, ObjectId> {
    
    @Query("{'name':?0}")
    // 使用 ?0 占位符可以将方法参数中的值替换为 JSON 查询字符串
    Flux<Person> findBySelfName(String name);

    @Query(value = "{'name':?0}",fields = "{name:1,age:1}")
    // fields 设置限制字段表达式
    Flux<Person> findBySelfNameLimitFiled(String name);

    @Query(value = "{'age':{$gt:?0}}",fields = "{name:1,age:1}")
    Flux<Person> findByAge(int age);

    @Aggregation("{ $group : { _id : $age, total : { $sum : $age } } }")
    // 聚合查询
    Flux<Object> findAndGroup();

}

3.5 按示例查询 QBE

1. 按示例查询 API 由四个部分组成:

  • Probe:具有填充字段的域对象的实际实例。
  • ExampleMatcher:如何匹配特定字段的详细信息。它可以在多个示例中重复使用。
  • ExampleExample 由 Probe 和 ExampleMatcher 组成。它用于创建查询。
  • FetchableFluentQuery:提供流畅的 API,允许进一步自定义派生自 Example 的查询。使用 Fluent API 可以指定查询的顺序、投影和结果处理。

2 适用场景

  • 使用一组静态或动态约束查询数据存储。
  • 频繁重构域对象,无需担心破坏现有查询。
  • 独立于底层数据存储 API 工作。

3 限制

  • 不支持嵌套或分组属性约束,例如 firstname = ?0 or (firstname = ?1 and lastname = ?2) 。
  • 特定于存储的字符串匹配支持。根据您的数据库,字符串匹配可以支持字符串的 starts/contains/ends/regex。
  • 与其他属性类型完全匹配。

4 使用

    @Test
    public void test() {
        Person personType = new Person();
        personType.setName("Ja");
        personType.setAge1(110);

        ExampleMatcher exampleMatcher = ExampleMatcher
                .matching()
                .withIgnorePaths("age")
                .withIncludeNullValues()
//                .withStringMatcher(ExampleMatcher.StringMatcher.STARTING)
                .withMatcher("name", matcher -> matcher.startsWith().ignoreCase());


        Example<Person> personTypeExample = Example.of(personType,exampleMatcher);

        repository.findAll(personTypeExample,Sort.by("age").descending()).doOnNext(System.out::println).blockLast();

        // findBy 通过在第二个参数中 sortBy 允许您指定结果的顺序。 as 允许您指定希望将结果转换为的类型。 project 限制了查询的属性。 first 、 firstValue 、 one 、 oneValue 、 all 、 page 、 stream 、 count 和 exists 定义了获得的结果类型以及当可用结果数超过预期数量时查询的行为方式。
        repository.findBy(personTypeExample ,q -> q.as(PersonType.class).sortBy(Sort.by("age").descending()).limit(3).all()).doOnNext(System.out::println).blockLast();
    }

3.6 排序方式

public interface PersonRepository extends ReactiveMongoRepository<Person, String> {

    Flux<Person> findByFirstnameSortByAgeDesc(String firstname);

    Flux<Person> findByFirstname(String firstname, Sort sort);

    @Query(sort = "{ age : -1 }")
    Flux<Person> findByFirstname(String firstname);

    @Query(sort = "{ age : -1 }")
    Flux<Person> findByLastname(String lastname, Sort sort);
}

3.7 数据修改

@Repository
public interface PersonRepository extends ReactiveMongoRepository<Person, ObjectId> {
    
    // 自定义函数名称查询 + @Update
    
    @Update("{'$inc':{age:?1}}")
    // 普通修改
    Mono<Long> findAndIncrementAgeByName(String name,int age);

    @Update("{'$inc':{age:?#{[1]}}}")
    // Spel表达式修改
    Mono<Long> findAndIncrementAgeSpelByName(String name,int age);

    @Update(pipeline = "{ '$set' : { 'age' : { '$add' : [ '$age', ?1 ] } } }")
    // 管道修改
    Mono<Long> findAndIncrementAgePiplineByName(String name,int age);

    @Update("{'$push':{cityId:?#{[1]}}}")
    // Spel表达式修改
    Mono<Long> findAndPushByName(String name,int age);

    // @Query + @Update
    @Query("{name:?0}")
    @Update("{'$push':{cityId:?#{[1]}}}")
    Mono<Long> findAndUpdateCityId(String name,int age);

}

3.8 数据删除

public interface PersonRepository extends ReactiveMongoRepository<Person, String> {

    //返回类型 Flux 将检索并返回所有匹配的文档,然后再实际删除它们
    Flux<Person> deleteByLastname(String lastname);
      
    //直接删除匹配的文档,返回已删除的文档总数。
    Mono<Long> deletePersonByLastname(String lastname);   
      
    //检索并删除第一个匹配的文档。
    Mono<Person> deleteSingleByLastname(String lastname);       
}

注意:根据返回类型确定具体操作

3.9 投影

1 基于接口投影

@Component
class MyBean {
    String getFullStr(Person person){
        return person.toString();
    }
}

public interface NameAndAge {
    // 闭合投影
    String getName();

    // 开放投影
    @Value("#{target.age1}")
    String getAge();

    // SPEL表达式计算
    @Value("#{target.name + '_' + target.age1}")
    String getFull();

    //获取方法参数
    @Value("#{args[0] + '_' + target.name}")
    String getPrefic(String prefix);

    //复杂表达式计算
    @Value("#{@myBean.getFullStr(target)}")
    String getFullStr();
}

2 动态投影

interface PersonRepository extends Repository<Person, UUID> {

  <T> Collection<T> ReactiveMongoRepository(String lastname, Class<T> type);
}
void someMethod(PersonRepository people) {

  Flux<Person> aggregates =
    people.findByLastname("Matthews", Person.class);

  Flux<NamesOnly> aggregates =
    people.findByLastname("Matthews", NamesOnly.class);
}

3.10 自定义仓库实现

interface HumanRepository {
  void someHumanMethod(User user);
}

class HumanRepositoryImpl implements HumanRepository {

  public void someHumanMethod(User user) {
    // Your custom implementation
  }
}

interface ContactRepository {

  void someContactMethod(User user);

  User anotherContactMethod(User user);
}

class ContactRepositoryImpl implements ContactRepository {

  public void someContactMethod(User user) {
    // Your custom implementation
  }

  public User anotherContactMethod(User user) {
    // Your custom implementation
  }
}

interface UserRepository extends CrudRepository<User, Long>, HumanRepository, ContactRepository {

  // Declare query methods here
}

注意:实现类需要遵循命名约定,即附加默认为 Impl 的后缀。或者设置自定义后缀:

@EnableMongoRepositories(repositoryImplementationPostfix = "MyPostfix")
class Configuration { … }
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/pengpengxiaobai/article/details/140982089

spring data mongodb_super wheat的博客-爱代码爱编程

Spring Data MongoDB 是Spring框架访问mongodb的神器,借助它可以非常方便的读写mongo库。 1.pom.xml中引入依赖(本项目为springboot项目) <dependency> <groupId>org.springframework.boot</groupId> <

java基础sql基础Linux常用命令-爱代码爱编程

java基础知识 类的定义和声明方法的重载类的继承方法的重写方法的隐藏接口数组Obejct类String类String、StringBuffer和StringBuilder使用比较类和数据结构-包装类包装类的数值转换容器类IO流JAVA异常异常的用法SQL基础数据库用户数据库权限grant的作用赋予权限,用法举例:REVOKE的作用是回收权限,用法

SpringData MongoDB-爱代码爱编程

目录 1 SpringData MongoDB简介2 MongoDB环境搭建2.1 解压2.2 创建需要的目录2.3 创建配置文件2.4 启动mongodb3 SpringData MongoDB入门案例3.1目标3.2 创建工程,引入坐标3.3 创建配置文件3.4 创建实体类3.5 自定义dao接口3.6 测试4 SpringData Mongo

Springdata-mongodb的基本使用-爱代码爱编程

文章目录 一、 SpringData介绍二、 SpringData操作MongoDB的两种方式及实战1. MongoTemplate1.1 Mongodb Driver Api操作MongoDB1.1.2 基本CRUD1.1.3 连接选项1.2 MongoTemplate的基本CRUD1.3 MongoTemplate的聚合查询1.3.1 Aggr

springdata整合mongodb-爱代码爱编程

项目地址: https://gitee.com/Anntly/mongo-crud 常见的 增删改查、地理位置、嵌套文档、聚合函数 新建项目 修改 application.yml 添加 mongodb数据源信息 s

spring data mongodb 使用-爱代码爱编程

1.Spring Data MongoDB接入 1.1添加依赖:   在项目的构建文件(如 Maven 的 pom.xml 或 Gradle 的 build.gradle)中添加 Spring Data MongoDB 的依赖项。例如,在 Maven 中添加以下依赖项: <dependencies> <!-- 其他依赖

springboot整合mongodb_springboot mongodb配置-爱代码爱编程

文章目录 一、环境准备二、集合操作三、文档操作3.1 实体类3.2 添加文档3.3 查询文档3.4 修改文档3.5 删除文档 四、聚合操作4.1 使用 Aggregation 实现4.2 使用 Ag

spring data mongodb 菜鸟教程_springdata mongodb-爱代码爱编程

Spring Data MongoDB 是 Spring Data 项目的一部分,该项目旨在为新的数据存储提供熟悉的、一致的基于 Spring 的编程模型,同时保留特定于存储的特性和功能。 Spring Data MongoDB 项目提供了与 MongoDB 文档数据库的集成。 Spring Data MongoDB 的关键功能是一个以 POJO 为中

12、mongodb -爱代码爱编程

目录 通过 SpringBoot 整合 Spring Data MongoDB 操作 MongoDB 数据库(传统的同步API编程)演示前提:登录单机模式的 mongodb 服务器命令登录【test】数据库的

mongodb保姆级指南(下):无缝集成springdata框架,一篇最全面的java接入指南!_操作mongdb的框架-爱代码爱编程

引言 前面的两篇文章已经将MongoDB大多数知识进行了阐述,不过其中的所有内容,都基于原生的MongoDB语法在操作。可是,在实际的日常开发过程中,我们并不会直接去接触MongoDB,毕竟MongoDB只能算作是系统内的一个组件,无法仅依靠它来搭建出一整套系统。 我们都是高级语言的开发者,就如同MySQL一样,在不同语言中,都会有各自的驱动、ORM框