整天从这里参考朋友们的文章, 今天也整理了一个 SpringMVC与Springboot整合mongodb 的范例给大家参考,省点开发时间
目录
1SpringMVC 整合 mongodb
在这里值得一说的是
我使用了 MongoSupportNew 这个转换类对 mongo 查询到的文档中的数据进行安全提取到 java对象中
相对于使用 alibaba的 faseJson JSON.parseObject(json, Class) 来说效率快了不少,
具体使用事项可通过下面代码去了解
注意: 如果是 springboot版本,则不需要这么复杂了
maven
<!-- mongodb -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver</artifactId>
<version>3.9.0</version>
</dependency>
config
低配版
package com.example.mongodb.driver.config;
import com.mongodb.MongoClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MongodbConfig {
@Value("${spring.data.mongodb.host:127.0.0.1}")
private String host;
@Value("${spring.data.mongodb.port:27017}")
private Integer port;
@Bean
public MongoClient mongoClient() {
return new MongoClient(host, port);
}
}
高配版
package com.thinkgem.jeesite.common.utils;
import com.mongodb.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;
import java.util.List;
@Slf4j
@Configuration
public class MongoManagerCc {
@Value("${mongodb.cc.authDbName:admin}")
private String authDbName;
@Value("${mongodb.cc.userName:mongo}")
private String userName;
@Value("${mongodb.cc.password:2607ce8120273ea6}")
private String password;
@Value("${mongodb.cc.host:10.208.255.255}")
private String host;
@Value("${mongodb.cc.port:37432}")
private Integer port;
@Value("${mongodb.cc.mPoolSize:100}")
private Integer poolSize;
@Value("${mongodb.cc.mBlockSize:100}")
private Integer blockSize;
@Bean("mongoManagerCcClient")
public MongoClient initMongoClient() {
MongoClient mongoCc = null;
ServerAddress serverAddress = new ServerAddress(host, port);
List<ServerAddress> seeds = new ArrayList<>();
seeds.add(serverAddress);
MongoCredential credentials = MongoCredential.createCredential(userName, authDbName, password.toCharArray());
List<MongoCredential> credentialsList = new ArrayList<>();
credentialsList.add(credentials);
mongoCc = new MongoClient(seeds, credentialsList);
MongoClientOptions.Builder options = new MongoClientOptions.Builder();
options.connectionsPerHost(poolSize);
options.threadsAllowedToBlockForConnectionMultiplier(blockSize);
options.build();
return mongoCc;
}
}
service
查询
package com.example.mongodb.driver.service;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.example.mongodb.driver.config.MongoSupportNew;
import com.example.mongodb.driver.entity.DemoObj;
import com.mongodb.BasicDBObject;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Filters;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Slf4j
@Service
public class DemoService2 extends MongoSupportNew<DemoObj> {
@Autowired
private MongoClient mongoClient;
public List<DemoObj> queryTest1() {
MongoDatabase database = mongoClient.getDatabase("demo");
MongoCollection<Document> collection = database.getCollection("test1");
// 如果mongo存的是时间类型, 则查询也需要穿时间类型的参数, 如果存的字符串, 那么可以直接通过字符串查询
Bson bson = Filters.and(
Filters.eq("STATUS", "dealing"),
Filters.gte("INSERT_DATE", DateUtil.parse("2023-01-30 00:57:27", DatePattern.NORM_DATETIME_PATTERN)),
Filters.lte("INSERT_DATE", DateUtil.parse("2023-07-30 23:57:27", DatePattern.NORM_DATETIME_PATTERN)));
BasicDBObject sortCondition = new BasicDBObject();
// 1 正序 ASC -1:倒序 DESC
sortCondition.put("INSERT_DATE", -1);
MongoCursor<Document> iterator = collection.find(bson).skip(0).limit(10).sort(sortCondition).iterator();
return getEntityList(iterator);
}
}
删除
public void delete() {
MongoDatabase database = mongoClient.getDatabase("demo");
MongoCollection<Document> collection = database.getCollection("test1");
DeleteResult deleteResult = collection.deleteOne(Filters.eq("STATUS", "normal2"));
System.out.println("deleteResult.getDeletedCount() = " + deleteResult.getDeletedCount());
}
修改
public void update() {
MongoDatabase database = mongoClient.getDatabase("demo");
MongoCollection<Document> collection = database.getCollection("test1");
Document doc = new Document();
doc.put("$set", new Document("STATUS", "normal2"));
UpdateResult updateOne = collection.updateOne(Filters.eq("STATUS", "normal"), doc);
long modifiedCount = updateOne.getModifiedCount();
System.out.println("modifiedCount = " + modifiedCount);
}
增加
/**
* 新增
*/
public void insert() {
MongoDatabase database = mongoClient.getDatabase("demo");
MongoCollection<Document> collection = database.getCollection("test1");
DemoObj demoObj = new DemoObj()
.setStatus("normal").setEndTime("2023-01-30 00:57:27")
.setCalledNo("138777999").setDoubleNum(99.99)
.setInsertDate(new Date()).setNumArr(Arrays.asList(1, 2, 3, 4))
.setStrArr(Arrays.asList("a", "c"));
Document doc = new Document();
doc.put("STATUS", demoObj.getStatus());
doc.put("END_TIME", demoObj.getEndTime());
doc.put("CALLED_NO", demoObj.getCalledNo());
doc.put("ERROR_MEMO", demoObj.getErrorMemo());
doc.put("DOUBLE_NUM", demoObj.getDoubleNum());
doc.put("INSERT_DATE", demoObj.getInsertDate());
doc.put("NUM_ARR", demoObj.getNumArr());
doc.put("STR_ARR", demoObj.getStrArr());
collection.insertOne(doc);
}
其他配置类
MongoSupportNew
package com.example.mongodb.driver.config;
import cn.hutool.core.util.ArrayUtil;
import com.mongodb.client.MongoCursor;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.lang.reflect.*;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/**
* @description 对 mongodb 查询后的实体进行转换
* 如果返回的结果集较少, 可用 alibaba.fastJson 的 JSON.parseObject(json, Class); (效率相对此方法较差) 可能更安全? 哈哈
* T 泛型的实体类 可以配合@FieldName 指定mongo 中的属性名 (默认情况以实体的属性名的大写 + _ 形式作为 Mongo 中的属性名)
* 支持的属性转换类型 基本类型 字符串 时间 带泛型的集合
* <p>
* 主要工作关注以下几个方法
* getEntity(T t) 通过mongo文档 获取 对象
* getEntityList(MongoCursor<Document> cursor) 通过mongo文档集合 获取 对象集合
* getDocument(T t) 通过 对象 获取 mongo文档
* getDocumentList(List<T> list) 通过 对象集合 获取 mongo文档集合
*/
@Slf4j
public class MongoSupportNew<T> {
/**
* mongodb 中的属性名集合, 其顺序与fields 数组中 一一对应
*/
private List<String> filedNameList;
/**
* 具体实体类的成员属性数组 set方法名
*/
private List<String> methodNameList;
/**
* 具体 set 方法形参的类型
*/
private List<Class<?>> fieldTypeList;
/**
* 用在效验List<> 中泛型是否正确
*/
private List<Type> typeList;
/**
* 类的记录泛型类型
*/
private Class<T> genericClass;
private static final char SEPARATOR = '_';
public MongoSupportNew() {
Class<T> clazz = getGenericClass();
Optional.ofNullable(clazz).orElseThrow(() -> new RuntimeException("当前MongoSupportNew类的使用有误, 子类继承时 需要声明父类中的泛型类型!"));
init(clazz);
}
private void init(Class<T> clazz) {
this.genericClass = clazz;
Field[] declaredFields = clazz.getDeclaredFields();
filedNameList = new ArrayList<>(declaredFields.length);
methodNameList = new ArrayList<>(declaredFields.length);
fieldTypeList = new ArrayList<>(declaredFields.length);
typeList = new ArrayList<>(declaredFields.length);
MapUnderscoreToCamelCase mapType = AnnotationUtils.getAnnotation(clazz, MapUnderscoreToCamelCase.class);
boolean underScoreToCameCase = true;
boolean upperCase = true;
if (mapType != null) {
underScoreToCameCase = mapType.value();
upperCase = mapType.upperCase();
}
for (Field field : declaredFields) {
String capitalize = StringUtils.capitalize(field.getName());
FieldName annotation = field.getAnnotation(FieldName.class);
if (annotation != null && !annotation.ignore()) {
filedNameList.add(annotation.value());
methodNameList.add("set" + capitalize);
fieldTypeList.add(field.getType());
typeList.add(field.getGenericType());
} else if (annotation == null) {
String fieldName = field.getName();
if (underScoreToCameCase) {
fieldName = toUnderScoreCase(fieldName);
}
if (upperCase) {
fieldName = fieldName.toUpperCase();
}
filedNameList.add(fieldName);
methodNameList.add("set" + capitalize);
fieldTypeList.add(field.getType());
typeList.add(field.getGenericType());
}
}
}
/**
* 对返回结果的情况进行结果集映射,取得封装后的实体对象
* 如果返回的结果集较少, 也可用 alibaba.fastJson 的 JSON.parseObject(json, Class); (效率相对此方法较差), 但故障率相对较低
*
* @param document 原始的Document类型实体
* @return list
*/
public T getEntity(Document document) {
T t = getGenericInstance();
fillFields(document, t);
return t;
}
/**
* 对返回多个结果的情况进行结果集映射,取得封装后的实体list
* 如果返回的结果集较少, 也可用 alibaba.fastJson 的 JSON.parseObject(json, Class); (效率相对此方法较差), 但故障率相对较低
*
* @param cursor 原始的Document类型实体
* @return list
*/
public List<T> getEntityList(MongoCursor<Document> cursor) {
if (cursor == null) {
return new ArrayList<>();
}
List<T> resList = new ArrayList<>();
LocalDateTime now = LocalDateTime.now();
while (cursor.hasNext()) {
Document document = cursor.next();
T t = getGenericInstance();
fillFields(document, t);
resList.add(t);
}
log.info("PRO MongoSupportNew.getEntityList 封装耗时:{}ms", Duration.between(now, LocalDateTime.now()).toMillis());
return resList;
}
/**
* 把 obj的数据转成 document
* 需关系下面两个注解
*
* @param t t
* @return document
* @see MapUnderscoreToCamelCase 驼峰命名的字段 转成 mongo的字段名
* @see FieldName 字段别名, 是否忽略转换
*/
public Document getDocument(T t) {
return processDocumentData(t);
}
/**
* 把 obj的数据转成 document
* 需关系下面两个注解
*
* @param list list
* @return documentList
* @see MapUnderscoreToCamelCase 驼峰命名的字段 转成 mongo的字段名
* @see FieldName 字段别名, 是否忽略转换
*/
public List<Document> getDocumentList(List<T> list) {
if (CollectionUtils.isEmpty(list)) {
return new ArrayList<>();
}
List<Document> voList = new ArrayList<>();
for (T t : list) {
Document document = processDocumentData(t);
voList.add(document);
}
return voList;
}
private Document processDocumentData(T t) {
Class<?> clazz = t.getClass();
Field[] fields = clazz.getDeclaredFields();
Document document = new Document();
for (Field field : fields) {
field.setAccessible(true);
String fieldName = field.getName();
Object invokeValue = null;
try {
Method method = t.getClass().getDeclaredMethod("get" + StringUtils.capitalize(fieldName));
method.setAccessible(true);
invokeValue = method.invoke(t);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
log.error("PRO MongoSupportNew.toDocumentList error fieldName:{}原因:", fieldName, e);
}
if (invokeValue == null) {
continue;
}
boolean upperCase = true;
boolean value = true;
MapUnderscoreToCamelCase toCamelCase = clazz.getAnnotation(MapUnderscoreToCamelCase.class);
if (toCamelCase != null) {
// 是否开启驼峰转下划线映射,默认开启
upperCase = toCamelCase.upperCase();
// 是否开启驼峰转下划线映射
value = toCamelCase.value();
}
FieldName annotation = field.getAnnotation(FieldName.class);
if (annotation != null && !annotation.ignore()) {
document.put(annotation.value(), invokeValue);
} else if (annotation == null) {
if (upperCase) {
fieldName = toUnderScoreCase(fieldName);
}
if (value) {
fieldName = fieldName.toUpperCase();
}
document.put(fieldName, invokeValue);
}
}
return document;
}
/**
* 填充实例类
*
* @param document mongoDB 查询到的原始 document 类型属性, 原始的封装实体类
* @param instance T 的实例
*/
private void fillFields(Document document, T instance) {
if (document == null) {
return;
}
int index = 0;
for (String methodName : methodNameList) {
String mongoFieldName = filedNameList.get(index);
Object mongoFieldValue = document.get(mongoFieldName);
Class<?> fieldTypeClazz = fieldTypeList.get(index);
Type type = typeList.get(index);
if (mongoFieldValue == null) {
index++;
continue;
} else if (mongoFieldValue instanceof String) {
invokeSet(instance, methodName, mongoFieldValue, fieldTypeClazz);
} else if (mongoFieldValue instanceof ObjectId) {
mongoFieldValue = ((ObjectId) mongoFieldValue).toHexString();
invokeSet(instance, methodName, mongoFieldValue, fieldTypeClazz);
} else if (mongoFieldValue instanceof List) {
invokeListSet(index, methodName, mongoFieldValue, fieldTypeClazz, (ParameterizedType) type, instance);
} else {
invokeSet(instance, methodName, mongoFieldValue, fieldTypeClazz);
}
index++;
}
}
private void invokeSet(T instance, String methodName, Object mongoFieldValue, Class<?> aClass) {
Method method = null;
try {
method = instance.getClass().getDeclaredMethod(methodName, aClass);
} catch (NoSuchMethodException e) {
log.error("PRO MongoSupportNew.invokeSet getDeclaredMethod error methodName:{},mongoFieldValue:{} 原因:", methodName, mongoFieldValue, e);
}
Optional.ofNullable(method).ifPresent(m -> {
try {
m.invoke(instance, mongoFieldValue);
} catch (Exception e) {
log.error("PRO MongoSupportNew.invokeSet invoke error methodName:{},mongoFieldValue:{} 原因:", methodName, mongoFieldValue, e);
}
});
}
/**
* 对数组进行转化
* 如果数组中参数的类型 与对象泛型中的类型不一致, 则 return
*
* @param index index
* @param methodName methodName
* @param list 仅支持 参数类型是 List 的子类
* @param clazz 参数的clazz
* @param parameterizedType 参数的parameterizedType
* @param instance 对象实例
*/
private void invokeListSet(int index, String methodName, Object list, Class<?> clazz, ParameterizedType parameterizedType, T instance) {
try {
if (List.class.isAssignableFrom(clazz)) {
for (Object obj : (List) list) {
if (!parameterizedType.getActualTypeArguments()[0].equals(obj.getClass())) {
return;
}
}
}
} catch (Exception e) {
log.error("PRO MongoSupportNew.tryTransArray error 原因:", e);
}
invokeSet(instance, methodName, list, fieldTypeList.get(index));
}
/**
* 用于获取类中的泛型类型实例, 类中的泛型类型需要具有无参构造器
*
* @return T 的实例
* @throws RuntimeException 方法执行时 genericClass 属性为空时, 尝试从类中获取, 对于继承 MongoSupportNew 而不声明其泛型的, 将会扔出异常
* @throws Exception 其他异常可能是 获取实例时 没有无参构造 或者未声明为 public
*/
private T getGenericInstance() {
T instance = null;
if (this.genericClass == null) {
this.genericClass = getGenericClass();
}
Optional.ofNullable(this.genericClass).orElseThrow(() -> new RuntimeException("当前MongoSupportNew类的使用有误, 子类继承时 需要声明父类中的泛型类型!"));
try {
instance = genericClass.getDeclaredConstructor().newInstance();
} catch (Exception e) {
log.error("PRO MongoSupportNew.getGenericInstance error 原因:", e);
}
return instance;
}
/**
* 驼峰命名法工具
*
* @return toCamelCase(" hello_world ") == "helloWorld"
* toCapitalizeCamelCase("hello_world") == "HelloWorld"
* toUnderScoreCase("helloWorld") = "hello_world"
*/
public static String toUnderScoreCase(String s) {
if (s == null) {
return null;
}
StringBuilder sb = new StringBuilder();
boolean upperCase = false;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
boolean nextUpperCase = true;
if (i < (s.length() - 1)) {
nextUpperCase = Character.isUpperCase(s.charAt(i + 1));
}
if ((i > 0) && Character.isUpperCase(c)) {
if (!upperCase || !nextUpperCase) {
sb.append(SEPARATOR);
}
upperCase = true;
} else {
upperCase = false;
}
sb.append(Character.toLowerCase(c));
}
return sb.toString();
}
private Class<T> getGenericClass() {
if (this.genericClass == null) {
ParameterizedType parameterizedType = (ParameterizedType) this.getClass().getGenericSuperclass();
Type[] types = parameterizedType.getActualTypeArguments();
if (ArrayUtil.isNotEmpty(types)) {
Type type = types[0];
try {
return (Class<T>) Class.forName(type.getTypeName());
} catch (ClassNotFoundException e) {
log.info("PRO MongoSupportNew.getGenericClass error 原因:", e);
}
}
}
return this.genericClass;
}
}
FieldName
package com.example.mongodb.driver.config;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
* @description 用于指定实体类中 成员属性所对应的 数据源的属性名
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface FieldName {
/**
* 指定所对应数据源的属性名
* @return
*/
String value() default "";
/**
* 是否忽略标注属性的填充,默认false,选true表示标注属性将不参与查询实体的转化,即不对标注属性赋值
* (所以自然就不会尝试获取标注属性)
* @return
*/
boolean ignore() default false;
}
MapUnderscoreToCamelCase
package com.example.mongodb.driver.config;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @description 对orm映射时的下划线转驼峰进行设置,对@FieldName映射,无需该注解,默认开启驼峰转下划线 和 转大写
* 如需关闭比映射转换可通过该注解进行操作
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MapUnderscoreToCamelCase {
/**
* 是否开启驼峰转下划线映射,默认开启
* @return
*/
boolean value() default true;
/**
* 是否转大写映射,默认开启
* @return
*/
boolean upperCase() default true;
}
DemoObj
package com.example.mongodb.driver.entity;
import com.example.mongodb.driver.config.FieldName;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
import java.util.List;
@Data
@Accessors(chain = true)
public class DemoObj {
@FieldName(value = "_id")
private String id;
private String status;
private String endTime;
private String calledNo;
private String errorMemo;
private Double doubleNum;
private Date insertDate;
private List<Integer> numArr;
private List<String> strArr;
}
2. Springboot 整合 mongodb
这里已经有成熟的文章案例了, 请大家去参考这里
https://so.csdn.net/so/search?q=mongo&t=blog&u=leilei1366615