Spring Data MongoDB

用途

快速集成 MongoDB,不用写一行 MongoDB 的 CRUD 语句。而是使用 Spring Data 独有的方法命名方式定义数据库操作,并且可以方便地替换各种数据库,比如 MySQL。

快速开始

(0)开始之前

确保已有可连接的 MongoDB

(1)依赖引入

build.gradle 中添加如下依赖。

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:2.0.5.RELEASE")
    }
}

apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

repositories {
    mavenCentral()
}

sourceCompatibility = 1.8
targetCompatibility = 1.8

dependencies {
    compile("org.springframework.boot:spring-boot-starter-data-mongodb")
}

(2)配置 MongoDB 连接

这里配置了 MongoDB 的连接地址和使用的数据库,还配置了扫描 Repositories 的位置。Repositories 我们后面会讲到是什么。

@Configuration
@EnableMongoRepositories(basePackages = "com.example.dao")
public class MongoConfig {

  @Bean
  public MongoOperations mongoTemplate() throws UnknownHostException {
    return new MongoTemplate(mongoDbFactory());
  }

  @Bean
  public MongoDbFactory mongoDbFactory() throws UnknownHostException {
    return new SimpleMongoDbFactory(new MongoClientURI("mongodb://localhost:27017/test"));
  }

}

(3)定义一个简单的实体类

实体类是一个 POJO,不过会多一些注解。简单介绍下这些注解吧:

  1. @Document ,用于自定义 MongoDB 中 Collection 的名称,默认情况下 collection 值为空,使用类名的小写形式作为 Collection 的名称
  2. @Id ,用于指定 MongoDB 内部使用字段 _id 的值,如果不指定,则使用自动生成的值。
  3. @Field ,用于指定字段存储时的名称,如果不指定,则直接使用字段名。如果字段名为id,那么一定要使用该注解,否则会读取时使用系统的_id作为 id 的值
  4. @Indexed,用于为指定字段添加索引,会调用 MongoDB 的 createIndex 方法。值得注意的是:必须 @Document 注解,否则不会自动生成索引
@Document(collection = "Customer")
public class Customer {
    @Id
    public String id;
    
    @Indexed
    @Field("first_name")
    public String firstName;
    
    @Field("last_name")
    public String lastName;
    
    public Customer() {}

    public Customer(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}

(4)定义一个 Repository

MongoRepository 中定义的基本的 CRUD 操作,你可以自定义查询方法,不过要遵守一定的规范,Spring Data MongoDB 会根据方法名和参数去执行数据库操作。这个规范参见下文 支持的查询方法关键字列表。此处只需要了解有 findByXx 的方法名即可。

public interface CustomerRepository extends MongoRepository<Customer, String> {

    public Customer findByFirstName(String firstName);
    public List<Customer> findByLastName(String lastName);
}

(5)让 Spring Boot 自动装配 CustomerRepository

@SpringBootApplication
public class Application implements CommandLineRunner {

    @Autowired
    private CustomerRepository repository;

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        repository.deleteAll();

        // save a couple of customers
        repository.save(new Customer("Alice", "Smith"));
        repository.save(new Customer("Bob", "Smith"));

        System.out.println("-------------------------------");

        // fetch an individual customer
        System.out.println(repository.findByFirstName("Alice"));
    }
}

(6)使用 MongoDB 命令行查询

$ mongo
> use test
> db.Customer.find({})

深入探讨

常用的匹配注解列表

AnnotationDesc
@Id用于指定 MongoDB 内部使用字段 _id 的值,如果不指定,则使用自动生成的值。
@Field用于指定数据库中存储的字段名。
@Document用于指定该类的实例对应 MongoDB 的某个指定 Collection 下的 Document。
@Indexed用于为指定字段添加索引。@Indexed(unique = true) 可注解唯一键
@CompoundIndex用于指定复合索引。
@Transient用于将某些字段排除,不与数据库匹配。
@Version用于指定字段的版本,默认值为 0,在每次更新字段后自增。

复合索引用例:

@Document
@CompoundIndexes({
    // lastName 升序,age 降序的复合索引,名称为 age_idx。
    @CompoundIndex(name = "age_idx", def = "{'lastName': 1, 'age': -1}")
})
public class Person<T extends Address> {
    //...
}

支持的查询方法关键字列表

KeywordSampleLogical result
AfterfindByBirthdateAfter(Date date){"birthdate" : {"$gt" : date}}
GreaterThanfindByAgeGreaterThan(int age){"age" : {"$gt" : age}}
GreaterThanEqualfindByAgeGreaterThanEqual(int age){"age" : {"$gte" : age}}
BeforefindByBirthdateBefore(Date date){"birthdate" : {"$lt" : date}}
LessThanfindByAgeLessThan(int age){"age" : {"$lt" : age}}
LessThanEqualfindByAgeLessThanEqual(int age){"age" : {"$lte" : age}}
BetweenfindByAgeBetween(int from, int to){"age" : {"$gt" : from, "$lt" : to}}
InfindByAgeIn(Collection ages){"age" : {"$in" : [ages…]}}
NotInfindByAgeNotIn(Collection ages){"age" : {"$nin" : [ages…]}}
IsNotNull, NotNullfindByFirstnameNotNull(){"firstname" : {"$ne" : null}}
IsNull, NullfindByFirstnameNull(){"firstname" : null}
Like, StartingWith, EndingWithfindByFirstnameLike(String name){"firstname" : name} (name as regex)
NotLike, IsNotLikefindByFirstnameNotLike(String name){"firstname" : { "$not" : name }} (name as regex)
Containing on StringfindByFirstnameContaining(String name){"firstname" : name} (name as regex)
NotContaining on StringfindByFirstnameNotContaining(String name){"firstname" : { "$not" : name}} (name as regex)
Containing on CollectionfindByAddressesContaining(Address address){"addresses" : { "$in" : address}}
NotContaining on CollectionfindByAddressesNotContaining(Address address){"addresses" : { "$not" : { "$in" : address}}}
RegexfindByFirstnameRegex(String firstname){"firstname" : {"$regex" : firstname }}
(No keyword)findByFirstname(String name){"firstname" : name}
NotfindByFirstnameNot(String name){"firstname" : {"$ne" : name}}
NearfindByLocationNear(Point point){"location" : {"$near" : [x,y]}}
NearfindByLocationNear(Point point, Distance max){"location" : {"$near" : [x,y], "$maxDistance" : max}}
NearfindByLocationNear(Point point, Distance min, Distance max){"location" : {"$near" : [x,y], "$minDistance" : min, "$maxDistance" : max}}
WithinfindByLocationWithin(Circle circle){"location" : {"$geoWithin" : {"$center" : [ [x, y], distance]}}}
WithinfindByLocationWithin(Box box){"location" : {"$geoWithin" : {"$box" : [ [x1, y1], x2, y2]}}}
IsTrue, TruefindByActiveIsTrue(){"active" : true}
IsFalse, FalsefindByActiveIsFalse(){"active" : false}
ExistsfindByLocationExists(boolean exists){"location" : {"$exists" : exists }}

Tip:将以上的 findBy 替换成 deleteBy 含义就变成了:查找后进行删除操作。

下面是一个使用示例:查询指定状态集合的 JobFlow。

// 方式一:使用 SqEL(Spring 表达式)
@Query("{ 'status': { $in: ?0 } }")
List<JobFlow> findByStatus(String... status);
// 方式二:使用 In
List<JobFlow> findByStatusIn(List<String> statusList);

save 的原理

// SimpleMongoRepository.java
public <S extends T> S save(S entity) {

    Assert.notNull(entity, "Entity must not be null!");

    if (entityInformation.isNew(entity)) {
        mongoOperations.insert(entity, entityInformation.getCollectionName());
    } else {
        mongoOperations.save(entity, entityInformation.getCollectionName());
    }

    return entity;
}

public <S extends T> List<S> save(Iterable<S> entities) {

    Assert.notNull(entities, "The given Iterable of entities not be null!");

    List<S> result = convertIterableToList(entities);
    boolean allNew = true;

    for (S entity : entities) {
        if (allNew && !entityInformation.isNew(entity)) {
            allNew = false;
        }
    }

    if (allNew) {
        mongoOperations.insertAll(result);
    } else {

        for (S entity : result) {
            save(entity);
        }
    }

    return result;
}
// AbstractEntityInformation.java
public boolean isNew(T entity) {

    ID id = getId(entity);
    Class<ID> idType = getIdType();

    if (!idType.isPrimitive()) {
        return id == null;
    }

    if (id instanceof Number) {
        return ((Number) id).longValue() == 0L;
    }

    throw new IllegalArgumentException(String.format("Unsupported primitive id type %s!", idType));
}

参考

  1. Spring Data MongoDB - Reference Documentation - spring.io
  2. Accessing Data with MongoDB - spring.io
  3. Working with Spring Data Repositories - spring.io

转载于:https://www.cnblogs.com/lshare/p/11334476.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值