序言:
近来无事,想着不久将来将用到NoSql,于是选择了MongoDB作为研究切入点。网上找了挺多 MongoDB Client 封装,都过于简单并且只能作为学习MongoDB入门例子,于是自己封装了MongoCliDrv作为与MongoDB交互工具类。
工具类支持基本的增删改查,查询支持返回Map和Bean对象。封装代码有用到自己helios框架的tool包,本文目的在于MongoDB封装思路上,如有需要读者,可以跟我要 helios-tool.jar 包。
代码如下:
/**
* MongoDB 操作封装,功能接口包括增删改查
* 查询支持分页和不分页查询,并支持查询返回 Map 和 Bean对象;
*
* by @author lms 2021.08.10
* last update by lms 2021.08.13 增加查询排序功能
*/
package ms.db.mongodb;
import java.security.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.bson.types.ObjectId;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.Sorts;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import ms.core.support.Page;
import ms.core.tool.JsonTool;
import ms.core.tool.Log4j2Tool;
import ms.core.tool.PropTool;
import ms.core.tool.StrTool;
public class MongoCliDrv {
//查询条件拼接逻辑 AND-并关系; OR-或关系; MIX-并和或混合(暂不支持)
public enum SqlLinkType {AND, OR, MIX}
//eq=>equal(等于操作); lt=>less than(小于);gt=>greater than(大于);let=>less equal than(小于等于);het=>higher equal than(大于等于);neq=>not equal(不等于)
public enum Sign{eq,lt,gt,let,get,neq}; //操作符
private static String configFile = "/config/mongo.properties";
private String host;
private int port;
private String dbName;
private String userName;
private String password;
private int maxConnections;
private int connectionTimeout;
private MongoClient cli;
/**
* 设置配置文件
* @param conf
*/
public static void initMongoConfigFile(String conf) {
if (StrTool.isBlank(conf) || conf.equalsIgnoreCase(configFile))
return;
configFile = conf;
}
public static MongoCliDrv getInstance() {
return new MongoCliDrv();
}
private boolean readMongoConfig() {
try {
PropTool tool = PropTool.getInstance(configFile);
host = tool.getValue("host", "127.0.0.1");
port = StrTool.strToInt(tool.getValue("port", "27017"), 27017);
dbName = tool.getValue("dbName", "");
userName = tool.getValue("userName", "");
password = tool.getValue("password", "");
maxConnections = StrTool.strToInt(tool.getValue("maxConnections", "100"), 100);
connectionTimeout = StrTool.strToInt(tool.getValue("connectionTimeout", "3000"), 3000);
return true;
}catch(Exception e) {
e.printStackTrace();
return false;
}
}
MongoCliDrv() {
if (cli!=null) return;
//读取mongo客户端连接配置
if (!readMongoConfig()) {
Log4j2Tool.error(MongoCliDrv.class, "MongoDB连接配置文件未指定或不存在!");
return;
}
try {
if (!StrTool.isBlank(userName)) {
ServerAddress addr = new ServerAddress(host, port);
//通过连接认证获取MongoDB连接
MongoCredential credential = MongoCredential.createScramSha1Credential(userName, dbName, password.toCharArray());
MongoClientOptions options = MongoClientOptions.builder()
.connectionsPerHost(maxConnections)
.connectTimeout(connectionTimeout)
.cursorFinalizerEnabled(true) //是否启用游标的 finalize 方法,用于清理客户端未关闭的 DBCursor 实例
.build();
cli = new MongoClient(addr, credential, options);
} else {
cli = new MongoClient(host, port);
}
}catch(Exception e) {
Log4j2Tool.error(MongoCliDrv.class, e);
}
}
public void close() {
try {
if (cli!=null) cli.close();
}catch(Exception e) {
Log4j2Tool.error(MongoCliDrv.class, e);
}
cli = null;
}
private MongoCollection<Document> getCollectionObject(String collectionName) {
try {
MongoDatabase db = cli.getDatabase(dbName);
MongoCollection<Document> collection = db.getCollection(collectionName);
return collection;
} catch(Exception e) {
Log4j2Tool.error(MongoCliDrv.class, e);
return null;
}
}
/**
* 写入单条数据
* @param collectionName 集合名称
* @param value 写入数据对象,可以是json字符串、JavaBean、Map
* @return
*/
public int insertDocument(String collectionName, Object value) {
try {
MongoCollection<Document> collection = getCollectionObject(collectionName);
Map<String, Object> map = JsonTool.objectToMap(value, false);
Document doc = new Document(map);
collection.insertOne(doc);
return 1;
}catch(Exception e) {
Log4j2Tool.error(MongoCliDrv.class, e);
return -1;
}
}
/**
* 插入 List<Map>
* @param collectionName
* @param values
* @return
*/
public int insertListMapToDocuments(String collectionName, List<Map<String, Object>> values) {
try {
MongoCollection<Document> collection = getCollectionObject(collectionName);
int size = values.size();
List<Document> list = new ArrayList<>();
for(int i=0; i<size; i++) {
list.add(new Document(values.get(i)));
}
collection.insertMany(list);
return values.size();
}catch(Exception e) {
Log4j2Tool.error(MongoCliDrv.class, e);
return -1;
}
}
/**
* 批量写入数据
* @param collectionName
* @param values
* @return
*/
public <T> int insertListBeanToDocuments(String collectionName, List<T> values) {
try {
MongoCollection<Document> collection = getCollectionObject(collectionName);
int size = values.size();
List<Document> list = new ArrayList<>();
for(int i=0; i<size; i++) {
list.add(new Document(JsonTool.objectToMap(values.get(i), true)));
}
collection.insertMany(list);
return values.size();
}catch(Exception e) {
Log4j2Tool.error(MongoCliDrv.class, e);
return -1;
}
}
/**
* 条件参数转 Bson 过滤对象
* @param params
* @param linkType
* @return
*/
private Bson toFilter(List<FieldParam> params, SqlLinkType linkType){
if (params==null || params.size()==0)
return null;
List<Bson> filters = new ArrayList<>();
for(int i=0; i<params.size(); i++) {
FieldParam p = params.get(i);
switch (p.getSign()) {
case eq: //等于
filters.add(Filters.eq(p.getKey(), p.getValue()));
break;
case lt: //小于
filters.add(Filters.lt(p.getKey(), p.getValue()));
break;
case gt: //大于
filters.add(Filters.gt(p.getKey(), p.getValue()));
break;
case let: //小等于
filters.add(Filters.lte(p.getKey(), p.getValue()));
break;
case get: //大等于
filters.add(Filters.gte(p.getKey(), p.getValue()));
break;
case neq: //不等于
filters.add(Filters.ne(p.getKey(), p.getValue()));
break;
default: break;
}
}
//条件组合
switch (linkType) {
case AND: return Filters.and(filters);
case OR: return Filters.or(filters);
default: return null;
}
}
/**
* 条件 Map 参数转 Bson 并过滤对象
* @param params
* @param linkType
* @return
*/
private Bson toFilter(Map<String, Object> cdt){
if (cdt==null)
return null;
List<Bson> filters = new ArrayList<>();
for(Map.Entry<String, Object> entry: cdt.entrySet()) {
filters.add(Filters.eq(entry.getKey(), entry.getValue()));
}
return Filters.and(filters);
}
/**
* 排序参数转对象
* @param list
* @return
*/
private Bson toOrderBy(List<Sort> list) {
if (list==null || list.size()==0) return null;
List<Bson> tmp = new ArrayList<>();
for(int i=0; i<list.size(); i++) {
Sort e = list.get(i);
if (e.getSortType()==Sort.SortType.ASC)
tmp.add(Sorts.ascending(e.getFieldName()));
else
tmp.add(Sorts.descending(e.getFieldName()));
}
return Sorts.orderBy(tmp);
}
/**
* 删除文档数据接口
* @param collectionName 集合名称
* @param delFilter 删除条件
* @return
*/
public long deleteDocument(String collectionName, Bson delFilter) {
try {
MongoCollection<Document> collection = this.getCollectionObject(collectionName);
DeleteResult ret = collection.deleteMany(delFilter);
return ret.getDeletedCount();
}catch(Exception e) {
Log4j2Tool.error(MongoCliDrv.class, e);
return -1;
}
}
/**
* 根据条件(key=value)并关系批量删除
* @param collectionName
* @param cdt
* @return
*/
public long deleteDocument(String collectionName, Map<String, Object> cdt) {
try {
MongoCollection<Document> collection = this.getCollectionObject(collectionName);
Bson filter = this.toFilter(cdt);
DeleteResult ret = collection.deleteMany(filter);
return ret.getDeletedCount();
}catch(Exception e) {
Log4j2Tool.error(MongoCliDrv.class, e);
return -1;
}
}
/**
* 根据条件"and"或者"or"关系批量删除
* @param collectionName 集合名称
* @param param 删除条件
* @return
*/
public long deleteDocument(DeleteParam param) {
try {
MongoCollection<Document> collection = this.getCollectionObject(param.getCollection());
Bson filter = toFilter(param.getFields(), param.getLinkType());
DeleteResult ret = collection.deleteMany(filter);
return ret.getDeletedCount();
}catch(Exception e) {
Log4j2Tool.error(MongoCliDrv.class, e);
return -1;
}
}
/**
* 删除集合
* @param collectionName
* @return
*/
public boolean dropCollection(String collectionName) {
try {
MongoCollection<Document> collection = this.getCollectionObject(collectionName);
collection.drop();
return true;
}catch(Exception e) {
Log4j2Tool.error(MongoCliDrv.class, e);
return false;
}
}
/**
* 按指定条件更新指定结果值
* @param collectionName 集合名称
* @param cdt 更新条件
* @param value 更新值
* @return
*/
public long updateDocument(String collectionName, Map<String, Object> cdt, Map<String, Object> value) {
try {
MongoCollection<Document> collection = this.getCollectionObject(collectionName);
Bson filter = this.toFilter(cdt);
Document doc = new Document(value);
UpdateResult ret = collection.updateMany(filter, doc);
return ret.getModifiedCount();
}catch(Exception e) {
Log4j2Tool.error(MongoCliDrv.class, e);
return -1;
}
}
//
查询功能接口 ///
//
/**
* 对象转换工具: Document 转 map
* @param doc
* @return
*/
public static Map<String, Object> toMap(Document doc){
if (doc==null) return null;
Map<String, Object> ret = new HashMap<>();
for(Map.Entry<String, Object> item: doc.entrySet()) {
if (item.getValue() instanceof ObjectId)
ret.put(item.getKey(), ((ObjectId)item.getValue()).toString());
else if (item.getValue() instanceof Timestamp)
ret.put(item.getKey(), ((Timestamp)item.getValue()).getTimestamp().getTime());
else
ret.put(item.getKey(), item.getValue());
}
return ret;
}
/**
* 对象转换工具: map转bean对象
* @param map
* @param cls
* @return
*/
@SuppressWarnings("unchecked")
public static <T> T toBean(Map<String, Object> map, Class<?> cls){
return (T) JsonTool.objectToBean(map, cls);
}
/**
* 对象转换工具: Document转bean对象
* @param doc
* @param cls
* @return
*/
public static <T> T toBean(Document doc, Class<?> cls){
return toBean(toMap(doc), cls);
}
/**
* 根据查询条件查询数据(原生态查询方式)
* @param collectionName 集合名称
* @param filter 过滤条件对象
* @param sort 排序对象
* @param page 查询页码(<=0表示不分页)
* @param pagesize 页大小(<=0表示不分页)
* @return
*/
public Page<Map<String, Object>> queryDocument(String collectionName, Bson filter, Bson sort, int page, int pagesize){
try {
MongoCollection<Document> collection = this.getCollectionObject(collectionName);
Page<Map<String, Object>> ret = new Page<>();
ret.setPageInfo(page, pagesize, 0, 0, null);
long rows = 0;
int pages = 1;
FindIterable<Document> docs = null;
//查询
if (filter!=null) {
rows = collection.countDocuments(filter);
docs = collection.find(filter);
}
else {
rows = collection.countDocuments();
docs = collection.find();
}
if (docs==null) return ret;
if (sort!=null) docs = docs.sort(sort);
//处理分页
if (page>0 && pagesize>0) {
pages = Math.round((rows+pagesize-1)/pagesize);
docs = docs.skip((page-1)*pagesize).limit(pagesize);
if (page>pages) return ret;
}
//取出数据
List<Map<String, Object>> data = new ArrayList<>();
MongoCursor<Document> cursor = docs.iterator();
while (cursor.hasNext()) {
Map<String, Object> item = toMap(cursor.next());
data.add(item);
}
ret.setPageInfo(page, pagesize, pages, (int)rows, data);
return ret;
}catch(Exception e) {
Log4j2Tool.error(MongoCliDrv.class, e);
return null;
}
}
/**
* 根据查询条件查询数据(原生态查询方式)
* @param collectionName 集合名称
* @param filter 过滤条件对象
* @param sort 排序对象
* @param cls 转换目标类
* @param page 查询页
* @param pagesize 页大小
* @return
*/
public <T>Page<T> queryDocument(String collectionName, Bson filter, Bson sort, Class<?> cls, int page, int pagesize){
Page<Map<String, Object>> tmp = queryDocument(collectionName, filter, sort, page, pagesize);
if (tmp==null) return null;
Page<T> ret = new Page<>();
ret.setPageInfo(tmp.getPage(), tmp.getPages(), tmp.getPages(), tmp.getRows(), null);
if (tmp.getData()==null || tmp.getData().size()==0) return ret;
List<T> data = new ArrayList<>();
try {
int size = tmp.getData().size();
for(int i=0; i<size; i++) {
data.add(toBean(tmp.getData().get(i),cls));
}
ret.setData(data);
return ret;
}catch(Exception e) {
Log4j2Tool.error(MongoCliDrv.class, e);
return null;
}
}
/**
* 多条件 "and" 或 "or" 查询
* @param <T>
* @param param
* @param cls
* @return
*/
public Page<Map<String, Object>> queryDocument(QueryParam param){
try {
Bson filter = toFilter(param.getFields(), param.getLinkType());
Bson orderBy = toOrderBy(param.getSorts());
return queryDocument(param.getCollection(), filter, orderBy, param.getPage(), param.getPagesize());
}catch(Exception e) {
Log4j2Tool.error(MongoCliDrv.class, e);
return null;
}
}
/**
* 多条件 "and" 或 "or" 查询
* @param param
* @param cls
* @return
*/
public <T>Page<T> queryDocument(QueryParam param, Class<?> cls){
Page<Map<String, Object>> tmp = queryDocument(param);
if (tmp==null) return null;
Page<T> ret = new Page<>();
ret.setPageInfo(tmp.getPage(), tmp.getPages(), tmp.getPages(), tmp.getRows(), null);
if (tmp.getData()==null || tmp.getData().size()==0) return ret;
try {
List<T> data = new ArrayList<>();
int size = tmp.getData().size();
for(int i=0; i<size; i++) {
data.add(toBean(tmp.getData().get(i),cls));
}
ret.setData(data);
return ret;
}catch(Exception e) {
Log4j2Tool.error(MongoCliDrv.class, e);
return null;
}
}
/**
* 根据(key=value)多条件并关系查询
* @param param
* @param cls
* @return
*/
public Page<Map<String, Object>> queryDocument(String collectionName, Map<String, Object> cdt){
QueryParam param = new QueryParam(collectionName);
List<FieldParam> list = new ArrayList<>();
for(Map.Entry<String, Object> entry: cdt.entrySet()) {
FieldParam p = new FieldParam(entry.getKey(), entry.getValue(), Sign.eq);
list.add(p);
}
param.setFields(list);
//不分页
param.setPage(-1);
param.setPagesize(0);
//条件并关系
param.setLinkType(SqlLinkType.AND);
return queryDocument(param);
}
/**
* 根据(key=value)多条件并关系查询
* @param param
* @param cls
* @return
*/
public <T>Page<T> queryDocument(String collectionName, Map<String, Object> cdt, Class<?> cls){
Page<Map<String, Object>> tmp = queryDocument(collectionName, cdt);
if (tmp==null) return null;
Page<T> ret = new Page<>();
ret.setPageInfo(tmp.getPage(), tmp.getPageSize(), tmp.getPages(), tmp.getRows(), null);
if (tmp.getData()==null || tmp.getData().size()==0) return ret;
try {
List<T> data = new ArrayList<>();
int size = tmp.getData().size();
for(int i=0; i<size; i++) {
data.add(toBean(tmp.getData().get(i),cls));
}
ret.setData(data);
return ret;
}catch(Exception e) {
Log4j2Tool.error(MongoCliDrv.class, e);
return null;
}
}
/**
* Mongo SQL 条件
* @author lms
*/
public static class QueryParam{
private String collection; //集合名称
private int page; //查询起始页
private int pagesize; //页大小
private List<FieldParam> fields; //查询参数
private SqlLinkType linkType; //条件拼接符号
private List<Sort> sorts; //排序参数
QueryParam(String collection){ this.collection = collection; }
public String getCollection() { return collection; }
public void setCollection(String collection) { this.collection = collection; }
public int getPage() { return page; }
public void setPage(int page) { this.page = page; }
public int getPagesize() { return pagesize; }
public void setPagesize(int pagesize) { this.pagesize = pagesize; }
public List<FieldParam> getFields() { return fields; }
public void setFields(List<FieldParam> fields) { this.fields = fields; }
public QueryParam addFieldParam(String key, Object value, Sign sign) {
if (fields==null) fields = new ArrayList<>();
FieldParam p = new FieldParam(key, value, sign);
fields.add(p);
return this;
}
public SqlLinkType getLinkType() { return linkType; }
public void setLinkType(SqlLinkType linkType) { this.linkType = linkType; }
public List<Sort> getSorts() { return sorts; }
public void setSorts(List<Sort> sorts) { this.sorts = sorts; }
public QueryParam addSort(String field, Sort.SortType sort) {
if (sorts==null) sorts = new ArrayList<>();
sorts.add(new Sort(field, sort));
return this;
}
}
//删除数据参数对象
public static class DeleteParam{
private String collection;
private List<FieldParam> fields;
private SqlLinkType linkType;
public String getCollection() { return collection; }
public void setCollection(String collection) { this.collection = collection; }
public List<FieldParam> getFields() { return fields; }
public void setFields(List<FieldParam> fields) { this.fields = fields; }
public DeleteParam addParam(String key, Object value, Sign sign) {
if (fields==null) fields = new ArrayList<>();
FieldParam p = new FieldParam(key, value, sign);
fields.add(p);
return this;
}
public SqlLinkType getLinkType() { return linkType; }
public void setLinkType(SqlLinkType linkType) { this.linkType = linkType; }
}
//单字段查询条件
public static class FieldParam{
private String key; //key名称
private Object value; //key值
private Sign sign; //操作符
FieldParam(String key, Object value, Sign sign){
this.key = key;
this.value = value;
this.sign = sign;
}
public String getKey() { return key; }
public void setKey(String key) { this.key = key; }
public Object getValue() { return value; }
public void setValue(Object value) { this.value = value; }
public Sign getSign() { return sign; }
public void setSign(Sign sign) { this.sign = sign; }
}
//排序
public static class Sort{
public enum SortType{ASC, DESC};
Sort(String fieldName, SortType sortType){
this.fieldName = fieldName;
this.sortType = sortType;
}
private String fieldName;
private SortType sortType;
public String getFieldName() { return fieldName; }
public void setFieldName(String fieldName) { this.fieldName = fieldName; }
public SortType getSortType() { return sortType; }
public void setSortType(SortType sortType) { this.sortType = sortType; }
}
public static void main(String args[]) {
MongoCliDrv.initMongoConfigFile("/config/mongo.properties");
MongoCliDrv mongo = MongoCliDrv.getInstance();
//Map<String, Object> value = new HashMap<>();
//value.put("recordTime", new Date());
//value.put("name", "linkin");
//value.put("age", 10);
//mongo.insertDocument("test", value);
QueryParam param = new QueryParam("test");
param.setPage(1);
param.setPagesize(5);
param.addSort("age", Sort.SortType.DESC);
param.addFieldParam("age", 15, Sign.let);
param.addFieldParam("age", 1, Sign.get);
param.setLinkType(SqlLinkType.AND);
System.out.println(JsonTool.objectToJson(mongo.queryDocument(param, Person.class)));
mongo.close();
}
}
MongoDB连接配置文件:
#mongo服务地址
host=127.0.0.1
#mongo服务端口,默认27017
port=27017
#database
dbName=MyMongo
#数据库帐户,如果为空,则跳过用户验证
userName=
#数据库密码
password=
#每个客户端最大连接数,默认为100
maxConnections=200
#连接超时时间,推荐>3000毫秒
connectionTimeout=5000
#线程队列数,如果连接线程排满了队列就会抛出"Out of semaphores to get db"错误
maxBlockThreads=500
Maven 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>helios.db</groupId>
<artifactId>helios-mongo</artifactId>
<version>helios-mongodb-1.0.0</version>
<packaging>jar</packaging>
<name>helios-mongo</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<helios-tool.version>1.0.0</helios-tool.version>
</properties>
<dependencies>
<!-- 引入框架工具包 -->
<dependency>
<groupId>helios.core</groupId>
<artifactId>helios-tool</artifactId>
<version>${helios-tool.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mongodb/mongo-java-driver -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.12.10</version>
</dependency>
</dependencies>
</project>