原始博文链接
之前始终想试试 MongoDB 但总是没什么机会用,关系型数据库还是大多数利用场景的首选。最近有机会须要做个评测零碎,其中波及蕴含大量选项的不同品种的表单解决,同时规模并不大,遂思考一番后决定用 MongoDB 来作为存储后端,正好尝尝鲜,也能加重点 CRUD 的干燥感。本文次要进行一些根本介绍以及展现应用示例,总结了一些应用的感触和体验。
MongoDB 简介
MongoDB 算是相当驰名的一款开源 NoSQL 数据库了,它也曾经有十多年的历史,截止到目前(文章公布)MongoDB 也曾经来到了 4.4 版本,以 MongoDB 数据库为根底成立的 MongoDB 公司目前除了 MongoDB Server 之外还提供了 MongoDB Atlas(SaaS 云服务数据库)、Charts(可视化图表)、Compass(数据库 GUI 工具)、Connectors(连接器)等各种配套服务和工具。有趣味能够看一看 MongoDB 的倒退历史(英),作为饭后读物非常不错,能够看到一款开源数据库的成长过程。
MongoDB 说到底是一款开源的由 C ++ 编写的文档数据库,面向用户展示的构造与一般 RDB 也比拟类似,其外围概念根本都能在 RDB 中找到对应的内容:
Mongo | RDB | 阐明 |
---|---|---|
database | database | 数据库 |
collection | table | 数据汇合 – 数据表 |
document | row | 文档 – 数据记录 |
field | column | 字段 – 数据列 |
index | index | 索引 |
其中最外围的便是文档,它代表了 MongoDB 中的一个数据记录。MongoDB 的文档构造如下图所示,很显著相似于 JSON,能够蕴含数组和其余文档。
无 schema 的构造(能够有)配合丰盛灵便的查问语句使得 MongoDB 相较于传统 RDB 具备很大的灵活性。
装置好 MongoDB 而后装个 GUI(能够抉择 MongoDB Compass),而后关上 GUI 工具连贯到数据库操作一番插入几条数据根本就可能明确其次要的能力。装置办法这里不赘述,因为顺手一查就有,不过装置时须要留神的是 MongoDB 原生反对分布式架构,包含分片、主备等等,留神装置设置。
SpringData MongoDB
理解了 MongoDB 之后就能够将其利用到理论应用场景中,而作为独立的存储工具,又必然波及到连贯交互,因而 MongoDB 提供了各种语言的 Driver,其中也蕴含了 Java。随着利用复杂度减少,各种工具提供了更高阶的形象或封装来简化对 DB 的操作以提高效率,SpringData 便是其中之一,它作为 Spring 对于数据存储层拜访的对立根底工具,也提供了针对 MongoDB 的实现。
BSON
BSON 是 MongoDB 数据存储和网络传输的次要格局,它意为 Binary JSON,它的字面意思间接示意了它被创造进去的次要起因。BSON 的二进制构造蕴含了类型和长度信息以获取更块的解析速度和更高效的查问性能,此外 BSON 扩大反对了非 JSON 原生的数据类型如日期、二进制数据等。得益于此,所有可能在 JSON 中表白的数据都可能间接在 MongoDB 中存储、读取。
此外 MongoDB 的查问同样采纳类 JSON 的格局来形容,集体感觉有一种谐和、自描述的感觉。配合各种操作符,表达能力很强且有很不错的可浏览性。这个个性也在 Driver API 中有所体现,以 Java 角度来说,数据和查问都能够应用同一种类型(org.bson.Document
)来示意,十分优雅。举一个简略的查问条件例子:
{
status: "A",
$or: [{ price: { $lt: 30} }, {item: /^p/} ]
}
上述形容了查问抉择 status 为 ”A” 且 price 大于 30 或 item 以字符 p 结尾(反对正则表达式)的所有文档。
Mongo Java Driver
Driver 作为与 MongoDB 通信交互的根底,Spring Data 所进行的操作最初还是会调用 Driver 提供的办法来发送申请,这些额定的封装操作最为重要的几点目标是查问的构建以及返回值到对象的映射,其实这也是 ORM 框架所做的次要内容。想较于 JDBC 只提供了对 SQL 字符串的提交解决,Mongo 的 Java Driver 要丰盛许多,间接提供了对象的解码编码以及函数式查问构建,这也意味着只用原生 Driver 的 API 也能够很好地适配面向对象以及便捷查问。
Spring Data
Spring Data 作为 Spring 对于数据存储层拜访的对立根底工具,也提供了针对 MongoDB 的封装。只管原生的 Java Mongo Driver 曾经提供了一些要害的能力,然而 Spring Data 并没有应用这些内容,查问的构建以及对象转换依然是 Spring 本人的一套。Spring Data MongoDB 看起来与 Driver 原生提供的性能有些反复,然而毕竟 Spring Data 个性更加丰盛,同时提供了对立的 Data Repository 拜访形式。不过应用 Spring Data 这一套也并不是没有毛病,Spring 的这层包装也会显得比拟重,若不是在 Spring 我的项目中应用,采纳原生 Driver 未尝不是好的抉择。
利用示例
上面别离对原生 Driver 和 Spring Data MongoDB 的应用做一些介绍和示例。
原生 Driver
原生的 Java Driver 4.1 版本文档传送门在此,文档很全面,包含了教程、API 文档、源码、更新信息等等。这里简述一些次要操作形容一番。
入口类是 com.mongodb.client.MongoClient
而并不是相似 xxxConnection 的模式,然而实质上依然是获取连贯对象。很显著 Mongo Driver 的形象水平自身就比拟高,MongoClient 提供了多种创立形式:
// direct for localhost:27017
MongoClient mongoClient1= MongoClients.create();
// specify host and port
MongoClient mongoClient2= MongoClients.create(MongoClientSettings.builder()
.applyToClusterSettings(builder ->
builder.hosts(Arrays.asList(new ServerAddress("hostOne", 27018))))
.build());
// from connection string
MongoClient mongoClient = MongoClients.create("mongodb://admin:admin@localhost:27017/?authSource=admin&readPreference=primary&ssl=false");
// print databases' name
mongoClient.listDatabaseNames().forEach(System.out::println);
通常 MongoClient 作为单例存在即可,通过 MongoClient 能够获取到 com.mongodb.client.MongoDatabase
代表 MongoDB 中的 Database,而 Database 对象又能够获取到具体的 Collection 即com.mongodb.client.MongoCollection
,通过 Collection 对象来执行对应的增删改查:
// get database
MongoDatabase database = mongoClient.getDatabase("mydb");
// get collection
MongoCollection<Document> collection = database.getCollection("test");
// insert
Document doc = new Document("name", "MongoDB")
.append("type", "database");
collection.insertOne(doc);
List<Document> documents = new ArrayList<Document>();
for (int i = 0; i < 100; i++) {documents.add(new Document("i", i));
}
collection.insertMany(documents);
// query
Document myDoc = collection.find().first();
// query with filter
Document filteredDoc = collection.find(and(gt("i", 50), lte("i", 100))).first();
// update
collection.updateOne(eq("i", 10), set("i", 110));
UpdateResult updateResult = collection.updateMany(lt("i", 100), inc("i", 100));
System.out.println(updateResult.getModifiedCount());
// delete
collection.deleteOne(eq("i", 110));
DeleteResult deleteResult = collection.deleteMany(gte("i", 100));
System.out.println(deleteResult.getDeletedCount());
可见 Mongo Driver 提供了很多静态方法来构建查问条件,这些办法根本都属于 com.mongodb.client.model.Filters
这个类下。而在面向对象方面的对象映射上,MongoClient、MongoDatabase、MongoCollection 均反对增加 CodecRegistry 来注册对象编码 / 解码器:
// build codecRegistry with automatic POJO codecs
CodecRegistry pojoCodecRegistry = fromRegistries(MongoClientSettings.getDefaultCodecRegistry(),
fromProviders(PojoCodecProvider.builder().automatic(true).build()));
// mongo client level
MongoClientSettings settings = MongoClientSettings.builder()
.codecRegistry(pojoCodecRegistry)
.build();
MongoClient mongoClient = MongoClients.create(settings);
// mongo database level
database = database.withCodecRegistry(pojoCodecRegistry);
// mongo collection level
collection = collection.withCodecRegistry(pojoCodecRegistry);
配置了对象的 codec 之后就能够利用 MongoCollection 的泛型参数来间接执行对应类型的查问和插入(更新和删除操作与非对象 codec 模式无异):
// example POJO
public final class Person {
private ObjectId id;
private String name;
private int age;
private Address address;
// getter & setter
// ...
}
public final class Address {
private String street;
private String city;
private String zip;
// getter & setter
// ...
}
// generic collection
MongoCollection<Person> collection = database.getCollection("people", Person.class);
// insert pojo
Person ada = new Person("Ada Byron", 20, new Address("St James Square", "London", "W1"));
collection.insertOne(ada);
// query pojo
Person somebody = collection.find(eq("address.city", "Wimborne")).first();
System.out.println(somebody.getName());
Spring Data
SpringData MongoDB 文档传送门在此,内容不算特地多,对于尝鲜应用来说能够疏忽掉聚合、Reactive 等一些“高级”个性。作为 NoSQL 里最像 SQL 的一类数据库,SpringData 提供了 MongoTemplate 和 MongoRepository 两种应用形式。
MongoTemplate
与其余 SpringData 我的项目类似(例如 Redis 的 RedisTemplate、JDBC 的 JdbcTemplate)提供了 MongoTemplate 这个外围操作类,它基本上涵盖了所有对 MongoDB 的反对并且提供了丰盛的操作个性。
MongoTemplate 的构造函数有如下三个:
MongoTemplate(MongoClient mongo, String databaseName);
MongoTemplate(MongoDatabaseFactory mongoDbFactory);
MongoTemplate(MongoDatabaseFactory mongoDbFactory, MongoConverter mongoConverter);
接下来看一看 MongoTemplate 的根本应用:
// example pojo
@Document("person")
public class Person {
@MongoId
private String id;
@Field("personName")
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// getter & setter ...
// ...
}
// insert
Person p = new Person("Bob", 33);
mongoTemplate.insert(p);
// raw query
BasicQuery query = new BasicQuery("{ age : { $lt : 50}, accounts.balance : {$gt : 1000.00}}");
List<Person> result = mongoTemplate.find(query, Person.class);
// criteria query
List<Person> result = template.query(Person.class)
.matching(where("age").lt(50)
.and("accounts.balance").gt(1000.00d))
.all();
Person one = template.query(Person.class)
.matching(criteria.orOperator(where("name").ne("Jack"),
where("age").lt(10)))
.firstValue();
// update
template.update(RelationGroup.class)
.apply(new Update().pull("interviewees", "Jack")
.pull("interviewers", "Jack"))
.all();
// upsert
template.update(Person.class)
.matching(query(where("ssn").is(1111)
.and("firstName").is("Joe")
.and("Fraizer").is("Update"))
.apply(update("address", addr))
.upsert();
// delete
template.remove(person, "person");
template.remove(query(where("lastname").is("lannister")), Person.class);
template.findAllAndRemove(new Query().limit(3), "person");
Repository
相熟 Spring Data JPA 的敌人肯定对 Repository 接口不会生疏,同样的 Spring Data MongoDB 也提供了 Repository 接口的反对,可能通过办法名称主动生成对应的查问实现,如果在应用时偏差传统的 DAO 层解决逻辑或者某些查问反复使用率很高,那么选用 Repository 的形式会非常便捷。应用 Java Configuration 模式的状况下,开启 MongoDB Repository 须要应用注解@EnableMongoRepositories
:
@Configuration
@EnableMongoRepositories
class ApplicationConfig extends AbstractMongoClientConfiguration {
@Override
protected String getDatabaseName() {return "e-store";}
@Override
protected String getMappingBasePackage() {return "com.oreilly.springdata.mongodb";}
}
Repository 样例以及应用示例:
public interface PersonRepository extends PagingAndSortingRepository<Person, String> {
// parse query by method name
List<Person> findByLastname(String lastname);
// support pageable
Page<Person> findByFirstname(String firstname, Pageable pageable);
// nested pojo
Person findByShippingAddresses(Address address);
// find first
Person findFirstByLastname(String lastname)
// support stream
Stream<Person> findAllBy();
// delete
List <Person> deleteByLastname(String lastname);
}
@Service
public sampleService {
@Autowired
private PersonRepository repository;
public Page<Person> getPersonPage(int page, int pageSize) {return repository.findAll(PageRequest.of(page, pageSize));
}
public List<Person> findAllPerson() {return new ArrayList<>(repository.findAll());
}
}
应用感触
在进行利用之前也有查问过什么时候应该应用 MongoDB 这个问题,须要决策是否应该采纳 MongoDB 而不是 RDB 这一更加传统、稳当的抉择,然而查了半天也没查到比拟好的解释,反倒是看到 MongoDB 官网有提供企业级的咨询服务来帮忙客户进行利用。集体感觉暂且抛开性能方面的抉择之外,最次要的思考因素有如下几点:
- 数据是否品种繁多且关联度很高、关联关系简单,在应用时须要大量的相似 join 的联合操作。这是 RDB 的强项,如果有这个个性那么 Mongo 不会是第一抉择。
- 数据操作是否存在大量的事务性跨汇合(表)操作、批量操作。同样事务也是 RDB 的强项,MongoDB 只管也曾经开始反对事务,然而若事务操作是次要操作类型,应用 MongoDB 或者须要考量一番。
- 归为一类的数据(即一个汇合或一张表)其构造是否须要比拟高的灵活性(字段不固定、变动大),或者构造中蕴含可变长度的字段,或者蕴含嵌套形数据。显然灵便度是 MongoDB 的杀手锏,应用 RDB 的时候碰到此类数据比拟难解决,往往就是弄成 Json 或者自定义构造塞到某个字段上面,举个例子比方要存储一系列选项各不相同的考察问卷 RDB 就会比拟头疼。毫无疑问如果有很多此类数据那么 MongoDB 相对值得一试。
- 数据的量级是否会到千万或者亿级别以上,有高度伸缩性,且须要频繁查问。这一点其实有性能方面的考量,然而实质上来说还是要归功于 Mongo 原生的高度可扩展性和数据结构的适配。如果有这样的需要那么一般 RDB 基本上无奈间接满足,如果同时满足上述的一些条件,Mongo 会是很好的抉择。
只管有些方面 Mongo 不善于,然而不善于并不阐明 Mongo 做不到,俗话说 ” 天下大势,分久必合,合久必分 ”,在 DB 上也有几分意思在外面,Mongo 也在反对事务、可能应用 DBRef 来做援用、引入 schema 束缚等等,也有原生适配 SQL 且原生提供分片、正本的新型 RDB 呈现。
再具体到 Java 对 Mongo 的应用下面,能够看到 SpringData 对 MongoDB 的解决方案仍然是其对立的领域建模领域,简略来说就是映射为 Java Bean 或者说 POJO,但作为 Java Bean 就必然有字段、有类型,这其实是与 MongoDB 反对 schema-less 这一劣势个性是有肯定抵触的,不过话又说回来没有 schema、没有结构化、没有类型平安对于逻辑代码的编写并不是坏事,所以咱们能够看到包含 Mongo 在内的很多 NoSQL 当初也开始反对设置 schema。综合思考来说,集体感觉如下状况在 Java 后端利用 MongoDB 是比拟难受的:
- 间接存储文档数据,如文章或评论的内容、HTML 页面等,这些内容无需通过代码逻辑解决间接返回给前端或者用户,充分利用 MongoDB 的个性。
- 可能以 ” 半结构化 ” 的模式存储数据,在固定构造中内嵌灵便的子数据。具体到利用层面来说,依然保留 Java Bean 的映射关系,在 Bean 中应用包含 Map、List、数组等容器类型的字段或者间接应用
org.bson.Document
类型,此外应用形象基类定义字段、各类数据应用不同的子类成员也是十分适配的一种建模形式。这样既不失固定构造带来的标准与便当,同时也保留了数据的灵活性。
当然这些只是一些比拟通俗的应用感触,仅供参考~MongoDB 远不止于此,集体对于 Mongo 是比拟有好感的,用起来感觉不错,心愿能有机会多多应用,深刻开掘。