Home | 简体中文 | 繁体中文 | 杂文 | 打赏(Donations) | 云栖社区 | OSChina 博客 | Facebook | Linkedin | 知乎专栏 | Github | Search | About

8.2. Spring Data MongoDB

https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/

8.2.1. Example Spring Data MongoDB

8.2.1.1. pom.xml

注意Spring4 与 1.9.1.RELEASE有兼容性问题,日志提示 Error creating bean with name 'mongoTemplate' defined in ServletContext resource

			
		<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-mongodb</artifactId>
			<version>1.8.1.RELEASE</version>
		</dependency>			
			
			

8.2.1.2. springframework-servlet.xml

			
	<mongo:db-factory id="mongoDbFactory" host="${mongo.host}" port="${mongo.port}" dbname="${mongo.database}" />
	<!-- username="${mongo.username}" password="${mongo.password}" -->

	<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
        <constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>
    </bean>
    
    <mongo:mapping-converter id="converter" db-factory-ref="mongoDbFactory"/>
    <bean id="gridFsTemplate" class="org.springframework.data.mongodb.gridfs.GridFsTemplate">
      <constructor-arg ref="mongoDbFactory"/>
      <constructor-arg ref="converter"/>
    </bean>
			
			

例 8.2. Spring Data MongoDB - springframework-servlet.xml

				
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 
	xmlns:mvc="http://www.springframework.org/schema/mvc" 
	xmlns:context="http://www.springframework.org/schema/context" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:mongo="http://www.springframework.org/schema/data/mongo"
	xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="
        http://www.springframework.org/schema/beans     
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc 
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd
		http://www.springframework.org/schema/data/mongo
		http://www.springframework.org/schema/data/mongo/spring-mongo-1.5.xsd        
        ">

	<mvc:resources location="/images/" mapping="/images/**" />
	<mvc:resources location="/css/" mapping="/css/**" />
	<mvc:resources location="/js/" mapping="/js/**" />
	<mvc:resources location="/zt/" mapping="/zt/**" />
	<mvc:resources location="/sm/" mapping="/sm/**" />
	<mvc:resources location="/module/" mapping="/module/**" />

	<context:component-scan base-package="cn.netkiller.controller" />
	<!-- <context:property-placeholder location="classpath:resources/development.properties" /> -->
	<mvc:annotation-driven />

	<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
		<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
		<property name="prefix" value="/WEB-INF/jsp/" />
		<property name="suffix" value=".jsp" />
	</bean>

	<bean id="configuracion" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="location" value="classpath:resources/development.properties" />
	</bean>
	
	<!-- MongoDB Connection Factory -->
	<mongo:db-factory id="mongoDbFactory" host="${mongo.host}" port="${mongo.port}" dbname="${mongo.database}" />
	<!-- username="${mongo.username}" password="${mongo.password}" -->
	
	<!-- MongoDB template definition -->
	<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
        <constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>
    </bean>
    
    <!-- MongoDB GridFS template definition -->
    <mongo:mapping-converter id="converter" db-factory-ref="mongoDbFactory"/>
    <bean id="gridFsTemplate" class="org.springframework.data.mongodb.gridfs.GridFsTemplate">
      <constructor-arg ref="mongoDbFactory"/>
      <constructor-arg ref="converter"/>
    </bean>
    
	<!-- Redis Connection Factory -->
	<bean id="jedisConnFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:host-name="192.168.2.1" p:port="6379" p:use-pool="true" />

	<!-- redis template definition -->
	<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" p:connection-factory-ref="jedisConnFactory" />
 
</beans>
				
				

development.properties 配置内容

			
mongo.host=192.168.4.1
mongo.port=27017
mongo.username=test
mongo.password=passw0rd
mongo.database=website
			
			

8.2.1.3. POJO

			
package cn.netkiller.pojo;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

@Document(collection = "tracker")
public class Tracker {
	@Id
	private String id;
	private String name;
	private String unique;
	private String hostname;
	private String referrer;
	private String href;

	public Tracker() {
		// TODO Auto-generated constructor stub
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getUnique() {
		return unique;
	}

	public void setUnique(String unique) {
		this.unique = unique;
	}

	public String getHostname() {
		return hostname;
	}

	public void setHostname(String hostname) {
		this.hostname = hostname;
	}

	public String getReferrer() {
		return referrer;
	}

	public void setReferrer(String referrer) {
		this.referrer = referrer;
	}

	public String getHref() {
		return href;
	}

	public void setHref(String href) {
		this.href = href;
	}

	@Override
	public String toString() {
		return "Tracker [id=" + id + ", name=" + name + ", unique=" + unique + ", hostname=" + hostname + ", referrer=" + referrer + ", href=" + href + "]";
	}

}		
			
			

8.2.1.4. Controller

			
package cn.netkiller.controller;

import cn.netkiller.pojo.Tracker;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.*;
import org.springframework.web.bind.annotation.*;

@Controller
public class TrackerController {

	@Autowired
	private MongoTemplate mongoTemplate;

	public TrackerController() {

	}

	@RequestMapping("/tracker/test")
	@ResponseBody
	String hello() {
		return "Hello World!";
	}

	@RequestMapping("/tracker")
	@ResponseBody
	String execute() {
		Tracker tracker = new Tracker();
		tracker.setName("test");
		tracker.setUnique("111223456");
		tracker.setHostname("www.example.com");
		tracker.setHref("http://example.com/test.html");
		tracker.setReferrer("http://example.com/");
		this.mongoTemplate.insert(tracker);
		
		return tracker.toString();
	}

}
			
			

8.2.1.5. 查看测试结果

			
> db.tracker.find();
{ "_id" : ObjectId("5757c0b92c526a6bda5eea3a"), "_class" : "cn.netkiller.repositories.Tracker", "name" : "test", "unique" : "111223456", "hostname" : "www.example.com", "referrer" : "http://example.com/", "href" : "http://example.com/test.html" }

			
			

8.2.1.6. 条件查询

			
	@RequestMapping("/read/name/{name}")
	public ArrayList<Tracker> sort(@PathVariable String name) {
	
		Query query = new Query(Criteria.where("name").is(name)); 
		 
		ArrayList<Tracker> trackers =  (ArrayList<Tracker>) mongoTemplate.find(query, Tracker.class);
		return trackers;
	}			
			
			

8.2.2. @Document

复杂的 @Document 数据类型定义

		
package cn.netkiller.domain;

import java.util.Date;
import java.util.List;
import java.util.Map;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

@Document
public class MultilevelDirectSellingTradingRebate {

	public enum Type {
		POINT, CASH, GIFT
	}

	public enum Rebate {
		DIRECT, INDIRECT
	}

	public enum Status {
		New, Rejected, Approved
	}

	@Id
	private String id;
	public String name;
	public Date beginDate;
	public Date endDate;
	public double lowAmount;
	public double highAmount;
	public Type type;
	public Status status = Status.New;
	public List<Map<String, Map<?, ?>>> product;

	@Override
	public String toString() {
		return "MultilevelDirectSellingTradingRebate [id=" + id + ", name=" + name + ", beginDate=" + beginDate
				+ ", endDate=" + endDate + ", lowAmount=" + lowAmount + ", highAmount=" + highAmount + ", type=" + type
				+ ", status=" + status + ", product=" + product + "]";
	}

}
		
		

8.2.2.1. 指定表名

默认使用 class 作为表名

			
@Document
public class Multilevel {
	...
	...
}			
			
			

指定特别表名

			
@Document(collection = "author")			
			
			

8.2.2.2. @Id

			
	@Id
	private String id;		
			
			

8.2.2.3. @Version

			
	@Version 
	private Long version;			
			
			

8.2.2.4. @Field 定义字段名

			
@Field("url")
private String link;			
			
			

8.2.2.5. @Indexed

https://docs.spring.io/spring-data/mongodb/docs/current/api/org/springframework/data/mongodb/core/index/Indexed.html

索引

8.2.2.5.1. 普通索引
				
@Indexed		
				
				
8.2.2.5.2. 唯一索引
				
@Indexed(unique=true)	
				
				
8.2.2.5.3. 索引排序方式
				
@Indexed(name = "first_name_index", direction = IndexDirection.DESCENDING)		
				
				
8.2.2.5.4. 稀疏索引

稀疏索引允许唯一索引存在多个 null 值

				
	@Indexed(unique = true, sparse = true)
	private String uuid;
	
	@Indexed(unique = true, sparse = true)
	private String transactionId = null;
				
				
8.2.2.5.5. 索引过期时间设置
				
@Indexed(name = "expire_after_seconds_index", expireAfterSeconds = 10)
private LocalDateTime updateDate;				
				
				

8.2.2.6. @CompoundIndex 复合索引

8.2.2.6.1. 普通复合索引
				
@Document
@CompoundIndexes({
    @CompoundIndex(name = "email_age", def = "{'email.id' : 1, 'age': 1}")
})
public class User {
    //
}			
				
				
				
@Document
@CompoundIndexes({
  @CompoundIndex(def = "{'firstName':1, 'salary':-1}", name = "compound_index_1"),
  @CompoundIndex(def = "{'secondName':1, 'profession':1}", name = "compound_index_2")
})
public class Person {
  @Id private String id;
  private String firstName;
  private String secondName;
  private LocalDateTime dateOfBirth;
  private Address address;
  private String profession;
  private int salary;
  // constructor
  // getters and setters
}			
				
				
8.2.2.6.2. 唯一复合索引

唯一复合索引:楼层和房号不能相同,不然就是同一个房间了

				
@CompoundIndexes({ 
    @CompoundIndex(name = "floor_num", def = "{'floor' : 1, 'num': 1}",unique=true) 
})				
				
				

不允许同名

				
@CompoundIndexes({ @CompoundIndex(name = "username", def = "{'firstname' : 1, 'lastname': 1}", unique = true) })		
				
				

8.2.2.7. @TextIndexed

			
@Document(language = "spanish")
class SomeEntity {

    @TextIndexed String foo;

    @Language String lang;

    Nested nested;
}

class Nested {

    @TextIndexed(weight=5) String bar;
    String roo;
}			
			
			

8.2.2.8. @GeoSpatialIndex 地理位置索引

点数据索引

				
	@GeoSpatialIndexed
	private GeoJsonPoint location; // GPS 定位信息
				
			

2D 数据索引

				
@GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE)				
				
			

8.2.2.9. @Transient 丢弃数据,不存到 mongodb

			
public class User {
     
    @Transient
    private Integer age;

    // standard getter and setter
 
}	
			
			

8.2.2.10. @DBRef 做外外键引用

8.2.2.10.1. Article 类
				
package cn.netkiller.api.domain;

import java.util.List;

import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;

@Document
public class Article {

	private String title; // 名称
	private String description; // 描述
	private String tag; // 类型
	@DBRef
	private List<Hypermedia> hypermedia; // 图片,视频

	public Article() {
		// TODO Auto-generated constructor stub
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public String getTag() {
		return tag;
	}

	public void setTag(String tag) {
		this.tag = tag;
	}

	public List<Hypermedia> getHypermedia() {
		return hypermedia;
	}

	public void setHypermedia(List<Hypermedia> hypermedia) {
		this.hypermedia = hypermedia;
	}

	@Override
	public String toString() {
		return "Article [title=" + title + ", description=" + description + ", tag=" + tag + ", hypermedia=" + hypermedia + "]";
	}

}
				
				
				
8.2.2.10.2. Hypermedia 类
				
package api.domain;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

@Document
public class Hypermedia {

	@Id
	private String id;
	private String hash;
	private String name;
	private String size;

	public Hypermedia() {
		// TODO Auto-generated constructor stub
	}

	public Hypermedia(String hash, String name, String size) {
		this.hash = hash;
		this.name = name;
		this.size = size;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getHash() {
		return hash;
	}

	public void setHash(String hash) {
		this.hash = hash;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getSize() {
		return size;
	}

	public void setSize(String size) {
		this.size = size;
	}

	@Override
	public String toString() {
		return "Hypermedia [id=" + id + ", hash=" + hash + ", name=" + name + ", size=" + size + "]";
	}

}
				
				
				

如果你只查询 Article 表,不会单独查询 Hypermedia,返回结果可以掩藏 Id ,不写 get/set 方法即可。

				
package cn.netkiller.api.domain;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

@Document
public class Hypermedia {

	@Id
	private String id;
	private String hash;
	private String name;
	private String size;

	public Hypermedia() {
		// TODO Auto-generated constructor stub
	}

	public Hypermedia(String hash, String name, String size) {
		this.hash = hash;
		this.name = name;
		this.size = size;
	}

	public String getHash() {
		return hash;
	}

	public void setHash(String hash) {
		this.hash = hash;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getSize() {
		return size;
	}

	public void setSize(String size) {
		this.size = size;
	}

	@Override
	public String toString() {
		return "Hypermedia [hash=" + hash + ", name=" + name + ", size=" + size + "]";
	}

}

				
				
8.2.2.10.3. MongoRepository
				
package cn.netkiller.api.repository;

import org.springframework.data.mongodb.repository.MongoRepository;

import api.domain.Article;

public interface ArticleRepository extends MongoRepository<Article, String> {

}

				
				
				
package cn.netkiller.api.repository;

import org.springframework.data.mongodb.repository.MongoRepository;

import api.domain.Hypermedia;

public interface HypermediaRepository extends MongoRepository<Hypermedia, String> {

}
				
				
8.2.2.10.4. RestController
				
package cn.netkiller.api.restful;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import api.domain.Article;
import api.domain.Hypermedia;
import api.repository.ArticleRepository;
import api.repository.HypermediaRepository;

@RestController
@RequestMapping("/article")
public class ArticleRestController {

	@Autowired
	private ArticleRepository articleRepository;

	@Autowired
	private HypermediaRepository hypermediaRepository;

	public ArticleRestController() {
		// TODO Auto-generated constructor stub
	}

	@GetMapping("/save")
	public Article save() {

		Article article = new Article();
		article.setTitle("标题");
		article.setDescription("摘要");
		article.setTag("标签");

		Hypermedia hypermedia = new Hypermedia("AAA", "BBB", "CCC");
		hypermediaRepository.save(hypermedia);

		List<Hypermedia> hypermedias = new ArrayList<Hypermedia>();
		hypermedias.add(hypermedia);

		article.setHypermedia(hypermedias);

		articleRepository.save(article);

		System.out.println(article);

		return article;
	}

}
				
				
				
8.2.2.10.5. 运行结果
				
neo@MacBook-Pro ~ % curl -s -H "Accept: application/json" -H "Content-Type: application/json" -H "Authorization: Bearer ${TOKEN}" -X GET ${URL}/article/save | jq
{
  "title": "标题",
  "description": "摘要",
  "tag": "标签",
  "hypermedia": [
    {
      "hash": "AAA",
      "name": "BBB",
      "size": "CCC"
    }
  ]
}
				
				

MongoDB 结果

db.getCollection('article').find({})

				
/* 1 */
{
    "_id" : ObjectId("5bab66f8c92782395817cb05"),
    "title" : "标题",
    "description" : "摘要",
    "tag" : "标签",
    "hypermedia" : [ 
        {
            "$ref" : "hypermedia",
            "$id" : ObjectId("5bab66f8c92782395817cb04")
        }
    ],
    "_class" : "cn.netkiller.api.domain.Article"
}				
				
				

db.getCollection('hypermedia').find({})

				
/* 1 */
{
    "_id" : ObjectId("5bab66b9c927823951f4f5fe"),
    "hash" : "AAA",
    "name" : "BBB",
    "size" : "CCC",
    "_class" : "api.domain.Hypermedia"
}				
				
				

8.2.2.11. @DateTimeFormat

			
@DateTimeFormat( pattern = "yyyy-MM-dd" )
private Date birthday

@DateTimeFormat(iso = DateTimeFormat.ISO.NONE)
private final Calendar datetime;

@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
private Date date;

@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private Date createdDate = new Date();			
			
			

8.2.2.12. @JsonFormat 格式化 json 时间格式

默认 json 中的时间格式是这样的

			
"createDate":"2018-09-11T07:34:20.106+0000","updateDate":"2018-09-11T07:34:20.106+0000"			
			
			

@JsonFormat 可以格式化 json 返回的时间格式。

			
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")			
			
			

格式化后

			
"createDate":"2018-09-11 07:42:44","updateDate":"2018-09-11 07:42:44"			
			
			

解决时区问题,MongoDb 默认使用UTC,显示时间相差8小时

			
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")	
private Date createdDate = new Date();			
			
			

8.2.2.13. @NumberFormat

				@NumberFormat(style=Style.CURRENCY)
				private double money;
			

8.2.2.14. 在 @Document 中使用 Enum 类型

			
	public enum Type {
		POINT, CASH, GIFT
	}

	public enum Rebate {
		DIRECT, INDIRECT
	}

	public enum Status {
		New, Rejected, Approved
	}
			
			

枚举类型的赋值方法

			
		MultilevelDirectSellingTradingRebate multilevelDirectSellingTradingRebate = new MultilevelDirectSellingTradingRebate();
		multilevelDirectSellingTradingRebate.name = "TEST";
		multilevelDirectSellingTradingRebate.beginDate = new Date();
		multilevelDirectSellingTradingRebate.endDate = new Date();
		multilevelDirectSellingTradingRebate.lowAmount = 1.5d;
		multilevelDirectSellingTradingRebate.highAmount = 100d;
		multilevelDirectSellingTradingRebate.type = Type.CASH;			
			
			

8.2.2.15. 在 @Document 中定义数据结构 List/Map

			
	public List<Map<String, Map<?, ?>>> product;
			
			

下面是数据集结构的赋值例子

			
	Map<Enum<Rebate>, Double> rebate = new HashMap<Enum<Rebate>, Double>();

	rebate.put(Rebate.DIRECT, 10.05d);
	rebate.put(Rebate.INDIRECT, 6.05d);

	Map<String, Map<?, ?>> prod1 = new HashMap<String, Map<?, ?>>();
	prod1.put("USDRMB", rebate);

	List<Map<String, Map<?, ?>>> products = new ArrayList<Map<String, Map<?, ?>>>();
	products.add(prod1);
	multilevelDirectSellingTradingRebate.product = products;
			
			

8.2.2.16. GeoJson 数据类型

			
	@GeoSpatialIndexed
	private GeoJsonPoint location; // GPS 地址位置
			
			
			
location = new GeoJsonPoint(Double.valueOf(longitude), Double.valueOf(latitude));			
			
			

8.2.3. MongoRepository

8.2.3.1. 扫描仓库接口

默认不需要设置,除非你的包不在当前包下,或者命令不是 repository。

			
@EnableMongoRepositories(basePackages = "cn.netkiller.repository")		
			
			

8.2.3.2. findAll()

			
	@RequestMapping(value = "read", method = RequestMethod.GET, produces = { "application/xml", "application/json" })
	@ResponseStatus(HttpStatus.OK)
	public List<Withdraw> read() {
		return repository.findAll();
	}
			
			

8.2.3.3. deleteAll()

			
repository.deleteAll();
			
			

8.2.3.4. save()

			
repository.save(new City("Shenzhen", "China"));
			
			

8.2.3.5. count()

			
	@RequestMapping("count")
	public long count() {
		return repository.count();
	}
			
			

8.2.3.6. exists() 判断是否存在

			
boolean isExists = userRepository.exists(user.getId());			
			
			

8.2.3.7. existsById()

			
memberRepository.existsById(id);			
			
			

8.2.3.8. findByXXXX

			
List<User> findByName(String name);

List<User> users = userRepository.findByName("Eric");
			
			

8.2.3.9. findAll with OrderBy

8.2.3.9.1. order by boolean 布尔型数据排序

因为 boolean 数据 true = 1, false = 0 所以 ASC false 会排列在前面。所有很多时候而我们需要 DESC 排序

				
List<ShippingAddress> shippingAddress = shippingAddressRepository.findAllByMemberIdOrderByDefaultsDesc(memberId);			
				
				

8.2.3.10. findAll with Sort

			
List<User> users = userRepository.findAll(new Sort(Sort.Direction.ASC, "name"));			
			
			

8.2.3.11. FindAll with Pageable

			
Pageable pageable = PageRequest.of(0, 1);
Page<User> page = userRepository.findAll(pageable);
List<User> users = pages.getContent();	
			
			
8.2.3.11.1. PageRequest - springboot 1.x 旧版本
			
Page<User> findByLastname(String lastname, Pageable pageable);			
			
				
			
	@RequestMapping(value = "read/{size}/{page}", method = RequestMethod.GET, produces = { "application/xml", "application/json" })
	@ResponseStatus(HttpStatus.OK)
	public List<Withdraw> readPage(@PathVariable int size, @PathVariable int page){
		PageRequest pageRequest = new PageRequest(page-1,size);
		return repository.findAll(pageRequest).getContent();
	}
			
				

URL翻页参数,每次返回10条记录

					第一页 http://localhost:8080/v1/withdraw/read/10/1.json
					第二页 http://localhost:8080/v1/withdraw/read/10/2.json
					...
					第五页 http://localhost:8080/v1/withdraw/read/10/5.json
				

8.2.3.12. StartingWith 和 EndingWith

			
List<User> findByNameStartingWith(String regexp);
List<User> findByNameEndingWith(String regexp);

List<User> users = userRepository.findByNameStartingWith("N");
List<User> users = userRepository.findByNameEndingWith("o");
			
			

8.2.3.13. Between

数值范围

			
List<User> findByAgeBetween(int ageGT, int ageLT);

List<User> users = userRepository.findByAgeBetween(20, 50);
			
			

日期范围,取值 e.g. 2018-07-04 00:00:00 and 2018-07-04 23:59:59

			
List<Member> findByCreatedDateBetween(DateTime start, DateTime end);	

List<Member> findByCreatedDate(@Temporal(TemporalType.DATE) Date date);		
			
			

8.2.3.14. Before / After

			
List<Assets> findAllByUpdateDateBefore(Date yesterday);
List<Assets> findAllByUpdateDateBeforeAndStatus(Date yesterday, String status);

List<Assets> findAllByUpdateDateAfter(Date yesterday);
			
			

8.2.3.15. @Query

			
public interface PersonRepository extends MongoRepository<Person, String> {
	@Query("{ 'name' : ?0 }")
	List<Person> findWithQuery(String userId);
}

	@Query(value = "{'statusHistories':{$elemMatch:{'status':{$in:['PROCESSABLE']}}},'created' : { '$gt' : { '$date' : ':?0' } , '$lt' : { '$date' : ':?1'}}}", count = true)
	Long countMe(@Param("dateFrom") Date datefrom, @Param("dateTo") Date dateTo);
			
			

8.2.4. mongoTemplate

导入与模板相关的包

		
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;		
		
		

注入 MongoTemplate 对象

		
	@Autowired
	private MongoTemplate mongoTemplate;		
		
		

8.2.4.1. Save 保存

			
User user = new User();
user.setName("Netkiller"); 
mongoTemplate.save(user, "user");			
			
			

更新数据

			
user = mongoTemplate.findOne(Query.query(Criteria.where("name").is("Jam")), User.class);
user.setName("Neo");
mongoTemplate.save(user, "user");	
			
			

8.2.4.2. Insert

			
User user = new User();
user.setName("Neo");
mongoTemplate.insert(user, "user");			
			
			
			
BSONObject personBsonObj = BasicDBObjectBuilder.start()
                .add("name","Neo Chen")
                .add("age",27)
                .add("address",null).get();

mongoTemplate.insert(personBsonObj,"personCollection");
			
			

document in the db:

			
db.personCollection.findOne().pretty();
{"age":21,"name":"John Doe";"address":null}*			
			
			

8.2.4.3. updateFirst 修改符合条件第一条记录

updateFirst 修改符合条件第一条记录

			
Query query = new Query();
query.addCriteria(Criteria.where("name").is("Neo"));
Update update = new Update();
update.set("name", "Netkiller");
mongoTemplate.updateFirst(query, update, User.class);			
			
			

8.2.4.4. updateMulti 修改符合条件的所有

更新所有数据

			
Query query = new Query();
query.addCriteria(Criteria.where("name").is("Neo"));
Update update = new Update();
update.set("name", "Jerry");
mongoTemplate.updateMulti(query, update, User.class);			
			
			

8.2.4.5. 查找并保存

			
Query query = new Query();
query.addCriteria(Criteria.where("name").is("Luck"));
Update update = new Update();
update.set("name", "Lisa");
User user = mongoTemplate.findAndModify(query, update, User.class);			
			
			

8.2.4.6. upsert - 修改符合条件时如果不存在则添加

			
Query query = new Query();
query.addCriteria(Criteria.where("name").is("Green"));
Update update = new Update();
update.set("name", "Tom");
mongoTemplate.upsert(query, update, User.class);			
			
			
			
mongoTemplate.upsert(new Query(Criteria.where("age").is("18")), new Update().set("name", "neo"), collectionName);			
			
			

8.2.4.7. 删除

			
User user = new User();
user.setId("5bbf091efd9557069c4a25c5")			
mongoTemplate.remove(user, "user");			
			
			

8.2.4.8. 查找一条数据

			

public Person findOneByName(String name) {
   Query query = new Query();
   query.addCriteria(Criteria.where("name").is(name));
   return mongoTemplate.findOne(query, Person.class);
}		
			
			

8.2.4.9. 查找所有数据

			
public List<Person> findByName(String name) {
   Query query = new Query();
   query.addCriteria(Criteria.where("name").is(name));
   return mongoTemplate.find(query, Person.class);
}				
			
			

8.2.4.10. Query

8.2.4.10.1. 翻页
				
public List<Person> getAllPersonPaginated(int pageNumber, int pageSize) {
   Query query = new Query();
   query.skip(pageNumber * pageSize);
   query.limit(pageSize);
   return mongoTemplate.find(query, Person.class);
}			
				
				
8.2.4.10.2. between

实现一个区间条件 new Criteria("createdDate").gte(beginDate).lte(endDate)

			
	public boolean AccountDeposit(Date beginDate, Date endDate) {

		MatchOperation matchOperation = match(new Criteria("createdDate").gte(beginDate).lte(endDate));
		GroupOperation groupOperation = group("loginname").sum("amount").as("amount");
		SortOperation sortOperation = sort(new Sort(Direction.ASC, "loginname"));

		Aggregation aggregation = newAggregation(matchOperation, groupOperation, sortOperation);
		AggregationResults<AccountSettlementDetails> results = mongoTemplate.aggregate(aggregation, AccountSettlementDetails.class, AccountSettlementDetails.class);

		if (results.getMappedResults() != null) {
			log.info(results.getRawResults().get("result").toString());
			for (AccountSettlementDetails settlementDetails : results.getMappedResults()) {
							
				log.info("{}", settlementDetails.toString());
				
			}
		}
		return true;
	}
			
				

8.2.4.11. Criteria

8.2.4.11.1. is
			
Query query = new Query();
query.addCriteria(Criteria.where("name").is("Neo"));
List<User> users = mongoTemplate.find(query, User.class);		
			
				
8.2.4.11.2. Regex 正则表达式搜索

查询以N开头的名字

			
Query query = new Query();
query.addCriteria(Criteria.where("name").regex("^N"));
List<User> users = mongoTemplate.find(query,User.class);	
			
				

查询以o结尾的名字

			
Query query = new Query();
query.addCriteria(Criteria.where("name").regex("o$"));
List<User> users = mongoTemplate.find(query, User.class);
			
				
8.2.4.11.3. lt 和 gt

查询年龄小于 < 30 并 > 20 的用户

			
Query query = new Query();
query.addCriteria(Criteria.where("age").lt(30).gt(20));
List<User> users = mongoTemplate.find(query,User.class);
			
				

查找日期范围

			
Date start = DateUtil.convertStringToDateTime("2014-02-10 20:38:44");
Date end = DateUtil.convertStringToDateTime("2014-02-10 20:38:50");
					
Query query = new Query();
Criteria criteria = Criteria.where("delflag").is(false);
criteria.and("modifyDate").gte(start).lte(end);
query.addCriteria(criteria);
query.limit(10);
			
			
				
8.2.4.11.4. exists()
				
Query query = new Query();
query.addCriteria(
    new Criteria().andOperator(
        Criteria.where("field1").exists(true),
        Criteria.where("field1").ne(false)
    )
);

List<Foo> result = mongoTemplate.find(query, Foo.class);
System.out.println("query - " + query.toString());

for (Foo foo : result) {
    System.out.println("result - " + foo);
}			
				
				
8.2.4.11.5. 包含
				
public List<Person> findByFavoriteBooks(String favoriteBook) {
   Query query = new Query();
   query.addCriteria(Criteria.where("favoriteBooks").in(favoriteBook));
   return mongoTemplate.find(query, Person.class);
}				
				
				

8.2.4.12. Update

8.2.4.12.1. set
				
Update update = new Update();
update.set("name", "Netkiller");				
				
				
8.2.4.12.2. 追加数据
				
	Query query = Query.query(Criteria.where("id").is("5bbf091efd9557069c4a25c5"));
	Update update = new Update().push("author", new Author("neo", "chen"));
	mongoTemplate.updateFirst(query, update, Article.class);				
				
				
8.2.4.12.3. 更新数据
				
	Query query = Query.query(Criteria.where("classId").is("1").and("Students.studentId").is("1"));
    Update update = Update.update("Students.$.name", "lisa");
    mongoTemplate.upsert(query, update, "class");
				
				
8.2.4.12.4. 删除数据
				
	Query query = Query.query(Criteria.where("classId").is("1").and("Students.studentId").is("3"));
	Update update = new Update();
	update.unset("Students.$");
	mongoTemplate.updateFirst(query, update, "class");				
				
				
8.2.4.12.5. inc
				
public void updateMultiplePersonAge() {
   Query query = new Query();
   Update update = new Update().inc("age", 1);
   mongoTemplate.findAndModify(query, update, Person.class);;
}
				
				
8.2.4.12.6. update.addToSet
				
Query query = Query.query(Criteria.where("classId").is("1"));
Student student = new Student("1", "lisa", 3, "girl");
Update update = new Update();
update.addToSet("Students", student);
mongoTemplate.upsert(query, update, "class");				
				
				

8.2.4.13. BasicUpdate

BasicUpdate 是底层更新可操作,需要手动实现$set等语句

			
BasicDBObject basicDBObject = new BasicDBObject();  
basicDBObject.put("$set", new BasicDBObject("date","2018-09-09"));  
Update update = new BasicUpdate(basicDBObject);  
mongoTemplate.updateFirst(new Query(Criteria.where("nickname").is("netkiller")), update,collectionName);  			
			
			

8.2.4.14. Sort

按照年龄排序

			
Query query = new Query();
query.with(new Sort(Sort.Direction.ASC, "age"));
List<User> users = mongoTemplate.find(query,User.class);
			
			

8.2.4.15. Query + PageRequest

			
final Pageable pageableRequest = new PageRequest(0, 2);
Query query = new Query();
query.with(pageableRequest);
			
			

8.2.4.16. newAggregation

			
		MultilevelDirectSellingAccountRewardsSettlementDetails multilevelDirectSellingAccountRewardsSettlementDetails = new MultilevelDirectSellingAccountRewardsSettlementDetails();
		multilevelDirectSellingAccountRewardsSettlementDetails.setLoginname("111");
		multilevelDirectSellingAccountRewardsSettlementDetails.setPhone("111");
		multilevelDirectSellingAccountRewardsSettlementDetails.setRecommenderLoginname("111");
		multilevelDirectSellingAccountRewardsSettlementDetails.setRecommenderPhone("111");
		multilevelDirectSellingAccountRewardsSettlementDetails.setRecommenderName("Neo");
		multilevelDirectSellingAccountRewardsSettlementDetails.setRecommenderType("客户");
		multilevelDirectSellingAccountRewardsSettlementDetails.setAmount(5.02);
		multilevelDirectSellingAccountRewardsSettlementDetails.setCreatedDate(new Date());
		multilevelDirectSellingAccountRewardsSettlementDetailsRepository.save(multilevelDirectSellingAccountRewardsSettlementDetails);
		
		Date beginDate = this.getToday("00:00:00");
		Date endDate = this.getToday("23:59:59");
		log.info(beginDate.toString() + " ~ " + endDate.toString());
		
		GroupOperation groupOperation = group("loginname").sum("amount").as("amount");
		MatchOperation matchOperation = match(new Criteria("createdDate").gte(beginDate).lte(endDate));
		SortOperation sortOperation = sort(new Sort(Direction.ASC, "loginname"));

		Aggregation aggregation = newAggregation(matchOperation, groupOperation, sortOperation);
		AggregationResults<MultilevelDirectSellingAccountRewardsSettlementDetails> results = mongoTemplate.aggregate(aggregation, MultilevelDirectSellingAccountRewardsSettlementDetails.class, MultilevelDirectSellingAccountRewardsSettlementDetails.class);		
		System.out.println(results.getRawResults().get("result").toString());
			
			

8.2.4.17. 创建索引

			
mongoOps.indexOps(User.class).ensureIndex(new Index().on("name", Direction.ASC));			
			
			

8.2.4.18. 子对象操作

8.2.4.18.1. List 类型
				
package cn.netkiller.api.domain;

import java.util.List;

import javax.persistence.Id;
import org.springframework.data.mongodb.core.mapping.Document;

@Document
public class Article {

	@Id
	private String id;
	private String title;
	private String description;

	List<Author> author;
	public static class Author {
		private String id;
		private String firstname;
		private String lastname;

		public Author(String firstname, String lastname) {
			this.firstname = firstname;
			this.lastname = lastname;
		}
	}
}
				
				

更新

				
db.getCollection('foo').update({"author.firstname":"neo"},{"$set":{"author.$.firstname":"netkiller"}})					
				
				

更新数据

				
	Query query = Query.query(Criteria.where("author.firstname").is("neo"));
	Update update = new Update().set("author.$.firstname", "netkiller");
	mongoTemplate.updateFirst(query, update, Article.class);
				
				

追加数据

				
	Query query = Query.query(Criteria.where("id").is("5bbf091efd9557069c4a25c5"));
	Update update = new Update().push("author", new Author("neo", "chen"));
	mongoTemplate.updateFirst(query, update, Article.class);				
				
				

删除数据

				
		Query query = Query.query(Criteria.where("id").is("5bbf091efd9557069c4a25c5"));
		Update update = new Update().pull("author", new Author("jerry", "lee"));
		mongoTemplate.updateFirst(query, update, Article.class);				
				
				

8.2.5. GeoJson 反序列化

正常情况下是不需要做反序列化操作的。如花你想测试,打印一些信息可以这样做。

		
package cn.netkiller.api.config;

import java.io.IOException;

import org.springframework.data.mongodb.core.geo.GeoJsonPoint;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;

public class GeoJsonDeserializer extends JsonDeserializer<GeoJsonPoint> {

	@Override
	public GeoJsonPoint deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {

		final JsonNode tree = jsonParser.getCodec().readTree(jsonParser);
		final String type = tree.get("type").asText();
		final JsonNode coordsNode = tree.get("coordinates");

		System.out.println(tree.toString());
		System.out.println(type);
		System.out.println(coordsNode.toString());

		double x = 0;
		double y = 0;
		if ("Point".equalsIgnoreCase(type)) {
			x = coordsNode.get(0).asDouble();
			y = coordsNode.get(1).asDouble();
		} else {
			System.out.println(String.format("No logic present to deserialize %s ", tree.asText()));
		}

		final GeoJsonPoint point = new GeoJsonPoint(x, y);

		return point;
	}
}

		
		

使用 @JsonDeserialize 指定反序列化 Class

		
@Document
public class Address {
	@Id
	private String id;
	// @GeoSpatialIndexed
	@JsonDeserialize(using = GeoJsonDeserializer.class)
	private GeoJsonPoint location; // GPS 定位信息
}		
		
		

8.2.6. FAQ

8.2.6.1. location object expected, location array not in correct format; nested exception is com.mongodb.MongoWriteException: location object expected, location array not in correct format

GeoJsonPoint 可能设置了索引,且有些数据部正确。