使用 Java 8 学习 MongoDB(Part 1)

来源:互联网 发布:杭州男装批发市场知乎 编辑:程序博客网 时间:2024/06/06 03:29

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 代码

上述的步骤完成后,就该写 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;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 表达式来实现我们的需求。

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

代码测试

新建一个 Main.java 类:

/****/package com.example.mongotest;import com.example.config.MongoContext;import com.mongodb.DBCursor;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 :: testsearch Query ::{ "age" : 32}{ "_id" : { "$oid" : "58ecde108b308657b44937b1"} , "name" : "Shamik" , "adress" : "1 Nivedita Lane" , "age" : 32}



0 0
原创粉丝点击