Vert.x 学习之MongoDB Client
Vert.x MongoDB Client
组件介绍
您的 Vert.x 应用可以使用 Vert.x MongoDB Client(以下简称客户端)来与 MongoDB 进行交互,包括保存,获取,搜索和删除文档。
MongoDB 是在 Vert.x 应用进行数据持久化时的最佳选择,因为 MongoDB 天生就是处理 JSON(BSON)格式的文档数据库。
特点
- 完全非阻塞
- 支持自定义编解码器,从而实现 Vert.x JSON 快速序列化和反序列化
- 支持 MongoDB Java 驱动大部分配置项
本客户端基于 MongoDB Async Driver。
使用 Vert.x MongoDB Client
使用此客户端,需要添加下列依赖:
- Maven (在
pom.xml
文件中):
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-mongo-client</artifactId>
<version>3.4.1</version>
</dependency>
- Gradle (在
build.gradle
文件中):
compile 'io.vertx:vertx-mongo-client:3.4.1'
创建客户端
您有以下几种方式来创建客户端:
默认使用共享的连接池
大部分情况下,您希望不同的客户端之间共享一个连接池。
例如:我们在部署 Verticle 时,设置了 Verticle 拥有多个实例化的对象,但是我们希望每个 Verticle 实例能够共享同一个数据源,而不是单独为每个 Verticle 实例设置不同的数据源。
要解决上面的问题,我们可以这么做:
MongoClient client = MongoClient.createShared(vertx, config);
只有在第一次调用 MongoClient.createShared
方法的时候,才会真正的根据 config
参数创建一个数据源。
之后再调用此方法,只会返回一个新的客户端对象,但使用的是相同的数据源。这时 config
参数也就不再有作用。
指定数据源名称
您还可以像下面这样,在创建一个客户端的时候指定数据源的名称:
MongoClient client = MongoClient.createShared(vertx, config, "MyPoolName");
如果不同的客户端对象使用了相同的 Vert.x 对象和相同的数据源名称,那么它们将共享数据源。
同样的(与默认使用共享的数据源),只有在第一次调用MongoClient.createShared
方法的时候,才会真正的根据 config
参数创建一个数据源。
之后再调用此方法,只会返回一个新的客户端对象,但使用的是相同的数据源。这时 config
参数也就不再有作用。
当我们希望不同含义的客户端对象拥有不同的数据源时,可以采用这种方式来创建它的对象。比如它们要与不同的数据源进行交互。
创建不共享数据源的客户端对象
在大部分情况下,我们会希望在不同的客户端对象之间共享数据源。但有时候,却恰恰相反。
这时,可以调用 MongoClient.createNonShared
方法:
MongoClient client = MongoClient.createNonShared(vertx, config);
每次调用此方法,就相当于在调用 MongoClient.createShared
方法时加上了具有唯一名称的数据源参数。
使用客户端 API
MongoClient
接口定义了操作客户端的API 方法。您可以使用 MongoClient
来使用调用 API 方法。
保存文档
您可以使用 save
方法来保存文档。
如果文档中没有 _id
字段,文档会被保存。若有,将执行 upserted。Upserted 意思是,如果此文档不存在,就保存此文档,此文档存在就更新。
如果文档没有 _id
字段,回调方法中可以获得保存后生成的 id 。
例如:
JsonObject document = new JsonObject().put("title", "The Hobbit"); mongoClient.save("books", document, res -> { if (res.succeeded()) { String id = res.result();
System.out.println("Saved book with id " + id); } else {
res.cause().printStackTrace();
} });
下面的例子,文档已有 _id
:
JsonObject document = new JsonObject().put("title", "The Hobbit").put("_id", "123244"); mongoClient.save("books", document, res -> { if (res.succeeded()) { // ... } else {
res.cause().printStackTrace();
} });
插入文档
您可以使用 insert
方法来插入文档。
如果被插入的文档没有包含 id,回调方法中可以获得保存后生成的 id 。
JsonObject document = new JsonObject().put("title", "The Hobbit"); mongoClient.insert("books", document, res -> { if (res.succeeded()) { String id = res.result();
System.out.println("Inserted book with id " + id); } else {
res.cause().printStackTrace();
} });
如果被插入的文档包含 id,但是此 id 代表的文档已经存在,插入就会失败:
JsonObject document = new JsonObject().put("title", "The Hobbit").put("_id", "123244"); mongoClient.insert("books", document, res -> { if (res.succeeded()) { //... } else { // Will fail if the book with that id already exists.
} });
更新文档
您可以使用 update
方法来更新文档(译者注:建议使用 updateCollection
方法或者 updateCollectionWithOptions
方法)。
此方法可以更新集合(译者注:MongoDB 中的集合概念对应 SQL 中的数据库表)中的一个或多个文档。其中 update
参数的 JSON 对象,必须包含 Update Operators ,因为由它决定更新的方式。
其中 query
参数决定更新集合中的哪个文档。
例如更新 books 集合中的一个文档:
JsonObject query = new JsonObject().put("title", "The Hobbit"); // Set the author field
JsonObject update = new JsonObject().put("$set", new JsonObject().put("author", "J. R. R. Tolkien")); mongoClient.update("books", query, update, res -> { if (res.succeeded()) { System.out.println("Book updated !"); } else { res.cause().printStackTrace();
} });
您可以使用 updateWithOptions
方法。
如果要指定 update 操作到底是 upsert(upsert 意思是,如果此文档不存在,就保存此文档;此文档存在就更新)或者仅仅只更新,请使用 updateWithOptions
方法并传递参数 UpdateOptions
.
参数 UpdateOptions
有以下选项:
multi
若设置为 true,则可以更新多个文档
upsert
若设置为 true,则可以在没有查询到要更新的文档时,新增该文档
writeConcern
写操作的可靠性(译者注:源码中是用
writeOption
枚举类型来代表的)
//译者注:MongoDB 默认写操作级别是 WriteOption.ACKNOWLEDGED
JsonObject query = new JsonObject().put("title", "The Hobbit");
// Set the author field
JsonObject update = new JsonObject().put("$set", new JsonObject().put("author", "J. R. R. Tolkien")); UpdateOptions options = new UpdateOptions().setMulti(true); mongoClient.updateWithOptions("books", query, update, options, res -> { if (res.succeeded()) { System.out.println("Book updated !"); } else { res.cause().printStackTrace();
} });
替换文档
您可以使用 replace
方法来替换文档
replace
方法和 update
方法类似,但是并不需要 update
中的 UpdateOptions
参数。因为 replace
方法替换的是 query
参数找到的整个文档。
例如替换 books 集合中的一个文档:
JsonObject query = new JsonObject().put("title", "The Hobbit"); JsonObject replace = new JsonObject().put("title", "The Lord of the Rings").put("author", "J. R. R. Tolkien"); mongoClient.replace("books", query, replace, res -> { if (res.succeeded()) { System.out.println("Book replaced !"); } else { res.cause().printStackTrace(); } });
查找文档
您可以使用 find
方法查找文档。
其中 query
参数用来匹配集合中的文档。
例如匹配所有文档:
JsonObject query = new JsonObject(); mongoClient.find("books", query, res -> { if (res.succeeded()) { for (JsonObject json : res.result()) { System.out.println(json.encodePrettily()); } } else { res.cause().printStackTrace(); } });
又例如匹配 books 集合中某一个作者的所有文档:
JsonObject query = new JsonObject().put("author", "J. R. R. Tolkien"); mongoClient.find("books", query, res -> { if (res.succeeded()) { for (JsonObject json : res.result()) { System.out.println(json.encodePrettily()); } } else { res.cause().printStackTrace(); } });
查询的结果包装成了 JSON 对象的 List 集合。
如果您需要指定返回哪些域,又或者需要指定返回的数据条数,可以使用 findWithOptions
方法,在参数 FindOptions
中指定这些查询要求。
FindOptions
中可以设置以下参数:
fields
指定返回哪些域。默认值为
null
,意味着返回所有域。sort
指定排序字段。默认为
null
。limit
指定返回的数据条数。默认值为
-1
,意味着返回所有数据。skip
表示返回的结果,跳过的数据行数。默认值为
0
。
JsonObject query = new JsonObject().put("author", "J. R. R. Tolkien"); mongoClient.findBatch("book", query, res -> { if (res.succeeded()) { if (res.result() == null) { System.out.println("End of research"); } else { System.out.println("Found doc: " + res.result().encodePrettily()); } } else { res.cause().printStackTrace(); }
});
所有返回的结果,都可以在回调方法中得到。
查询单个文档
要查询单个文档,您可以使用 findOne
方法。
这有点类似 find
方法,但是仅仅返回 find
方法查询到的第一条数据。
删除文档
您可以使用 removeDocuments
方法来删除文档。
其中 query
参数决定了要删除集合中的哪些文档。
例如删除作者为 Tolkien 的所有文档:
JsonObject query = new JsonObject().put("author", "J. R. R. Tolkien"); mongoClient.remove("books", query, res -> { if (res.succeeded()) { System.out.println("Never much liked Tolkien stuff!"); } else { res.cause().printStackTrace(); }
});
删除单个文档
您可以使用 removeDocument
方法来删除单个文档。
这有点类似 removeDocuments
方法,但是 removeDocument
方法只返回匹配到的第一个文档。
文档计数
您可以使用 count
方法来计算文档数量。
例如计算作者为 Tolkien 的书的数量,结果包装在回调方法中。
JsonObject query = new JsonObject().put("author", "J. R. R. Tolkien"); mongoClient.count("books", query, res -> { if (res.succeeded()) { long num = res.result(); } else { res.cause().printStackTrace(); }
});
JsonObject query = new JsonObject().put("author", "J. R. R. Tolkien"); mongoClient.count("books", query, res -> { if (res.succeeded()) { long num = res.result(); } else { res.cause().printStackTrace(); }
});
管理 MongoDB 集合
MongoDB 的所有文档数据都存储在集合中。
您可以使用 getCollections
方法来获得所有的集合:
mongoClient.getCollections(res -> { if (res.succeeded()) { List<String> collections = res.result(); } else { res.cause().printStackTrace(); }
});
您也可以使用 createCollection
方法来创建一个新的集合:
mongoClient.createCollection("mynewcollectionr", res -> { if (res.succeeded()) { // Created ok! } else { res.cause().printStackTrace(); }
});
您也可以使用 dropCollection
方法来删除文档
请注意:删除一个集合将会删除集合中所有的文档!
mongoClient.dropCollection("mynewcollectionr", res -> { if (res.succeeded()) { // Dropped ok! } else { res.cause().printStackTrace(); }
});
执行 MongoDB 其他命令
您可以使用 runCommand
方法来执行任何 MongoDB 命令。
使用这种方式,可以发挥出 MongoDB 更多优点,比如使用 MapReduce。更多详情,请参考说明文档Commands。
例如执行 aggregate(译者注:聚合)命令。请注意,命令的名称要做为 runCommand
方法的一个参数,并且同时也必须包含在包装命令的 JSON 参数中。这么做是因为 JSON 不是有序的,但 BSON 却是,而且 MongoDB 期望 BSON 参数的第一个键值对是命令的名称。所以,为了明确 JSON 中的哪个键值对是命令名称,我们也就必须把命令名称单独设置为一个参数:
JsonObject command = new JsonObject()
.put("aggregate", "collection_name")
.put("pipeline", new JsonArray()); mongoClient.runCommand("aggregate", command, res -> {
if (res.succeeded()) {
JsonArray resArr = res.result().getJsonArray("result");
// etc
} else {
res.cause().printStackTrace();
}
});
MongoDB 扩展的 JSON 支持
目前,MongoDB 只支持 date,oid 和 binary 类型(请参考:http://docs.mongodb.org/manual/reference/mongodb-extended-json )
例如插入含有 date 类型字段的文档:
JsonObject document = new JsonObject().put("title", "The Hobbit")
//ISO-8601 date
.put("publicationDate", new JsonObject().put("$date", "1937-09-21T00:00:00+00:00")); mongoService.save("publishedBooks", document, res -> { if (res.succeeded()) { String id = res.result(); mongoService.findOne("publishedBooks", new JsonObject().put("_id", id), null, res2 -> {
if (res2.succeeded()) { System.out.println("To retrieve ISO-8601 date : "
+ res2.result().getJsonObject("publicationDate").getString("$date")); } else {
res2.cause().printStackTrace();
}
}); } else {
res.cause().printStackTrace();
} });
例如插入含有 binary 类型字段的文档以及读取这个字段
byte[] binaryObject = new byte[40]; JsonObject document = new JsonObject()
.put("name", "Alan Turing")
.put("binaryStuff", new JsonObject().put("$binary", binaryObject)); mongoService.save("smartPeople", document, res -> { if (res.succeeded()) { String id = res.result(); mongoService.findOne("smartPeople", new JsonObject().put("_id", id), null, res2 -> {
if(res2.succeeded()) { byte[] reconstitutedBinaryObject = res2.result().getJsonObject("binaryStuff").getBinary("$binary");
//This could now be de-serialized into an object in real life
} else {
res2.cause().printStackTrace();
}
}); } else {
res.cause().printStackTrace();
} });
例如保存一个 base 64 编码的字符串,将这个字符串作为 binary 字段插入。并且读取这个字段:
String base64EncodedString = "a2FpbHVhIGlzIHRoZSAjMSBiZWFjaCBpbiB0aGUgd29ybGQ="; JsonObject document = new JsonObject()
.put("name", "Alan Turing")
.put("binaryStuff", new JsonObject().put("$binary", base64EncodedString)); mongoService.save("smartPeople", document, res -> { if (res.succeeded()) { String id = res.result(); mongoService.findOne("smartPeople", new JsonObject().put("_id", id), null, res2 -> {
if(res2.succeeded()) { String reconstitutedBase64EncodedString = res2.result().getJsonObject("binaryStuff").getString("$binary");
//This could now converted back to bytes from the base 64 string
} else {
res2.cause().printStackTrace();
}
}); } else {
res.cause().printStackTrace();
} });
例如插入一个 object ID 并且读取它:
String individualId = new ObjectId().toHexString(); JsonObject document = new JsonObject()
.put("name", "Stephen Hawking")
.put("individualId", new JsonObject().put("$oid", individualId)); mongoService.save("smartPeople", document, res -> { if (res.succeeded()) { String id = res.result(); mongoService.findOne("smartPeople", new JsonObject().put("_id", id), null, res2 -> {
if(res2.succeeded()) {
String reconstitutedIndividualId = res2.result().getJsonObject("individualId").getString("$oid");
} else {
res2.cause().printStackTrace();
}
}); } else {
res.cause().printStackTrace();
} });
例如获取 distinct 后的值:
JsonObject document = new JsonObject().put("title", "The Hobbit"); mongoClient.save("books", document, res -> { if (res.succeeded()) { mongoClient.distinct("books", "title", String.class.getName(), res2 -> {
System.out.println("Title is : " + res2.result().getJsonArray(0));
}); } else {
res.cause().printStackTrace();
} });
例如获取批量模式下 distinct 的值:
JsonObject document = new JsonObject().put("title", "The Hobbit"); mongoClient.save("books", document, res -> { if (res.succeeded()) { mongoClient.distinctBatch("books", "title", String.class.getName(), res2 -> {
System.out.println("Title is : " + res2.result().getString("title"));
}); } else {
res.cause().printStackTrace();
} });
客户端参数配置
此客户端把配置参数放在 JSON 对象中。
此客户端支持以下这些参数:
db_name
使用的 mongoDB 实例的数据库名称。默认是
default_db
useObjectId
此参数用来支持 ObjectId 的持久化和检索。如果设置为
true
,将会在集合的文档中,以 16 进制的字符串来保存 MongoDB 的 ObjectId 类型的字段。而且在设置为true
后,可以让文档基于创建时间排序(译者注:前4个字节用来存储创建的时的时间戳,精确到秒)。您也可以通过使用ObjectId::getDate()
方法,从这个 16进制的字符串中导出创建时间。若您选择其他类型作为_id
,则设置此参数为false
。如果您保存的文档中,没有设置_id
字段的值,将会默认的生成 16进制的字符串作为_id
。此参数默认为false
。
此客户端尝试着支持驱动所支持的大多数参数配置。有两种配置方式,一种是连接字符串,另一种是驱动配置选项。
请注意:如果使用了字符串连接的方式,此客户端将会忽略所有配置选项。
connection_string
连接字符串,指的是创建客户端的字符串,例如:
mongodb://localhost:27017
。有关连接字符串格式的更多信息,请参考驱动程序文档。
驱动配置的具体选项
{
// Single Cluster Settings
"host" : "127.0.0.1", // string
"port" : 27017, // int // Multiple Cluster Settings
"hosts" : [
{
"host" : "cluster1", // string
"port" : 27000 // int
},
{
"host" : "cluster2", // string
"port" : 28000 // int
},
...
],
"replicaSet" : "foo", // string
"serverSelectionTimeoutMS" : 30000, // long // Connection Pool Settings
"maxPoolSize" : 50, // int
"minPoolSize" : 25, // int
"maxIdleTimeMS" : 300000, // long
"maxLifeTimeMS" : 3600000, // long
"waitQueueMultiple" : 10, // int
"waitQueueTimeoutMS" : 10000, // long
"maintenanceFrequencyMS" : 2000, // long
"maintenanceInitialDelayMS" : 500, // long // Credentials / Auth
"username" : "john", // string
"password" : "passw0rd", // string
"authSource" : "some.db" // string
// Auth mechanism
"authMechanism" : "GSSAPI", // string
"gssapiServiceName" : "myservicename", // string // Socket Settings
"connectTimeoutMS" : 300000, // int
"socketTimeoutMS" : 100000, // int
"sendBufferSize" : 8192, // int
"receiveBufferSize" : 8192, // int
"keepAlive" : true // boolean // Heartbeat socket settings
"heartbeat.socket" : {
"connectTimeoutMS" : 300000, // int
"socketTimeoutMS" : 100000, // int
"sendBufferSize" : 8192, // int
"receiveBufferSize" : 8192, // int
"keepAlive" : true // boolean
} // Server Settings
"heartbeatFrequencyMS" : 1000 // long
"minHeartbeatFrequencyMS" : 500 // long
}
驱动参数说明
host
mongoDB 实例运行的地址。默认是
127.0.0.1
。 如果设置了hosts
参数,就会忽略host
参数port
mongoDB 实例监听的端口。默认是
27017
。如果设置了hosts
参数,就会忽略port
参数hosts
表示支持 MongoDB 集群(分片/复制)的一组地址和端口
host
集群中某个运行实例的地址
port
集群中某个运行实例监听的端口
replicaSet
某个 mongoDB 实例作为副本集的名称
serverSelectionTimeoutMS
驱动选择服务器的最大时间,单位毫秒
maxPoolSize
连接池最大连接数。默认为
100
minPoolSize
连接池最小连接数。默认为
100
maxIdleTimeMS
连接池的连接最大空闲时间。默认为
0
,表示一直存在maxLifeTimeMS
连接池的连接最大存活时间。默认为
0
,表示永远存活。waitQueueMultiple
连接池中最大等待连接数。默认为
500
waitQueueTimeoutMS
线程等待作为连接的最长等待时间。默认为
120000
(2分钟)maintenanceFrequencyMS
维护任务进行循环检查连接的时间间隔(译者注:维护任务会定时检查连接的状态,直到连接池剩下最小连接数)。默认为
0
maintenanceInitialDelayMS
连接池启动后,维护任务第一次启动的时间。默认为
0
。username
授权的用户名。默认为
null
(意味着不需要授权)password
授权的密码
authSource
与授权用户关联的数据库名称。默认值为
db_name
authMechanism
所使用的授权认证机制。请参考 Authentication 来获取更多信息。
gssapiServiceName
当使用
GSSAPI
的授权机制时,所使用的 Kerberos 服务名。connectTimeoutMS
打开连接超时的时间,单位毫秒。默认为
10000
(10 秒)socketTimeoutMS
在 socket 上接收或者发送超时的时间。默认为
0
,意味着永远不超时(译者注:这是客户端的超时时间。如果一个 insert 达到了 socketTimeoutMS, 将无法得知服务器是否已写入)。sendBufferSize
设置 socket 发送缓冲区大小(SO_SNDBUF)。默认为
0
,这将使用操作系统默认大小。receiveBufferSize
设置 socket 接收缓冲区大小(SO_RCVBUF)。默认为
0
,这将使用操作系统默认大小。keepAlive
设置是否复用 socket(SO_KEEPALIVE)连接。默认为
false
heartbeat.socket
配置集群监视器监控 MongoDB 集群的 socket 连接情况的参数
heartbeatFrequencyMS
集群监视器访问每个集群服务器的频率。默认为
5000
(5s)minHeartbeatFrequencyMS
最小心跳频率。默认为
1000
(1s)
请注意:上面提到的各类参数的默认值,都是 MongoDB Java 驱动的默认值。请参考驱动文档来获取最新信息。
Vert.x 学习之MongoDB Client的更多相关文章
- Vert.x学习之 Web Client
Vert.x Web Client 原文档 组件源码 组件示例 中英对照表 Pump:泵(平滑流式数据读入内存的机制,防止一次性将大量数据读入内存导致内存溢出) Response Codec:响应编解 ...
- MongoDB索引(一) --- 入门篇:学习使用MongoDB数据库索引
这个系列文章会分为两篇来写: 第一篇:入门篇,学习使用MongoDB数据库索引 第二篇:进阶篇,研究数据库索引原理--B/B+树的基本原理 1. 准备工作 在学习使用MongoDB数据库索引之前,有一 ...
- MongoDB学习笔记:MongoDB 数据库的命名、设计规范
MongoDB学习笔记:MongoDB 数据库的命名.设计规范 第一部分,我们先说命名规范. 文档 设计约束 UTF-8 字符 不能包含 \0 字符(空字符),这个字符标识建的结尾 . 和 $ ...
- vert.x学习(八),用JDBCClient配合c3p0操作数据库
今天学习了下vert.x的JDBCClient,我这里将今天的学习笔记记录下来.这次学习中使用了c3p0. 用使用JDBCClient和c3p0得现在pom.xml文件里面导入对应的依赖,下面贴出xm ...
- vert.x学习(七),使用表单获取用户提交的数据
在web开发中,用的最多的就是表单了,用户通过表单提交数据到系统后台,系统又可以通过表单传递的数据做业务分析.那么这章就学习在vert.x中怎么使用表单,获取表单的参数值. 编写一个表单模板代码res ...
- vert.x学习(五),用StaticHandler来处理静态文件
做web开发,css.js.图片等静态资源是必不可少的,那么vert.x又是怎么来加载这些静态资源呢.请看StaticHandler 编写HelloStaticResource.java packag ...
- vert.x学习(四),使用模板解析器ClassLoaderTemplateResolver
在vert.x中使用模板解析,可以为我们带来很多方便.我这里学习了一下ClassLoaderTemplateResolver的简单使用.这次工程配置与上篇一样,不需要做任何多的配置.直接编写代码就可以 ...
- vert.x学习(一),开篇之hello world
今天决定学习下vert.x这个框架,记录下学习笔记. 下面列下我的开发环境: Java版本 1.8 maven版本 3.3 IDEA版本 2016 在idea中使用vert.x不用下载或安装其他东西了 ...
- NOSQL Mongo入门学习笔记 - MongoDB的安装(一)
手上的工作不是很忙,所以来学习学习很久就像接触的MongoDb,无奈前段时间工作时间都比较多.记录在这里供以后参考 环境: Centos 7 64位 开始: 1. 在官网下载Mongo : wget ...
随机推荐
- 学习Python的相关资料
Learning python the hardway Python Tip社区啄木鸟社区编程指南社区 Python基础教程MIT 计算机科学及其导论Harward:计算机科学CS50Crossin的 ...
- dotnetcore 与 hbase 之二——thrift 客户端的制作
说明 在上一篇文章dotnetcore 与 hbase 之一--hbase 环境准备结束后,我们已经有了 hbase 数据库环境.接下来就可以利用 thrift 生成 c# hbase 客户端了.如果 ...
- Meta 用法汇总
本文引自: http://blog.csdn.net/MR_LP/article/details/53607087 什么是 meta ? meta 是html语言head区的一个辅助性标签.也许你认为 ...
- mybatis 源码分析(一)框架结构概览
本篇博客将主要对 mybatis 整体介绍,包括 mybatis 的项目结构,执行的主要流程,初始化流程,API 等各模块进行简单的串联,让你能够对 mybatis 有一个整体的把握.另外在 myba ...
- 8.15 day33 进程池与线程池_协程_IO模型(了解)
进程池和线程池 开进程开线程都需要消耗资源,只不过两者比较的情况线程消耗的资源比较少 在计算机能够承受范围之内最大限度的利用计算机 什么是池? 在保证计算机硬件安全的情况下最大限度地利用计算机 ...
- 纯数据结构Java实现(2/11)(栈与队列)
栈和队列的应用非常多,但是起实现嘛,其实很少人关心. 但问题是,虽然苹果一直宣传什么最小年龄的编程者,它试图把编程大众化,弱智化,但真正的复杂问题,需要抽丝剥茧的时候,还是要 PRO 人士出场,所以知 ...
- Windows Server 2008在网络环境配置打印机
下面学习在Windows Server2008在网络环境搭建打印机服务器,打印机服务器也是很常用的,特别是在中大型企业里面,打印机数量比较多为方便管理,可以搭建一个打印机服务,这里介绍一下,本地打印机 ...
- cookie、session和application都是些什么神?——图文加案例,不怕你不会,就怕你不看
cookie.session和application都是些什么神? 前言: 一直想写一篇关于cookie和session的博客,由于种种原因,一直没有整理,这不,今天还就遇到问题了,之前虽然会,但是好 ...
- JavaScript数组方法大全(第一篇)
数组方法大全(第一篇) 注意:第一次写博客有点小紧张,如有错误欢迎指出,如有雷同纯属巧合,本次总结参考书籍JavaScript权威指南,有兴趣的小伙伴可以去翻阅一下哦 join()方法 该方法是将数组 ...
- FileUtils工具类的使用
import org.apache.commons.io.FileUtils; import org.apache.commons.io.filefilter.DirectoryFileFilter; ...