1. MongoDB简介
在本文中,我们提供了全面的MongoDB教程,该教程与基于Java的应用程序集成。 每当我们读到NoSQL数据库时,我们都需要知道为什么在SQL数据库运行良好时会开发它们。 NoSQL的基础是通过宽松的ACID (原子性,一致性,隔离性,持久性)保证而实现的,而其性能,可伸缩性,灵活性和降低的复杂性得到了支持。 大多数NoSQL数据库在提供尽可能多的前述质量方面都走了一条路,甚至为开发人员提供了可调节的保证。
MongoDB是一个开放源代码,面向文档的跨平台数据库,它使用C ++开发,并且是最流行和使用最广泛的NoSQL类型数据库之一。 它可在具有键-值对的类JSON文档的顶部运行,其键值对在每个文档中都无法定义。 同样,它是免费使用的,因为它是在GNU Affero通用公共许可证和Apache许可证的组合下发布的。 MongoDB将数据存储在类似JSON的文档(称为BSON)中,该文档在相同集合中的文档之间可以具有动态模式。 可以通过简单地添加新字段或删除现有字段来更改同一集合中文档的结构。
在这篇文章中,我们将研究此NoSQL数据库的一些特性以及它在各个版本中的演变方式,并添加具有改进的可伸缩性和性能的新功能。 我们还将开发一个基于Maven的小型Java项目,其中将使用MongoDB Java驱动程序运行一些示例查询。
2. MongoDB的发展
MongoDB由10gen开发,于2009年8月27日首次发布。MongoDB的第一个版本具有一些基本功能,授权和ACID保证,弥补了性能和灵活性方面的不足。 同样,v1.0和v1.2的一些基本功能是:
- 基于JSON的文档模型
- 全局锁定在流程级别
- 驻留在RAM上的集合的索引(主要)
- 对集合中的文档进行CRUD操作
- 主从架构中的复制支持
- Map-Reduce(在v1.2中受支持)
- Javascript函数(在v1.2中受支持)
在初始版本之后不久,MongoDB的下一版本带来了许多新功能,并且对索引集合进行了令人兴奋的改进,并且还基于先前的功能集。 为了列出演变过程,以下是MongoDB版本2中提供的一些功能:
- 分片(v1.6中支持)
- 查询运算符(在v1.6中受支持)
- 稀疏索引和覆盖索引(在v1.8中受支持)
- 更有效的内存使用
- 并发改进
- MapReduce改进
- 身份验证(在v2.0中支持分片)
- 地理空间查询和数据支持
现在,随着大多数组织的数据库中越来越多的数据的到来,需要提高性能,更快地建立索引和搜索文档。 所有这些需求在MongoDB的版本3中均得到了正确回答。 以下功能是主要功能的增强,这些功能使MongoDB成为最常用的NoSQL数据库之一:
- 聚合框架(在v2.2中受支持)
- 文字搜索(在v2.4中受支持)
- 哈希索引(在v2.4中受支持)
- 安全性增强,对数据库的基于角色的访问(在v2.4中受支持)
- V8 JavaScript引擎(从v2.4开始取代SpiderMonkey)
- 查询引擎的改进(自v2.6起)
- 文件验证(自v3.2起)
- 多个存储引擎(自v3.2起,仅企业版)
当我们开始注意到MongoDB自开始发展以来就一直添加的功能时,我们可以很容易地看到MongoDB是一个数据库,可以处理从启动MVP和POC到具有数百台服务器的企业应用程序的数据负载和更多。
3.适用于SQL开发人员的MongoDB术语
如果在SQL术语和NoSQL概念之间建立比较,我们可以更快地了解MongoDB概念。 这是Mongo与传统MySQL系统之间的简单类比比较:
- MySQL中的表成为MongoDB中的集合
- 行成为一个文件
- 列成为字段
- 联接被定义为链接和嵌入式文档(稍后会对此进行更多介绍)
为了消除任何误解,这只是查看MongoDB概念的一种简单方法,虽然每个概念可能都不能严格应用于其MongoDB对应对象,但这仍然很重要。
4.使用Maven制作Java项目
我们将使用许多Maven原型之一为我们的示例创建一个示例项目。 要创建项目,请在将用作工作空间的目录中执行以下命令:
创建一个项目
mvn archetype:generate -DgroupId=com.javacodegeeks.example -DartifactId=JCG-JavaMongoDB-Example -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
如果您是第一次运行maven,则完成生成命令将花费几秒钟,因为maven必须下载所有必需的插件和工件才能完成生成任务。 运行该项目后,我们将看到以下输出并创建该项目:
5.添加Maven依赖项
创建项目后,请随时在您喜欢的IDE中打开它。 下一步是向项目添加适当的Maven依赖关系。 我们将在项目中使用以下依赖项:
-
mongo-java-driver
:此依赖关系将Java的MongoDB驱动程序引入了我们的依赖关系。
这是pom.xml
文件,其中添加了适当的依赖项:
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.javacodegeeks.example</groupId><artifactId>JCG-JavaMongoDB-Example</artifactId><packaging>jar</packaging><version>1.0-SNAPSHOT</version><name>JCG-JavaMongoDB-Example</name><url>http://maven.apache.org</url><dependencies><dependency><groupId>org.mongodb</groupId><artifactId>mongo-java-driver</artifactId><version>3.4.1</version></dependency></dependencies></project>
如果要检查并使用较新版本的驱动程序,请在此处检查发行版本。
最后,要了解添加此依赖项时添加到项目中的所有JAR,我们可以运行一个简单的Maven命令,当我们向其添加一些依赖项时,该命令使我们能够查看项目的完整依赖关系树。 当我们以适当的层次结构方式添加一些自己的依赖项时,此依赖关系树还将显示添加了多少个依赖项。 这是我们可以使用的相同命令:
检查依赖树
mvn dependency:tree
当我们运行此命令时,它将向我们显示以下依赖关系树:
显然,这是一个非常简单的项目,因为它仅添加了一个Maven依赖项。
6.项目结构
在继续进行并开始为该项目编写代码之前,让我们介绍一下一旦完成将所有代码添加到项目中之后将拥有的项目结构,以便我们知道将在该项目中放置类的位置:
我们在此应用程序中使用了单个类,因为它可以满足我们的所有目的。 我们可以根据需要执行的操作以及项目扩展到更复杂的操作时将该类分为多个类。
7.在Java中使用MongoDB
现在,我们可以在刚刚完成的Java项目中开始使用MongoDB查询。 我们将从基本的CRUD操作开始,然后是分页实现和更多查询。 在继续执行代码示例之前,请确保在您的计算机上安装并运行MongoDB 。
7.1与MongoDB建立连接
在任何应用程序中使用数据库的第一步是连接到数据库。 我们可以通过以下方式通过对authentication-database
(默认为admin
)进行身份验证来连接到MongoDB:
MongoApp.java
System.out.println("Connecting to DB...");
List<MongoCredential> credentialsList = new ArrayList<>();
//Use username, authtication database and password in MongoCredential object
MongoCredential creds = MongoCredential.createCredential("db_user", "admin", "db_password".toCharArray());
credentialsList.add(creds);
ServerAddress serverAddress = new ServerAddress("localhost", 27017); //host and port
MongoClient mongoClient = new MongoClient(serverAddress, credentialsList);
System.out.println("Connected to MongoDB...");
当我们连接到经过身份验证的数据库时,可以使用上面的代码,该数据库是安全的并且已启用身份验证。 如果尚未保护MongoDB安装,则可以简单地使用:
MongoApp.java
MongoClient mongoClient = new MongoClient("localhost", 27017);
现在,我们准备使用此MongoClient
对象运行各种命令,该对象已在其中建立并存储了数据库连接。
7.2显示现有数据库
我们将首先显示系统中存在的所有数据库,当前用户可以访问这些数据库(如果安装通过身份验证进行保护)。 要在Mongo Shell中执行此操作,我们可以运行以下简单命令:
使用Mongo Shell显示数据库
show databases;
下列Java代码段也可以执行相同的操作:
使用Java显示数据库
// print existing databases
mongoClient.getDatabaseNames().forEach(System.out::println);
这将显示系统中的现有数据库:
输出量
admin
local
在上面的输出中, local
是默认的Mongo数据库。
7.3创建一个集合
我们可以通过以下Java代码片段在数据库中创建一个集合:
MongoApp.java
// get database
MongoDatabase jcgDatabase = mongoClient.getDatabase("JavaCodeGeeks");// create collection
jcgDatabase.createCollection("authors");
jcgDatabase.createCollection("posts");
请注意, 我们从未创建过提到的数据库 。 尽管如此,MongoDB 不会抛出任何错误。 它很乐观,并且了解如果没有数据库,它将为您创建一个数据库。 如果再次运行show database命令,这次我们将看到不同的输出:
资料库
JavaCodeGeeks
admin
local
数据库是自动创建的。
7.4保存–插入收藏夹
在MongoDB中,插入的工作方式与其他数据库略有不同。 插入时,如果数据库中存在一个ID,它将更新同一文档,否则,将执行插入操作。 如果我们尝试保存数据库中不存在的新作者,则会发生插入:
插入
MongoCollection<Document> authorCollection = jcgDatabase.getCollection("authors");
Document document = new Document();
document.put("name", "Shubham");
document.put("company", "JCG");
document.put("post_count", 20);
authorCollection.insertOne(document);
System.out.println("Inserted document = " + document);
当运行此代码片段时,我们将看到一个_id
自动分配给该对象,以后我们可以在代码中更新同一文档:
输出量
Inserted document = Document{{name=Shubham, company=JCG, post_count=20, _id=5b77c15cf0406c64b6c9dae4}}
7.5保存–更新现有文档
现在,我们已经在MongoDB的数据库中的现有集合中插入了文档,我们也可以更新它,因为我们知道它的ID。 我们将使用以下代码段执行更新操作,并更新作者的姓名:
更新文件
//Find existing document
Document updateQuery = new Document();
updateQuery.put("name", "Shubham");//Field to update
Document newNameDocument = new Document();
newNameDocument.put("name", "Shubham Aggarwal");//Perform set operation
Document updateObject = new Document();
updateObject.put("$set", newNameDocument);UpdateResult updateResult = authorCollection.updateOne(updateQuery, updateObject);
System.out.println("Documents updated: " + updateResult.getModifiedCount());
运行此代码段时,我们将看到至少一个文档已在数据库中正确修改:
输出量
Documents updated: 1
正如我们注意到的那样,在此提供的代码段中,保存遵循更新约定,因为我们使用了具有给定_id
的对象。
7.6获取集合中的所有文档
很容易找到集合中存在的所有文档,我们只需要为其提供一个查询对象,就可以在从集合中获取文档之前对其进行过滤。 让我们看看如何使用Java代码片段来完成此任务:
获取所有文件
Document searchQuery = new Document();
searchQuery.put("company", "JCG");FindIterable<Document> documents = authorCollection.find(searchQuery);for (Document document: documents) {System.out.println(document);
}
运行上面的代码后,我们将获得以下输出:
输出量
Document{{_id=5b77c15cf0406c64b6c9dae4, name=Shubham Aggarwal, company=JCG, post_count=20}}
可以对FindIterable
对象执行的操作还有很多,其中一些将在以后的部分中介绍。
7.7删除文件
删除文档很容易。 我们只需要提供过滤器查询即可完成工作。
pom.xml
Document deleteSearchQuery = new Document();
deleteSearchQuery.put("_id", new ObjectId("5b77c15cf0406c64b6c9dae4"));
DeleteResult deleteResult = authorCollection.deleteOne(deleteSearchQuery);
System.out.println("Documents updated: " + deleteResult.getDeletedCount());
运行上面的代码后,我们将获得以下输出:
输出量
Documents updated: 1
请注意,我们将ID的值不是用作String
而是用作ObjectId
因为MongoDB可以理解。 尽管完全可以使用简单的String作为ID,但是标识字段不支持其他类型。 最后,请注意,我们使用了deleteOne
方法,该方法仅删除单个文档。 即使MongoDB为提供的过滤器找到了多个文档(在ID的情况下,找不到多个文档,但在其他情况下可能找到多个文档),它仍将仅从其中删除一个文档(删除了第一个匹配的文档)。 要删除所有过滤的文档,我们可以使用deleteMany
方法。
8.解码的_id字段
每个MongoDB文档都有一个_id
字段,它是集合中给定文档的唯一标识符。 看起来像:
_ID
{"_id" : ObjectId("5b77c15cf0406c64b6c9dae4")... other fields ...
}
很多时候,人们(绝对不是开发人员)认为这是随机生成的String令牌,它是经过精心选择的,因此在此collection中保存的所有数字文档中都保持唯一。 但这种情况并非如此。 MongoDB中所有文档的每个_id是一个12字节的十六进制数字,可确保文档在集合中的唯一性。 如果开发人员自己未提供,则由MongoDB生成(在这种情况下,该String的含义将完全改变,但仍保留在集合中的唯一标识符)。
在这12个字节中,前4个字节描述了当前时间戳,接下来的3个字节描述了机器标识符,接下来的2个字节描述了该机器上MongoDB服务器的进程ID,最后3个字节是简单的自动递增计数器由MongoDB服务器维护。 这也可以用一个简单的图来解释:
此字段还表示MongoDB集合中每个文档的主键。
9. MongoDB的局限性
尽管MongoDB可以扩展很多并且具有超快速的索引,但是它也有一些可能的缺点。 我们使用“可能”一词,因为这可能会对您的用例造成不利影响,也可能不会造成不利影响。 让我们在这里看到一些缺点:
- 每个文件可以占用的最大大小为16MB。
- MongoDB文档中的最大文档嵌套级别为100
- 数据库名称限制为64个字符
- 如果我们在任何字段上应用索引,则该字段值不能包含超过1024个字节
- 哈希索引不能唯一
- 如果数据超过300 MB,则无法自动回滚。 在这种情况下,需要人工干预
正如我们所提到的,这些是一些缺点,这些缺点在您的应用程序中可能永远不会发生,并且您无需为此做任何事情。
10. MongoDB和RDBMS
MongoDB和RDBMS数据库之战永无止境。 开发人员一直在争论哪一个比另一个要快或更好,但是答案是,由于使用每种情况的用例,两者之间没有比较。 值得一提的是,让我们在这里描述MongoDB相对于RDBS数据库的优势:
- MongoDB数据库中的集合是无模式的 。 插入集合中的文档可以具有不同的字段集,而不必在应用程序级别或数据库级别执行任何其他操作。
- MongoDB具有丰富的查询支持。 MongoDB支持对数据库的动态查询。
- 数据库对象和应用程序对象之间的转换或映射很简单,因为大多数应用程序都支持使用数据库对象进行JSON映射。
- 集成的内存支持使用户可以以更快的方式访问数据。
MongoDB并不是神奇的更快。 如果您以相同的方式存储相同的数据,并以完全相同的方式对其进行访问,那么您真的不应该期望结果会大相径庭。 毕竟,MySQL和MongoDB都是GPL,因此,如果Mongo中包含一些神奇的更好的IO代码,则MySQL团队可以将其合并到他们的代码库中。
人们看到了真实的MongoDB性能,主要是因为MongoDB允许您以对工作负载更敏感的另一种方式进行查询。 例如,考虑一种设计,该设计以规范化方式保留了有关复杂实体的许多信息。 这可以轻松地使用MySQL(或任何关系数据库)中的数十个表以正常形式存储数据,并需要许多索引来确保表之间的关系完整性。
现在考虑与文档存储相同的设计。 如果所有这些相关表都从属于主表(并且经常属于主表),那么您也许可以对数据建模,以便将整个实体存储在单个文档中。 在MongoDB中,我们可以将其作为单个文档存储在单个集合中。 这是MongoDB开始提供卓越性能的地方。
11.结论
在本课程中,我们Swift熟悉了在基于maven的简单Java应用程序中使用MongoDB的过程,并使用更新的API对我们的集合执行了CRUD操作。
我们还研究了MongoDB的各种优势。 尽管您可能希望在许多用例上通过SQL数据库使用MongoDB,但我认为Schema灵活性最有可能是影响此决策的最重要因素 。 MongoDB使我们能够将数据或文档存储在可以具有不同字段的集合中,这可以在应用程序的开发阶段提供很大帮助,而且还可以从可能具有或不具有相同属性和架构的多个源中提取数据与他们相关联。 与必须预先定义列并且可以惩罚具有稀疏数据的RDBMS数据库相比,在MongoDB中,这是一种规范,并且是大多数用例共享的功能。 能够将属性深层嵌套到文档中,将值的数组添加到属性中,并且始终能够搜索和索引这些字段,这有助于应用程序开发人员利用MongoDB的无模式本质。
最后,扩展和分片是MongoDB用例的最常见模式。 使用内置分片和副本集轻松进行数据复制,以及从读取负载中卸载主要服务器,可以帮助开发人员有效地存储数据。 除了MongoDB提供的优势之外,MongoDB仍然有一些批评者提到规模并不是我们在每个应用程序中都需要的东西,因此消除了偏爱MongoDB的最重要因素之一。
12.下载源代码
这是带有Java编程语言的MongoDB教程。
您可以在此处下载此示例的完整源代码: MongoDB示例
翻译自: https://www.javacodegeeks.com/2018/08/mongodb-tutorial-for-beginners.html