使用 MongoDB 数据库
NoSQL 数据库
NoSQL 数据库并不遵循 SQL 数据库的表状结构。相反,许多人使用文档风格的格式(见 MongoDB 等)或键 -> 值结构(见 Aerospike、Redis 等)。
本文特别针对 MongoDB,即许多 Minecraft 网络使用的数据库。
MongoDB 的概念
像在 SQL 中一样,你在 MongoDB 中也有数据库。在这些数据库中,有像表一样的东西,称为集合。但是这些表没有定义列。每个集合只是一个小 JSON 对象的列表。这些 JSON 对象可以包含任何他们想要的字段,他们不一定要有一个共同的字段。为了更快地搜索,你可以定义一个在每个 JSON 对象中都能找到的字段,比如说玩家的 UUID。这就是所谓的索引。这样,DB 只在内存中保留这些值(UUID),并在加载其他对象之前只搜索它们。你仍然可以搜索 JSON 对象中的其他值,但搜索的速度不会像搜索索引那样快。
Morphia
除了本教程中所涉及的内容外,还有一种方法可以从 MongoDB 中保存和访问数据,那就是使用一个名为 Morphia 的 API。它是完全可选的,但它使从 Java 对象创建新集合的过程变得非常精简。关于 Spigot 的教程可以在这里找到:结合 Morphia 使用 MongoDB
安装数据库
当你在 Debian Linux 上时,你可以简单地运行:
apt-get install mongodb
这将自动安装和设置你的数据库。 当不在Linux上时,你可以从他们的主页上获得MongoDB。下载
确保你的数据库安全
默认情况下,数据库将绑定在 127.0.0.1 上,这意味着它只能从根目录下访问。
如果你想把绑定 IP 改为 0.0.0.0(可以从任何地方访问),并为你的数据库创建一个用户和密码,请按照以下说明进行。启用验证功能
0.0.0.0不能从任何地方 "访问",它只是与每台本地机器的IP绑定。
开始使用 MongoDB
在你的根目录下,只要在控制台输入 mongo
,你就会进入所谓的 mongo shell。在那里你可以简单地分配命令。我们的目的是用 Java 调度这些命令,所以我们继续下载 MongoDB 的 Java 驱动。将.jar
作为库导入到你的项目中。或者,更有经验的开发者可以通过其 maven 工件导入。
我建议使用旧的 2.x 驱动,因为这里会用到它。如果你想试试新的,可以随意使用。我没能让新驱动工作,因为它的引用缺失了 :/
使用 Java 连接到 MongoDB
private DBCollection players;
private DB mcserverdb;
private MongoClient client;
public boolean connect(String ip, int port){
//连接到指定的IP和端口
//默认为localhost, 27017
try {
client = new MongoClient(ip, port);
} catch (UnknownHostException e) {
//当你在这里结束时,无法找到数据库所运行的服务器!
System.out.println("Could not connect to database!");
e.printStackTrace();
return false;
}
//获取名为 "mcserver "的数据库
//如果它不存在,将被自动创建, 一旦你在其中保存了一些东西
mcserverdb = client.getDB("mcserver");
//获取数据库 "mcserver "中名为 "player "的集合。
//相当于MySQL中的表,你可以在这里存储对象
players = mcserverdb.getCollection("players");
return true;
}
存储玩家信息
public void storePlayer(UUID uuid, String name, long tokens, String rank){
//让我们存储我们的第一个玩家!
//这个玩家以前从未玩过,我们只想为他创建一个对象。
DBObject obj = new BasicDBObject("uuid", uuid);
obj.put("name", name);
obj.put("tokens", tokens);
obj.put("rank", rank);
//让我们把它插入我们的集合。
players.insert(obj);
}
读取玩家信息
这里有两种方法。你可以使用游标来循环浏览所有的对象,或者使用 findOne()
来只在服务器端寻找一个对象。对于只搜索一个对象,就像我们现在做的那样,findOne()
更快。对于其他操作,即搜索多个对象,Cursor 非常有用。
选项 1:使用 findOne()
public void readPlayer(UUID uuid){
//让我们建立一个最小的对象,以获得集合 "player "中的所有对象,其中包含字段 "uuid",其值为uuid(我们正在搜索的内容)。
DBObject r = new BasicDBObject("uuid", uuid);
//使用findOne来只获得一个对象!
DBObject found = players.findOne(r);
if(found==null){
//用户尚未保存。在数据库中添加他!
return;
}
//找到用户了! 得到我们想要的值!
//你可以把这些对象转变成String/Long等。
//因为它们是作为二进制对象被传递的,而不是像MySQL中的字符串。
String name = (String) found.get("name");
long tokens = (long) found.get("tokens");
String rank = (String) found.get("rank");
}
选项2:使用 DBCursor
public void readPlayer(UUID uuid){
//让我们建立一个最小的对象,以获得集合 "player "中的所有对象,其中包含字段 "uuid",其值为uuid(我们正在搜索的内容)。
DBObject r = new BasicDBObject("uuid", uuid);
//创建一个游标对象来循环浏览所有的结果。
//得到所有的对象,并得到我们正在搜索的一个对象
DBObject found = null;
// try-with-resources将处理资源的关闭。
try (DBCursor cursor = players.find(r)){
while(cursor.hasNext()) {
found = cursor.next();
}
}
if (found == null) {
//用户尚未保存。在数据库中添加他!
return;
}
//找到用户了! 得到我们想要的值!
//你可以将这些对象转变成String/Long等。
//因为它们是作为二进制对象被传递的,而不是像MySQL中的字符串。
String name = (String) found.get("name");
long tokens = (long) found.get("tokens");
String rank = (String) found.get("rank");
}
更新数据库中的玩家信息
更新的工作方式和阅读一样。你搜索对象,修改它并把它送回服务器。
选项1:更新所有对象
public void updatePlayer(UUID uuid, String name, long tokens, String rank) {
DBObject r = new BasicDBObject("uuid", uuid);
DBObject found = players.findOne(r);
if (found == null){
return;
}
//用户被找到了! 让我们创建一个新的替换对象!
DBObject obj = new BasicDBObject("uuid", uuid);
obj.put("name", name);
obj.put("tokens", tokens);
obj.put("rank", rank);
//更新它! 这就简单地替换了我们找到的对象!
players.update(found, obj);
}
只更新1个对象
public void updatePlayer(UUID uuid, String rank) {
DBObject r = new BasicDBObject("uuid", uuid);
DBObject found = players.findOne(r);
if (found == null){
return;
}
BasicDBObject set = new BasicDBObject("$set", r);
set.append("$set", new BasicDBObject("rank", rank));
players.update(found, set);
}
在你的服务器中使用 MongoDB 驱动
所以,我假设在阅读完这个主题后,你已经做了一个成功使用 MongoDB 作为数据库系统的插件,耶!现在你把插件放在服务器上,启动它,然后...什么?
现在你把这个插件放在服务器上,启动它,然后......等等......什么?"错误"?
NoClassDefFound?NoClassFoundException: com.mongodb.MongoClient.?
这怎么可能呢?
一切都应该是正常的。
Oooooh 我忘了把驱动放到插件文件夹里了
~Places mongo-java-driver-{Version here}.jar into plugins folder~。
现在应该可以......等等,还是不能用?
~将mongo-java-driver-{这里的版本}.jar移到{服务器文件夹}/lib~。
发生了什么?
现在,你应该知道,MongoDB Java驱动并没有作为bukkit插件加载,因为它并不是一个插件。因此,漂浮在空中的问题是:
我怎样才能在我的服务器中使用MongoDB?
选项1:--classpath
有一个很酷的命令行选项允许我们解决这个问题,那就是 -classpath
,它的作用是告诉 JRE。
Hey! We need to execute this program with these classes!
该参数的用法是:
java –classpath ${CLASSPATH} {MAIN CLASS}
因此,对应地我们应该使用:
java -classpath "spigot.jar:lib/*" org.bukkit.craftbukkit.Main
这将把 {spigot.jar 文件夹}/lib
中的 jar 文件作为依赖项载入!现在我们可以使用 MongoDB 了,并且可以永远地快乐地运行下去了!
!!! note "注意"
- 如果使用 Windows 作为你的操作系统,你需要在上述命令中使用分号而不是冒号。
- 在使用 classpath 选项时,不要使用 `-jar spigot.jar`,classpath 将执行服务器而不带 `-jar 参数`
选项 2:将 java 驱动 shading到插件中。
shading 有它的优点也有它的缺点。缺点是最终编译的 JAR 的文件大小将包括 MongoDB Java 驱动的大小。好处是没有版本不匹配的风险(与选项 1 相反)。
如果您使用的是 Maven,请打开 pom.xml
文件,在插件部分添加 Maven shade 插件:
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.2</version>
<configuration>
</configuration>
<executions>
<execution>
<phase>deploy</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
将 MongoDB 驱动的依赖范围设置为 "scope":
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-sync</artifactId>
<version>4.0.1</version>
<scope>compile</scope>
</dependency>