Spring Data with MongoDB
节选自《Netkiller Spring Cloud 手札》
复杂的 @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 + "]";
}
}
指定表名
默认使用 class 作为表名
@Document
public class Multilevel {
...
...
}
指定特别表名
@Document(collection = "author")
@Id
@Id
private String id;
@Version
@Version
private Long version;
@Field 定义字段名
@Field("url")
private String link;
@Indexed
索引
普通索引
@Indexed
唯一索引
@Indexed(unique=true)
索引排序方式
@Indexed(name = "first_name_index", direction = IndexDirection.DESCENDING)
稀疏索引
稀疏索引允许唯一索引存在多个 null 值
@Indexed(unique = true, sparse = true)
private String uuid;
@Indexed(unique = true, sparse = true)
private String transactionId = null;
索引过期时间设置
@Indexed(name = "expire_after_seconds_index", expireAfterSeconds = 10)
private LocalDateTime updateDate;
@CompoundIndex 复合索引
普通复合索引
@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
}
唯一复合索引
唯一复合索引:楼层和房号不能相同,不然就是同一个房间了
@CompoundIndexes({
@CompoundIndex(name = "floor_num", def = "{'floor' : 1, 'num': 1}",unique=true)
})
不允许同名
@CompoundIndexes({ @CompoundIndex(name = "username", def = "{'firstname' : 1, 'lastname': 1}", unique = true) })
@TextIndexed
@Document(language = "spanish")
class SomeEntity {
@TextIndexed String foo;
@Language String lang;
Nested nested;
}
class Nested {
@TextIndexed(weight=5) String bar;
String roo;
}
@GeoSpatialIndex 地理位置索引
点数据索引
@GeoSpatialIndexed
private GeoJsonPoint location; // GPS 定位信息
2D 数据索引
@GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE)
@Transient 丢弃数据,不存到 mongodb
public class User {
@Transient
private Integer age;
// standard getter and setter
}
@DBRef 做外外键引用
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 + "]";
}
}
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 + "]";
}
}
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> {
}
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;
}
}
运行结果
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"
}
@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();
@NumberFormat
@NumberFormat(style=Style.CURRENCY)
private double money;
在 @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;
在 @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;
GeoJson 数据类型
@GeoSpatialIndexed
private GeoJsonPoint location; // GPS 地址位置
location = new GeoJsonPoint(Double.valueOf(longitude), Double.valueOf(latitude));