皇上,还记得我吗?我就是1999年那个Linux伊甸园啊-----24小时滚动更新开源资讯,全年无休!

使用 Java 8 学习 MongoDB(Part 1)

Java 8 提供了很多工具,能让你连接 MongoDB,查询数据,甚至转换结果。 欢迎来到新的系列教程:使用 Java 8 学习 MongoDB。通过这份教程,希望大家对 Java 8 提供的大数据和新特性有基本的概念,同时我们会在本教程广泛使用这些新特性。 在这篇文章里面,我们将会学习:

  1. 什么是 MongoDB
  2. 如何安装 MongoDB
  3. 如何在 MongoDB 中创建一个集合
  4. 如何在集合中插入文档
  5. 如何编写一个简单的 Java 程序连接 MongoDB,并从 Mongo 集合中查找某一个值

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

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"
}

集合

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

无模式(No Schema)

这是 SQL 和 NoSQL 数据库之间的主要区别之一。对我而言,我不喜欢这些术语。我更倾向于使用关系数据库和非关系数据库来描述他们。使用 NoSQL,不需要任何预定义的模式 — 它能包含任何 BSON 格式的数据。具体来说,任何数据结构都适用于无模式数据库,因此它适用于存储非结构化的数据。 这使得开发者能更容易的进行开发,因为在关系型数据库中, 所有的数据都有一个固定的模式。比如一张员工表中有 name, age 和 address 字段,它存储的数据都保持着相同的数据结构。如果现在需要修改结构,比方说我们想要添加一个 gender 属性,则必须要改变表的模式以纳入新的属性。但是在 Mongo 中,因为它是模式自由的,所以这些都不需要,我们可将任何数据结构和任意属性的组合放在一起,可存储任意格式的数据。 注意:虽然 MongoDB 是模式自由的,但在设计的过程中,逻辑上我们把相同结构的文档放到一个集合中,所以会存在一个隐形的模式。

横向扩展

大数据的成功依赖于它的横向扩展能力。Mongo 作为大数据技术的一部分,当然也是支持的。通过横向扩展,MongoDB 能够将数据分发到多个节点上,每个节点都可以是低配的电脑,我们也可以轻松地添加和删除这些节点。所以当我们需要存储更多的数据时,可以添加新的节点而不影响现有的架构,然而在纵向扩展中(例如 RDBMS 使用的方式),我们则需要一台超级计算机,并且数据采用中央化存储的方式。 注意:在向多节点分发数据的时候,Mongo 有一定的容错机制。如果一个节点挂掉了之后,我们会使用其他的节点获取数据。当然,如果使用纵向扩展,因为数据是中央化存储,如果出现故障的话,我们会丢失所有的数据,这将会导致单点故障。

分片(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

安装 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 8 学习 MongoDB(Part 1)

编写 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。

创建 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)来写。

下一步,从数据库中查询到我们需要的文档。 为此,我写了个泛型方法

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 对象里以进行维护。

代码测试

新建一个 Main.java 类:

/**
*
*/
package com.example.mongotest;
import com.example.config.MongoContext;
import com.mongodb.DBCursor;
/**
* @author Shamik Mitra
*
*/
public class Main {
    /**
    *
    */
    public static void main(String[] args) {
        DBCursor result = MongoContext.get().connectDb("test").findByKey("Employee", "age", 32,
                (value) -> new Integer(value));
        while (result.hasNext()) {
            System.out.println(result.next());
        }
    }
}

在代码中,我创建了个 MongoContext 对象,连接到测试用的服务器。随后把要查询的集合名 Employee、键名 age 以及 32 传递给 findByKey 方法调用。我还向方法提供了一条 Lambda 表达式,代表键 age 的对应数据类型是整型。 注意在 findByKey 的方法定义里,我用泛型参数 T 来代表的数据类型。 那么,为什么方法要用函数接口?其实我可以让调用者直接给我提供类型 T 的信息来表示值的类型,但是,这样做有个好处,调用者可以在进行查询前,在函数接口里验证或检查用户所提供的查询数据是否合法、被修改之类。 代码执行结果:

DB Details :: test
search Query ::{ "age" : 32}
{ "_id" : { "$oid" : "58ecde108b308657b44937b1"} , "name" : "Shamik" , "adress" : "1 Nivedita Lane" , "age" : 32}

英文原文:Learn MongoDB With Java 8 (Part 1)

参与翻译 (2人) : Quran, AzureSora

转自 https://www.oschina.net/translate/learn-mongo-with-java8-part-1?lang=chs&page=2#