加载中

Java 8's more functional tools give you more than what you need to connect to MongoDB, query the data, and even transform the results.

Welcome to my new tutorial series, Learn MongoDB with Java 8. I hope that you have a basic idea of Big Data and the new features offered in Java 8, as we use those features extensively in this tutorial.

In this article, we will learn:

  1. What MongoDB is.

  2. How to set up MongoDB.

  3. How to create a collection in MongoDB.

  4. How to insert documents in collections.

  5. How to write a simple Java program to connect to MongoDB and search a value from the Mongo collection.

Here are the short and crisp details about MongoDB.

Java 8 提供了很多工具,能让你连接 MongoDB,查询数据,甚至转换结果。

欢迎来到新的系列教程:使用 Java 8 学习 MongoDB。通过这份教程,希望大家对 Java 8 提供的大数据和新特性有基本的概念,同时我们会在本教程广泛使用这些新特性。

在这篇文章里面,我们将会学习:

  1. 什么是 MongoDB

  2. 如何安装 MongoDB

  3. 如何在 MongoDB 中创建一个集合

  4. 如何在集合中插入文档

  5. 如何编写一个简单的 Java 程序连接 MongoDB,并从 Mongo 集合中查找某一个值

下面是一些关于 MongoDB 的简单介绍。

MongoDB

MongoDB is a NoSQL database where we can store data in BSON format where a key represents the property and the value represents the property value stored against a key.

Documents

In Mongo, the document represents a data structure that can hold any number of key and value pairs. "Employee" can be represented as a document where the name, address, age, and its value are stored as key-value pairs in that document. Please note that documents are stored in binary JSON format, called BSON (Binary JSON).

An example of a document is:

{ 
    "_id" : { 
        "$oid" : "58eb8c2b1de2b36bfcc74326"
    } , 
    "name" : “Shamik Mitra”
}

MongoDB

MongoDB 是一种 NoSQL 类型的数据库。数据按照 BSON 格式进行存储,所有的数据按照键/值对(key/value)的方式进行存储。其中 key 表示属性,value 表示相对于 key 存储的属性值。

Documents

在 Mongo 里面,文档代表着一种能存储任意数量的键值对的数据结构。比如 “员工” 数据可以表示为一个文档,其中 name,address,age 和它们的值以键值对(key-value)的方式存储在该文档中。要注意的是,文档存储为一种二进制 JSON 格式,称为 BSON(Binary JSON)。

下面是一个文档的例子:

{ 
    "_id": { 
        "$oid" : "58eb8c2b1de2b36bfcc74326"
    }, 
    "name": "Shamik Mitra"
}

Collections

In Mongo, often, documents with the same structure are put into a bucket, called a collection. You can think of a collection as a table in a relational database, where every row represents a document. So we can say the Employee collection holds multiple Employee documents. Note that this is logical, By definition, a collection can contain any type of documents — for example, a collection can contain Employee documents as well as Car documents. There are no restrictions.

Note: While designing, it is preferable to create a collection basis on similarly structured documents.

集合

在 Mongo 中,经常把具有相同数据结构的文档放入一个容器,并将其称为集合。可以把集合视为关系数据库中的表,每一行数据其实就对应着一个文档。所以我们可以认为 Employee 集合包含了多个 Employee 文档。不过要注意的是,这只是逻辑上面的一种理解。按照定义,一个集合可包含任意类型的文档 — 例如,一个集合能包含 Employee 文档以及 Car 文档。这些数据结构是没有约束的。

注意:在设计数据结构的时候,创建的集合最好与文档有着相似的结构。

No Schema

This is one of the key differences between a SQL and NoSQL database, although personally, I don’t like those terms. I believe it would be better to say relational and non-relational database. By NoSQL, we mean it has no predefined schema — it can hold anything in BSON format. To be specific, any data structure fits into a no-schema database, so it is suitable for storing unstructured data.

That makes life easier for the developer because, in a relational database, there is a fixed schema. If an Employee table contains name, age, and address columns it stores all data which maintains the same data structure. Now, if we want to modify the structure, say we want to an add a gender property, then we need to change the schema altogether to incorporate the new property. But in Mongo, as it is schema free, we can able to put any data structure with any combination of properties.

Note:Although MongoDB is schema free, while designing, we logically put the same structured documents into a collection, so it's maintaining an implicit schema!

无模式(No Schema)

这是 SQL 和 NoSQL 数据库之间的主要区别之一。对我而言,我不喜欢这些术语。我更倾向于使用关系数据库和非关系数据库来描述他们。使用 NoSQL,不需要任何预定义的模式 — 它能包含任何 BSON 格式的数据。具体来说,任何数据结构都适用于无模式数据库,因此它适用于存储非结构化的数据。

这使得开发者能更容易的进行开发,因为在关系型数据库中, 所有的数据都有一个固定的模式。比如一张员工表中有 name, age 和 address 字段,它存储的数据都保持着相同的数据结构。如果现在需要修改结构,比方说我们想要添加一个 gender 属性,则必须要改变表的模式以纳入新的属性。但是在 Mongo 中,因为它是模式自由的,所以这些都不需要,我们可将任何数据结构和任意属性的组合放在一起,可存储任意格式的数据。

注意:虽然 MongoDB 是模式自由的,但在设计的过程中,逻辑上我们把相同结构的文档放到一个集合中,所以会存在一个隐形的模式。

Horizontal Scaling

The success of Big Data lies in horizontal scaling. As Mongo is a part of the Big Data stack, it also supports the same. Through horizontal scaling, MongoDB can distribute data to multiple nodes, each node representing a commodity machine — i.e a low-cost computer — and we can add and remove nodes with ease. So when we need to store more data, we can add new nodes without impacting existing architecture, wherein with vertical scaling (which RDBMS follows), we need a supercomputer, and data resides in a single centralized storage space.

Note: By distributing data over multiple nodes, Mongo makes itself a fault tolerant system. If one node goes down, we can use others to fetch data. Of course, with vertical scaling, as data resides in one centralized area, if that goes down, we lose all data, so it is considered to be a single point of failure.

横向扩展

大数据的成功依赖于它的横向扩展能力。Mongo 作为大数据技术的一部分,当然也是支持的。通过横向扩展,MongoDB 能够将数据分发到多个节点上,每个节点都可以是低配的电脑,我们也可以轻松地添加和删除这些节点。所以当我们需要存储更多的数据时,可以添加新的节点而不影响现有的架构,然而在纵向扩展中(例如 RDBMS 使用的方式),我们则需要一台超级计算机,并且数据采用中央化存储的方式。

注意:在向多节点分发数据的时候,Mongo 有一定的容错机制。如果一个节点挂掉了之后,我们会使用其他的节点获取数据。当然,如果使用纵向扩展,因为数据是中央化存储,如果出现故障的话,我们会丢失所有的数据,这将会导致单点故障。

Sharding

Sharding is a technique by which MongoDB breaks gigantic clumps of data to small chunks. Afterward, it creates replicas of each chunk, then distributes those chunks into multiple nodes. When queried, the server holds metadata information that tells the Mongo server which node has the data. The query then fetches data only from that node.

Mongo Setup

  1. Download the latest Mongo ZIP distribution from this link.

  2. Create a directory D:\InstalledApps and extract the ZIP in the same location.

  3. Rename the distribution folder to mongodb.

  4. Now create a folder named data inside the mongodb folder: D:\InstalledApps\mongodb\data

  5. Now open a command prompt and go to D:\InstalledApps\mongodb\bin using the following command: cd D:\InstalledApps\mongodb\bin

  6. Start MongoDB server using: mongod.exe --dbpath D:\InstalledApps\mongodb\data

It will start the server in localhost:27017 .

分片(Sharding)

分片是 MongoDB 将庞大的数据块拆分成小的数据块的技术,之后,它会为每个块创建一份副本,然后这些块会被分发到多个节点中。当查询时,服务器通过元数据查询到数据所在的节点,然后从该节点返回查询结果。

Mongo 安装步骤

  1. 下载最新的 Mongo ZIP 包(下载地址

  2. 创建目录 D:\InstalledApps,解压 ZIP 的文件到这个目录

  3. 将解压的文件夹重命名为 mongodb

  4. 在 mongodb 文件夹下新建 data 文件夹:D:\InstalledApps\mongodb\data

  5. 打开 cmd(命令行),输入以下命令:cd D:\InstalledApps\mongodb\bin 进入 D:\InstalledApps\mongodb\bin

  6. 通过命令启动 MongoDB Server:mongod.exe --dbpath D:\InstalledApps\mongodb\data

MongoDB Server 启动在 localhost:27017

Set Up Mongo Client

We will use RoboMongo as our mongo client. It has a nice looking GUI, so we can easily create a collection, then add documents using RoboMongo GUI.

Download RoboMongo from here. Extract the ZIP file and click on RoboMongo.exe. It will launch the RoboMongo GUI.

Connect to the Mongo server using the following credentials:

  • Host: localhost and port: 27017 .

  • Create a new database: Now in RoboMongo GUI, right-click on the computer icon in the right-hand panel to Create a Database. Use test as the name of the database.

  • Create a collection: Now right-click on the collection icon in the right-hand panel to create a collection. Create a new collection named Employee.

  • Insert a document: Now right-click on the Employee collection and hit Insert Document, then paste the following in text area

{ "name" : "Shamik" , "address" : "1 Nivedita Lane" , "age" : 32}

Hit save. That will insert a document into the Mongo Server.

The same thing can be done through the Mongo console.

安装 Mongo 客户端

我们将会使用 RoboMongo 作为 mongo 客户端。它有漂亮的图形化界面,所以我们可以轻松创建集合,并通过使用 RoboMongo GUI 添加文档。

RoboMongo 下载地址(戳这里),解压 ZIP 文件后,双击 RoboMongo.exe 运行程序。它将启动 RoboMongo GUI

通过下面的步骤连接到 Mongo Server:

  • Host: localhost 和 port: 27017。

  • 创建数据库:在 RoboMongo 图形化界面中,右键点击右侧面板的计算机图标以创建一个数据库。使用 test 作为数据库的名字。

  • 创建集合:右键点击右侧面板的集合图标。创建一个新的集合,命名为 Employee

  • 插入一个文档:右键单击 Employee 集合,然后点击 Insert Document,在文本区域粘贴以下内容:

{ "name" : "Shamik" , "address" : "1 Nivedita Lane" , "age" : 32}

点击 save,这将在 Mongo Server 中插入文档。

这些操作其实也可以通过 Mongo 的控制台来完成。

Java Time

Now we are all set to write our first Java program to connect to MongoDB. We'll use:

  1. Eclipse Neon

  2. Eclipse maven plugin.

  3. Java 8

Step 1

Now we will create a maven project in Eclipse called mongoExample. Set the Java compiler version as Java 1.8. Please download Java 1.8 if you do not have Java 8.

Step 2

Write a pom.xml like the following:

<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/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>mongoExample</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>org.mongodb</groupId>
            <artifactId>mongo-java-driver</artifactId>
            <version>2.10.1</version>
        </dependency>
    </dependencies>
</project>

Here we use the mongo-java-driver JAR to connect to the Mongo Server using Java 8.

编写 Java 代码

上述的步骤完成后,就该写 Java 代码连接 MongoDB 数据库了。要用到的工具有:

  1. Eclipse Neon

  2. 适用于 Eclipse 的 Maven 插件

  3. Java 8

第一步

首先,在 Eclipse 里创建一个名为 mongoExample 的 Maven 项目,并将 Java 编译器版本设置为 1.8。没有的话请去网上下载。

第二步

编写一个 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/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>mongoExample</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>org.mongodb</groupId>
            <artifactId>mongo-java-driver</artifactId>
            <version>2.10.1</version>
        </dependency>
    </dependencies>
</project>

可以看到我们用了 mongo-java-driver 这个库配合 Java 8 来连接 Mongo Server。

Create a MongoContext

The next part is to connect to the Mongo Server using Java code. We will create a top-level API class that will abstract the logic of connecting to the MongoDB Server and expose some utility methods for our API clients:

/**
*
*/
package com.example.config;
import java.net.UnknownHostException;
import java.util.function.Function;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.MongoClient;
/**
* @author Shamik Mitra
*
*/
public class MongoContext {
    private static MongoContext ctx = new MongoContext();
    private  MongoClient client;
    private  DB db;
    private MongoContext(){
        try{
            init();
        }catch(Exception ex){
            ex.printStackTrace();
        }
    }
    private void init() throws UnknownHostException{
        this.client = new MongoClient("localhost" , 27017);
    }
    public static MongoContext get(){
        return ctx;
    }
    public MongoContext connectDb(String dbname){
        if(db !=null){
            throw new RuntimeException("Already conected to " + db.getName() + "can't connect " + dbname);
        }
        this.db = client.getDB(dbname);
        System.out.println("DB Details :: " + db.getName());
        return ctx;
    }
    public <T,X> DBCursor findByKey(String collectionName,String key,T value,Function<T,X> convertDataType){
        DBCollection collection = db.getCollection(collectionName);
        BasicDBObject searchQuery = new BasicDBObject();
        searchQuery.put(key, convertDataType.apply(value));
        System.out.println("search Query ::" + searchQuery);
        DBCursor cursor = collection.find(searchQuery);
        return cursor;
    }
}

Code Explanation

Here, I created a Singleton MongoContext Object, which connects to MongoDB using the MongoClient class in the init method. Note that the MongoClient class is a part of the Mongo Java driver JAR: this.client = new MongoClient("localhost" , 27017);

Now, we return that context with the static get() method.

At this point, we've successfully established a connection with the Mongo server.

The next thing to do is choose a database to connect to. I have created a generic method called connectDb(String dbName), where the caller of our API will pass the database name it wants to connect. Please note that the return type of this method is MongoContext itself. I'm using the Fluent API technique.

创建 MongoContext 类

接下来,我们使用 Java 代码连接 Mongo Server。我们先写一个顶级 API 类,把连接 MongoDB Server 的操作抽象出来,并给调用者提供一些辅助方法:

/**
*
*/
package com.example.config;
import java.net.UnknownHostException;
import java.util.function.Function;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.MongoClient;
/**
* @author Shamik Mitra
*
*/
public class MongoContext {
    private static MongoContext ctx = new MongoContext();
    private  MongoClient client;
    private  DB db;
    private MongoContext(){
        try{
            init();
        }catch(Exception ex){
            ex.printStackTrace();
        }
    }
    private void init() throws UnknownHostException{
        this.client = new MongoClient("localhost" , 27017);
    }
    public static MongoContext get(){
        return ctx;
    }
    public MongoContext connectDb(String dbname){
        if(db !=null){
            throw new RuntimeException("Already conected to " + db.getName() + "can't connect " + dbname);
        }
        this.db = client.getDB(dbname);
        System.out.println("DB Details :: " + db.getName());
        return ctx;
    }
    public <T,X> DBCursor findByKey(String collectionName,String key,T value,Function<T,X> convertDataType){
        DBCollection collection = db.getCollection(collectionName);
        BasicDBObject searchQuery = new BasicDBObject();
        searchQuery.put(key, convertDataType.apply(value));
        System.out.println("search Query ::" + searchQuery);
        DBCursor cursor = collection.find(searchQuery);
        return cursor;
    }
}

代码说明

在代码中,我创建了一个单例对象 MongoContext。它的 init 方法会创建一个 MongoClient 类来连接 MongoDB。注意 MongoClient 类来自 Mongo Java Driver 这个库: this.client = new MongoClient("localhost" , 27017);

然后,我定义了静态的 get() 方法来返回类内部维护的 MongoContext 单例对象。

至此,成功建立了到 Mongo server 的连接。

下一步,指定需连接的数据库。我写了个泛型方法 connectDb(String dbName),调用它就可以指定要连接到的数据库。注意这个方法返回的类型是类的单例对象 MongoContext 本身。这里我用了链式代码的风格(Fluent API)来写。

After that, we want to do a query to fetch documents from the database.

So I create a Generic method:

public <T,X> DBCursor findByKey(String collectionName,String key,T value,Function<T,X> convertDataType)

This method takes four parameters:

  • collectionName: It takes the target collection name from the caller. Here, Employee is the target collection. Again, think of it as a table name in RDBMS.

  • Key: Key is the property by which caller wants to search the collection. You can think it is the column name, which we would put into a SQL where clause.

  • Value: Value represents the value caller searched for. It is same as the value provided in a SQL where clause.

  • Function<T, X>: Here, I define a Functional Interface (Java 8), which takes one type of data and transforms it to another datatype. ( T -> X). I need this for when I try to write a generic method findbyKey.

At the moment, I do not know what key a client will search for. For example, in the Employee collection, the name key is a String, but the age key is an Integer. So, if a caller wants to search by the name key, the desired datatype must be a String. An age's datatype will be an Integer. So, I need some strategy that transforms a value to its desired datatype. We'll use lambda expressions for that. Take a look at this post for a detailed understanding of lambda expressions.

In the method body, I got the name of the collection, then created a SearchQuery using BasicDataObject. After that, I put the key and value, passed by the caller, into that query. At last, we pass the above query to the collection, get the result, and wrap in a DBCursor Object, which internally holds the result.

下一步,从数据库中查询到我们需要的文档。

为此,我写了个泛型方法

public <T,X> DBCursor findByKey(String collectionName,String key,T value,Function<T,X> convertDataType)

方法接受四个参数:

collectionName:代表要查询的集合名称,比如现在我们要查询的就是 Employee 集合。记住,可把集合看作关系型数据库里的表。

key:代表要从集合里查询的键,相当于在 SQL where 查询子句里指定的字段名。

value:代表键所对应的值,同样相当于 where 里给的值。

Function<T,X>:这里运用了 Java 8 的函数接口,可以把数据从一个类型 T 转换成另一个类型 X( T -> X)。我们的这个泛型方法 findbyKey 需要用到这东西。

然而,现在的情况是,我们并不知道调用者到底想查询什么键,于是就会比较麻烦。例如说,在 Employee 集合里,员工姓名这个键是字符串类型,而年龄是整型。如果调用者想以姓名来查询,那对应的数据类型就得是字符串,同理查询年龄就得用整型。因此,我们得用一种特殊方法,能把提供的值转换成对应的类型。现在呢,我们用 Lambda 表达式来实现我们的需求。可以看一下这篇文章,了解到底什么是 Lambda 表达式。

在方法内,我们得到了要查询的集合名称,然后创建了 BasicDataObject 类的一个对象 searchQuery。随后,把参数里的键和值提供给对象来提供查询信息。最后,我们执行查询,并把得到的查询结果放进一个 DBCursor 对象里以进行维护。

返回顶部
顶部