Spring Data for Mongo 介绍

48 篇文章 1 订阅
15 篇文章 0 订阅

本文将快速介绍 Spring Data for Mongo 的使用。

Spring Data for MongoDB是Spring Data的一个子模块。 目标是为MongoDB提供一个相近的一致的基于Spring的编程模型。

Spring Data for MongoDB核心功能是映射POJO到Mongo的DBCollection中的文档,并且提供Repository 风格数据访问层。

相似的ORM/持久化框架还有

  • morphia : MongoDB官方支持的ORM框架,可以很好的和Spring, Guice等DI框架集成,使用起来很方便。
  • Hibernate OGM : Hibernate提供了Hibernate风格的NoSql ORM框架。
  • jongo : 提供Mongo shell一样灵活的查询,并且提供ORM by Jackson,和Mongo java driver一样快。

特性:
  • 可以通过@Configuration注解或者XML风格配置
  • MongoTemplate 辅助类 (类似JdbcTemplate),方便常用的CRUD操作
  • 异常转换
  • 丰富的对象映射
  • 通过注解指定对象映射
  • 持久化和映射声明周期事件
  • 通过MongoReader/MongoWriter 定义底层的映射
  • 基于Java的Query, Criteria, Update DSL
  • 自动实现Repository,可以提供定制的查找
  • QueryDSL 支持类型安全的查询
  • 跨数据库平台的持久化 - 支持JPA with Mongo
  • GeoSpatial 支持
  • Map-Reduce 支持
  • JMX管理和监控
  • CDI 支持
  • GridFS 支持

本文介绍的Spring Data for MongoDB版本为1.7.0.M1。

Spring Data for MongoDB提供了两种编程风格来应用MongoDB,下面逐一介绍这两种方式。

Spring Data Repository 风格

Spring Data提供了repository 抽象方式,可以极大的减少数据访问层千篇一律的类似的重复的代码。 基本DAO都会实现,find,findAll, findById, save, delete,update等方法,而且代码逻辑基本一致。Spring Data提供了简化方法,通过接口定义 Spring Data通过Proxy自动提供具体的实现。

这里有一篇介绍 文章 。

核心概念

Spring Data最重要的接口是 Repository 。它使用域类型和它的ID类型作为类型参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

public interface CrudRepository < T , ID extends Serializable >

extends Repository < T , ID > {

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

T findOne (ID primaryKey) ;

Iterable<T> findAll () ;

Long count () ;

void delete (T entity) ;

boolean exists (ID primaryKey) ;

// … more functionality omitted.

}

同时也提供特定数据库的接口: JpaRepository 和 MongoRepository 。 这些接口继承 CrudRepository 。 CrudRepository 之上还有一个接口 PagingAndSortingRepository 提供分页的功能。

1
2
3
4
5
6
7

public interface PagingAndSortingRepository < T , ID extends Serializable >

extends CrudRepository < T , ID > {

Iterable<T> findAll (Sort sort) ;

Page<T> findAll (Pageable pageable) ;

}

可以增加一些特定的统计和删除方法。

1
2
3

public interface UserRepository extends CrudRepository < User , Long > {

Long countByLastname (String lastname) ;

}

1
2
3
4

public interface UserRepository extends CrudRepository < User , Long > {

Long deleteByLastname (String lastname) ;

List<User> removeByLastname (String lastname) ;

}

查询方法

标准的CRUD功能的repositories一般会提供一些查询方法。在Spring Data中只需四步。

1) 声明一个子接口: interface PersonRepository extends Repository<User, Long> { … }

2) 声明查询方法

1
2
3

interface PersonRepository extends Repository < User , Long > {

List<Person> findByLastname (String lastname) ;

}

3) 设置Spring为这些接口产生Proxy实例:两种方式。

1
2
3
4

import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@EnableJpaRepositories

class Config {}

1
2
3
4
5
6
7
8
9
10
11
12
13
14

<?xml version="1.0" encoding="UTF-8"?>

< beans xmlns = "http://www.springframework.org/schema/beans"

xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"

xmlns:jpa = "http://www.springframework.org/schema/data/jpa"

xsi:schemaLocation = "http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/data/jpa

http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

< jpa:repositories base-package = "com.acme.repositories" />

</ beans >

4) 注入此repository实例并使用它

1
2
3
4
5
6
7
8
9

public class SomeClient {

@Autowired

private PersonRepository repository;

public void doSomething () {

List<Person> persons = repository.findByLastname( "Matthews" );

}

}

定义repository 接口

第一步就是为特定的domain类定义相应的定义repository。

调整repository 定义

典型的,repository 接口应该继承 Repository , CrudRepository , PagingAndSortingRepository 。 如果你不想继承这些接口,使用 @RepositoryDefinition注解标记此接口为repository。

@NoRepositoryBean 增加一些非典型的方法,然后在domain class Repository继承它。

1
2
3
4
5
6
7
8
9
10
11

@NoRepositoryBean

interface MyBaseRepository < T , ID extends Serializable > extends Repository< T , ID > {

T findOne (ID id) ;

T save (T entity) ;

}

interface UserRepository extends MyBaseRepository < User , Long > {

User findByEmailAddress (EmailAddress emailAddress) ;

}

定义查询方法

repository proxy有两种方式根据方法导出数据库特定的语句。 一种是根据方法名直接导出。 另一种是是手工定义。

Query lookup策略

通过XML方式的query-lookup-strategy 属性或者${store}注解的queryLookupStrategy 指定。

  • CREATE: 根据方法名
  • USE_DECLARED_QUERY: 通过注解或者其它方式得到
  • CREATE_IF_NOT_FOUND: 复合前面两种

Query creation

方法名应该是find…By, read…By, query…By, count…By, and get…By这样的格式。可以设置Distinct 和 And , Or :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

public interface PersonRepository extends Repository < User , Long > {

List<Person> findByEmailAddressAndLastname (EmailAddress emailAddress, String lastname) ;

// Enables the distinct flag for the query

List<Person> findDistinctPeopleByLastnameOrFirstname (String lastname, String firstname) ;

List<Person> findPeopleDistinctByLastnameOrFirstname (String lastname, String firstname) ;

// Enabling ignoring case for an individual property

List<Person> findByLastnameIgnoreCase (String lastname) ;

// Enabling ignoring case for all suitable properties

List<Person> findByLastnameAndFirstnameAllIgnoreCase (String lastname, String firstname) ;

// Enabling static ORDER BY for a query

List<Person> findByLastnameOrderByFirstnameAsc (String lastname) ;

List<Person> findByLastnameOrderByFirstnameDesc (String lastname) ;

}

Property expressions

假定 Person 有 Address , Address 有 ZipCode 属性。

List<Person> findByAddressZipCode(ZipCode zipCode); 会使用x.address.zipCode遍历。

更明确的用:

1

List<Person> findByAddress_ZipCode (ZipCode zipCode) ;

特殊参数

1
2
3
4

Page<User> findByLastname (String lastname, Pageable pageable) ;

Slice<User> findByLastname (String lastname, Pageable pageable) ;

List<User> findByLastname (String lastname, Sort sort) ;

List<User> findByLastname (String lastname, Pageable pageable) ;

限制查询结果

1
2
3
4
5
6

User findFirstByOrderByLastname () ;

User findTopByOrderByAgeDesc () ;

Page<User> queryFirst10ByLastname (String lastname, Pageable pageable) ;

Slice<User> findTop3ByLastname (String lastname, Pageable pageable) ;

List<User> findFirst10ByLastname (String lastname, Sort sort) ;

List<User> findTop10ByLastname (String lastname, Pageable pageable) ;

产生repository实例

XML配置方式

1
2
3
4
5
6
7
8
9
10
11

<?xml version="1.0" encoding="UTF-8"?>

< beans:beans xmlns:beans = "http://www.springframework.org/schema/beans"

xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"

xmlns = "http://www.springframework.org/schema/data/jpa"

xsi:schemaLocation = "http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/data/jpa

http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

< repositories base-package = "com.acme.repositories" />

</ beans:beans >

使用筛选器:

1
2
3

< repositories base-package = "com.acme.repositories" >

< context:exclude-filter type = "regex" expression = ".*SomeRepository" />

</ repositories >

JavaConfig方式

使用@Enable${store}Repositories注解。如 EnableMongoRepositories , EnableJpaRepositories

1
2
3
4
5
6
7
8
9

@Configuration

@EnableJpaRepositories ( "com.acme.repositories" )

class ApplicationConfiguration {

@Bean

public EntityManagerFactory entityManagerFactory () {

// …

}

}

编程方式

1
2

RepositoryFactorySupport factory = … // Instantiate factory here

UserRepository repository = factory.getRepository(UserRepository.class);

定制Repository实现

如果觉得默认的约定不够,可以定制实现。

1
2
3
4
5
6
7
8
9
10

interface UserRepositoryCustom {

public void someCustomMethod (User user) ;

}

class UserRepositoryImpl implements UserRepositoryCustom {

public void someCustomMethod (User user) {

// Your custom implementation

}

}

注意实现名是接口名加 Impl 。 不过可以定制后缀。

1
2

< repositories base-package = "com.acme.repository" />

< repositories base-package = "com.acme.repository" repository-impl-postfix = "FooBar" />

你可以到 这里 下载例子。

相关文件:

文件 描述
config/MongoConfig Spring配置文件,替代Spring XML配置文件
entity/Customer domain object
entity/Address domain object
repository/CustomerRepository DAO层接口
APP 测试类

MongoTemplate方式

以上啰嗦了很多,感觉和Mongo关系不大。 这是Spring Data为各种数据库如JPA,Mongo提供的一种统一的方式。 理论上来说,无论你使用Mysql, Oracle,Mongo,都可以采用这种方式组织你的代码。

但是, Spring Data for MongoDB还提供了另外一种方式, 类似JdbcTemplate的方式。 这种方式你可以自己定义你的repository的编程方式。 这种方式让你感觉更灵活, 不被上面的各种约定束缚住。

你可以通过XML或者JavaConfig方式配置MongoTemplate.

1) JavaConfig方式

1
2
3
4
5
6

/*

* Use the standard Mongo driver API to create a com.mongodb.Mongo instance.

*/

public @Bean Mongo mongo () throws UnknownHostException {

return new Mongo( "localhost" );

}

或者

1
2
3
4
5
6
7
8

/*

* Factory bean that creates the com.mongodb.Mongo instance

*/

public @Bean MongoFactoryBean mongo( ) {

MongoFactoryBean mongo = new MongoFactoryBean( ) ;

mongo.setHost( "localhost" ) ;

return mongo;

}

2) XML方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

<?xml version="1.0" encoding="UTF-8"?>

< beans xmlns = "http://www.springframework.org/schema/beans"

xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"

xmlns:context = "http://www.springframework.org/schema/context"

xmlns:mongo = "http://www.springframework.org/schema/data/mongo"

xsi:schemaLocation =

" http: // www.springframework.org / schema / context

http: // www.springframework.org / schema / context / spring-context-3.0.xsd

* http: // www.springframework.org / schema / data / mongo http: // www.springframework.org / schema / data / mongo / spring-mongo-1.0.xsd *

http: // www.springframework.org / schema / beans

http: // www.springframework.org / schema / beans / spring-beans-3.0.xsd ">

<!-- Default bean name is 'mongo' -->

* < mongo:mongo host = "localhost" port = "27017" /> *

</ beans >

可以设置更多的参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

< beans >

< mongo:mongo host = "localhost" port = "27017" >

< mongo:options connections-per-host = "8"

threads-allowed-to-block-for-connection-multiplier = "4"

connect-timeout = "1000"

max-wait-time = "1500}"

auto-connect-retry = "true"

socket-keep-alive = "true"

socket-timeout = "1500"

slave-ok = "true"

write-number = "1"

write-timeout = "0"

write-fsync = "true" />

</ mongo:mongo />

</ beans >

如果你想将MongoTemplate配置成Spring Bean:

1
2
3
4
5
6
7
8
9
10
11
12

@Configuration

public class MongoConfiguration {

public @Bean MongoDbFactory mongoDbFactory () throws Exception {

UserCredentials userCredentials = new UserCredentials( "joe" , "secret" );

return new SimpleMongoDbFactory( new Mongo(), "database" , userCredentials);

}

public @Bean MongoTemplate mongoTemplate () throws Exception {

return new MongoTemplate(mongoDbFactory());

}

}

或者XML:

1
2
3
4
5
6

< mongo:db-factory id = "anotherMongoDbFactory"

host = "localhost"

port = "27017"

dbname = "database"

username = "joe"

password = "secret" />

MongoTemplate

MongoTemplate 提供了非常多的操作MongoDB的方法。 它是线程安全的,可以在多线程的情况下使用。

MongoTemplate 实现了 MongoOperations 接口, 此接口定义了众多的操作方法如”find”, “findAndModify”, “findOne”, “insert”, “remove”, “save”, “update” and “updateMulti”等。

它转换domain object为 DBObject ,并提供了Query, Criteria, and Update等流式API。

缺省转换类为 MongoMappingConverter 。

它有几个构造函数:

  • MongoTemplate(Mongo mongo, String databaseName)
  • MongoTemplate(Mongo mongo, String databaseName, UserCredentials userCredentials)
  • MongoTemplate(MongoDbFactory mongoDbFactory)
  • MongoTemplate(MongoDbFactory mongoDbFactory, MongoConverter mongoConverter)
Repository

在这种方式下,你需要自己实现Repository的具体类。

一般情况下你需要为所有的Repository实现一个抽象的父类,在父类中实现大部分CRUD操作。 在子类中实现特定的操作方法。

你的Repository可以实现MongoRepository或者CrudRepository接口,但是不是必须的。

本例子中简单实现了一个简单的Repository,纯演示使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.data.domain.Sort;

import org.springframework.data.geo.Circle;

import org.springframework.data.geo.Distance;

import org.springframework.data.geo.GeoResults;

import org.springframework.data.geo.Point;

import org.springframework.data.mongodb.core.MongoTemplate;

import org.springframework.data.mongodb.core.query.Criteria;

import org.springframework.data.mongodb.core.query.NearQuery;

import org.springframework.data.mongodb.core.query.Query;

import org.springframework.stereotype.Repository;

import com.colobu.springmongo.entity.Customer;

@Repository

public class AnotherCustomerRepository {

@Autowired

private MongoTemplate mongoTemplate;

public Customer save (Customer c) {

mongoTemplate.save(c);

return c;

}

public void deleteAll () {

mongoTemplate.dropCollection(Customer.class);

}

public List<Customer> findAll () {

return mongoTemplate.findAll(Customer.class);

}

public List<Customer> findByLastname (String lastname, Sort sort) {

Criteria criteria = new Criteria( "lastname" ).is(lastname);

return mongoTemplate.find( new Query(criteria), Customer.class);

}

public GeoResults<Customer> findByAddressLocationNear (Point point, Distance distance) {

return mongoTemplate.geoNear(NearQuery.near(point).maxDistance(distance), Customer.class);

}

}

在这种情况下,方法名比较自由,你毋须遵守某种约定。

参考文档

  1. http://docs.spring.io/spring-data/data-mongo/docs/1.7.0.M1/reference/html/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值