MongoDB 是时下非常流行的非关系型数据库, Vertx 也对它提供了很好的支持。本文演示如何用 Vertx 的 MongoDB client。
加入依赖
在项目的 pom.xml 文件中,添加 MongoDB client。
1 2 3 4 5
| <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-mongo-client</artifactId> <version>3.9.2</version> </dependency>
|
获取连接实例
在 Vertx MongoDB Client 中, 对 MongoDB 的访问都包装在 MongoClient 这个类中。因此我们首先需要获取 MongoClient。 在这里,我们设计一个简单的单例来封装这部分的逻辑,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| @Slf4j public class MongoDB {
private JsonObject config = null; private Vertx vertx = null; private JsonObject mongoconfig = new JsonObject();
private static final MongoDB instance = new MongoDB();
public MongoDB() {
}
public static MongoDB getInstance() { return instance; }
public MongoDB init(Vertx vertx, JsonObject config) { this.vertx = vertx; this.config = config;
mongoconfig.put("connection_string", config.getString("connStr")); mongoconfig.put("db_name", config.getString("dbName")); mongoconfig.put("username", config.getString("username")); mongoconfig.put("password", config.getString("password")); mongoconfig.put("authSource", config.getString("authSource"));
return this; }
public MongoClient getClient() {
return MongoClient.createShared(vertx, mongoconfig, MongoDefault.MONGODB_SHARE_POOL_NAME); } }
|
MONGODB_SHARE_POOL_NAME 是定义在 MongoDefault 这个类中的一个字符串常量,用来指定共享名,这样,在同一个 Vertx 的上下文中,就可以使用 MongoClient 的共享池。
定义抽象实体类
在 Vertx 中, MongoDB client 可以直接对 JsonObject (Vertx的”一等公民”)进行操作,但在企业开发中,我们往往习惯于操作实体类,因此在这里定义一个抽象的实体类来包装 JsonObject 和实体之间这转换。 代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| @JsonIgnoreProperties(ignoreUnknown = true) public abstract class AbstractMongoModel {
private String _id;
private String createDate;
public String get_id() { return _id; }
public void set_id(String _id) { this._id = _id; }
public String getCreateDate() { return createDate; }
public void setCreateDate(String createDate) { this.createDate = createDate; }
@JsonIgnore public JsonObject getPKQuery() { JsonObject query = new JsonObject(); query.put("_id", _id); return query; }
public JsonObject toJson() {
JsonObject json = JsonObject.mapFrom(this); return json; }
public static <T> T fromJson(JsonObject json, Class<T> t) {
T o = json.mapTo(t); return o; }
public static <T> List<T> fromJsonArray(JsonArray jsona, Class<T> t) {
List<T> rc = new ArrayList<T>(); List<T> o = jsona.getList(); for(T tt: o){ T oo = ((JsonObject) tt).mapTo(t); rc.add(oo); } return rc; }
}
|
定义回调接口
因为在 Vertx 中的 API 都设计为以回调的方法来返回值,为后续进行统一处理方法,我们定义要给回调的包装接口:
1 2 3 4
| public interface MResultHandler<T> {
public void handle(MResult<T> mResult); }
|
和一个对应的类:
1 2 3 4 5 6 7 8
| @Data @NoArgsConstructor public class MResult<T> {
private boolean succ = false; private Throwable exception = null; private T data; }
|
定义查询方法
首先,定义一个通用的,通过查找条件 (query) 来查询一整个对象(文档,记录)的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| protected void findOne(JsonObject query, MResultHandler<T> handler) {
MongoClient mongo = MongoDB.getInstance().getClient();
mongo.findOne(getRepositoryName(), query, null, h -> { MResult<T> mResult = new MResult<>(); if (h.succeeded()) { JsonObject obj = h.result(); if (obj != null) { T t = (T) T.fromJson(obj, entityClass); postMapper(obj, t); mResult.setSucc(true); mResult.setData(t); } } handler.handle(mResult);
mongo.close(); });
}
|
稍加包装,就可以得到要给通过主键(“_id”)来查找一整个对象(文档,记录)的方法:
1 2 3 4 5 6 7
| public void findByPK(String pk, MResultHandler<T> handler) {
JsonObject query = new JsonObject(); query.put("_id", pk);
findOne(query, handler); }
|
定义新增方法
在新增中,可以考虑由 MongoDB 自己生成 _id 的情况和用户指定的情况, 代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public void insert(T t, boolean removeId, MResultHandler<String> mh) {
MongoClient mongo = MongoDB.getInstance().getClient();
JsonObject json = t.toJson();
postToJson(json, t);
if (removeId) { json.remove("_id"); } json.put("createDate", DateTimeUtil.getCurrentDateTime());
mongo.insert(getRepositoryName(), json, h -> {
MResult<String> result = new MResult<>(); if (h.succeeded()) { result.setSucc(true); result.setData(h.result()); } else { log.error("Failed to insert object: ", h.cause()); } mh.handle(result); mongo.close(); });
}
|
定义更新方法
在更新中,我们通过判断影响的文档(记录)数来判断更新是否成功, 代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| private static final UpdateOptions DEFAULT_UPDATE_OPTIONS = new UpdateOptions(false, false); protected void update(JsonObject query, JsonObject update, MResultHandler<JsonObject> handler) {
JsonObject setPhase = new JsonObject(); setPhase.put("$set", update);
MongoClient mongo = MongoDB.getInstance().getClient();
mongo.updateCollectionWithOptions(getRepositoryName(), query, setPhase, DEFAULT_UPDATE_OPTIONS, h -> {
MResult<JsonObject> mResult = new MResult<>(); if (h.succeeded()) { MongoClientUpdateResult r = h.result(); if (r.getDocMatched() == 1) { mResult.setSucc(true); } } else { log.error("Update failed: ", h.cause()); } handler.handle(mResult);
mongo.close(); });
}
|
定义删除方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public void remove(JsonObject query, MResultHandler<JsonObject> handler) {
MongoClient mongo = MongoDB.getInstance().getClient();
mongo.removeDocumentsWithOptions(getRepositoryName(), query, null, h -> {
MResult<JsonObject> mResult = new MResult<>(); if (h.succeeded()) { MongoClientDeleteResult r = h.result(); if (r.getRemovedCount() > 0) { mResult.setSucc(true); } } else {
} handler.handle(mResult);
mongo.close(); });
}
|