1:MongoDB-maven仓库
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
2:配置文件application.properties添加MongoDB配置
#MongoDB
spring.data.mongodb.uri=mongodb://127.0.0.1:27017/test-MongoDB
3:通过MongoTemplate 简单的实现 增删改
import com.alibaba.fastjson.JSON;
import com.base.utils.AssertUtils;
import com.base.dto.other.BaseMongoUpdateDTO;
import com.base.util.ObjectParseUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.mongodb.core.BulkOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Objects;
/**
* author : wb
* File Name: GenericMongoCrudApplication
* Package Name: com.base.domain.mongo
* Date: 2022/5/16 13:35
* Copyright (c) 2022,All Rights Reserved.
*/
@Service
public class GenericMongoCrudApplication {
private Logger logger = LoggerFactory.getLogger(GenericMongoCrudApplication.class);
private MongoTemplate mongoTemplate;
public GenericMongoCrudApplication(MongoTemplate mongoTemplate){
this.mongoTemplate = mongoTemplate;
}
/**
* @description: 根据ID修改数据
* @param:
* @return:
**/
public void modifyDataAccordingToId(BaseMongoUpdateDTO updateDTO){
logger.info("变更数据入参:{}", JSON.toJSONString(updateDTO));
String primaryKey = ObjectParseUtils.parsePrimaryKey(updateDTO.getUpdateObj());
AssertUtils.hasText(primaryKey,"缺少变更数据唯一标识");
Object primaryVal = ObjectParseUtils.getFieldValueByName(primaryKey,updateDTO.getUpdateObj());
AssertUtils.notNull(primaryVal,"缺少变更数据唯一标识");
Query query = new Query();
query.addCriteria(Criteria.where(primaryKey).is(primaryVal));
logger.info("变更数据Query:{}", JSON.toJSONString(query));
Update update = new Update();
if(Objects.nonNull(updateDTO.getIncObj())){
List<String> attrs = ObjectParseUtils.getFiledName(updateDTO.getIncObj());
attrs.forEach(key->{
Object obj = ObjectParseUtils.getFieldValueByName(key,updateDTO.getIncObj());
if(Objects.nonNull(obj)){
Integer num = (Integer)obj;
update.inc(key, num == 0 ? 1 : num);
}
});
}
if(Objects.nonNull(updateDTO.getUpdateObj())){
List<String> attrs = ObjectParseUtils.getFiledName(updateDTO.getUpdateObj());
attrs.forEach(key->{
Object obj = ObjectParseUtils.getFieldValueByName(key,updateDTO.getUpdateObj());
String val = JSON.toJSONString(obj);
if(Objects.nonNull(obj) && !"null".equals(val)
&& StringUtils.isNotEmpty(val)){
update.set(key, obj);
}
});
}
logger.info("变更数据Update:{}", JSON.toJSONString(update));
this.mongoTemplate.updateFirst(query, update, updateDTO.getClazz());
}
/**
* @description: 插入数据
* @param:
* @return:
**/
public void insertData(Object clazz) {
logger.info("插入数据:{}",JSON.toJSONString(clazz));
this.mongoTemplate.insert(clazz);
}
/**
* @description: 删除消息数据
* @param: id
**/
public void delete(Query query,Class<?> clss) {
this.mongoTemplate.remove(query,clss);
}
/**
* @description 批量插入
* @param list
* @param clazz
*/
public void batchInsert(List<?> list, Class<?> clazz){
this.mongoTemplate.bulkOps(BulkOperations.BulkMode.UNORDERED, clazz).insert(list).execute();
}
}
4:调用封装简陋的通用类进行 增删改
@SpringBootTest
@RunWith(SpringRunner.class)
public class FailMqRecordApplicationTest {
@Autowired
private GenericMongoCrudApplication genericMongoCrudApplication;
/**
* 新增
*/
@Test
public void testAdd(){
MessageNoticePO po = new MessageNoticePO();
//po.set 省略N步set操作
this.genericMongoCrudApplication.insertData(po);
}
/**
* 批量新增
*/
@Test
public void testBatchAdd(){
List<MessageNoticePO> list = new ArrayList();
//po.set 省略N步set操作
this.genericMongoCrudApplication.batchInsert(list,MessageNoticePO.class);
}
/**
* 删除
*/
@Test
public void delete(){
Query query = new Query();
Criteria criteria = new Criteria();
criteria.and("_id").is(111);
query.addCriteria(criteria);
this.genericMongoCrudApplication.delete(query,MessageNoticePO.class);
}
/**
* 修改
*/
@Test
public boolean modifyData(){
BaseMongoUpdateDTO updateDTO = new BaseMongoUpdateDTO();
updateDTO.setClazz(MessageNoticePO.class);
MessageNoticePO no = new MessageNoticePO();
no.setId(111);
//省略N步要编辑的set操作
updateDTO.setUpdateObj(no);
updateDTO.setIncObj(new MessageNoticePO(retryCount));
this.genericMongoCrudApplication.modifyDataAccordingToId(updateDTO);
}
}
5:操作新增改的对象
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.redis.core.index.Indexed;
import java.io.Serializable;
import java.util.Date;
/**
* author : wb
* File Name: MessageNoticePO
* Package Name: com.base.domain.po.mongo
* Date: 2022/5/18 13:43
* Copyright (c) 2022,All Rights Reserved.
*/
@Document(collection = "message_notice")
public class MessageNoticePO implements Serializable {
/**
* '主键'
*/
@Id
private Long messageId;
/**
* 消息分组ID
**/
@Field
@Indexed
private Long messageGroupId;
//省略N个字段 以及set get
}
6:统一修改的入参对象
import java.io.Serializable;
/**
* author : wb
* File Name: BaseMongoUpdateDTO
* Package Name: com.base.dto.other
* Date: 2022/5/16 13:38
* Copyright (c) 2022,All Rights Reserved.
*/
public class BaseMongoUpdateDTO<T> implements Serializable {
/**
* 要修改的属性+值
*/
private T updateObj;
/**
* 要修改的具体操作对象
*/
private Class<?> clazz;
/**
* 需要特殊操作的
* 实现数字修改,添加or减少
* 例如 原本数据库是 1 这里传递 表字段 num = 2 表示当前字段取数据库原有的自动+2
*/
private T incObj;
public Class<?> getClazz() {
return clazz;
}
public void setClazz(Class<?> clazz) {
this.clazz = clazz;
}
public T getUpdateObj() {
return updateObj;
}
public void setUpdateObj(T updateObj) {
this.updateObj = updateObj;
}
public T getIncObj() {
return incObj;
}
public void setIncObj(T incObj) {
this.incObj = incObj;
}
}
7:查询实现简单简单阐述一下 MongoDB 对于 > < null not null的实现
mongoDB大于小于符号对应:
> 大于 $gt
< 小于 $lt
>= 大于等于 $gte
<= 小于等于 $lte
要查询同一个时间多个约束可能出现的error:
org.springframework.data.mongodb.InvalidMongoDbApiUsageException:
Due to limitations of the com.mongodb.BasicDocument, you can't add a second 'createdDate' expression specified as 'createdDate:
Document{{$lt=2018-01-06}}'. Criteria already contains 'createdDate: Document{{$gte=2017-12-31}}'.
解决办法:
要查询同一个字段多个约束需要用andOperator:
Query query = new Query(
Criteria.where("ip").is(ip)
.andOperator(
Criteria.where("createdDate").lt(endDate),
Criteria.where("createdDate").gte(startDate)));
MongoDB中的null 和not null
1、查询全部数据
> db.foo.find()
{ "_id" : ObjectId("544db3565d92133398a80daa"), "a" : 1, "name" :null}
{ "_id" : ObjectId("544db3565d92133398a80daa"), "a" : 1, "name" : "ZZZZZ" }
{ "_id" : ObjectId("5448ac1d735969c5f8386958"), "a" : 4 }
{ "_id" : ObjectId("544854ee3966c0424b50b46d"), "b" : 2 }
{ "_id" : ObjectId("544855ce735969c5f8386952"), "pwd" : "1212", "username" : "zhangsan
{ "_id" : ObjectId("544855e8735969c5f8386953"), "username" : "lisi", "pwd" : "222" }
{ "_id" : ObjectId("5448ae4c735969c5f838695d"), "username" : "tom", "age" : 23 }
2、查询name值为null
查询name为null,会查询出name字段不存的数据,如下:
> db.foo.find({name:{$in:[null]}})
{ "_id" : ObjectId("544db3565d92133398a80daa"), "a" : 1, "name" : null }
{ "_id" : ObjectId("5448ac1d735969c5f8386958"), "a" : 4 }
{ "_id" : ObjectId("544854ee3966c0424b50b46d"), "b" : 2 }
{ "_id" : ObjectId("544855ce735969c5f8386952"), "pwd" : "1212", "username" : "zhangsan
{ "_id" : ObjectId("544855e8735969c5f8386953"), "username" : "lisi", "pwd" : "222" }
查询也可用:> db.foo.find({name:null})
除第一条记录外,其它记录不存在name字段
3、name字段加上 $exists:true
> db.foo.find({name:{$in:[null],$exists:true}})
{ "_id" : ObjectId("544db3565d92133398a80daa"), "a" : 1, "name" :null}
4、查询name为不为空时(not null )
> db.foo.find({name:{$ne:null}})
{ "_id" : ObjectId("544db3b45d92133398a80dab"), "a" : 1, "name" : "zzz" }
8:简陋封装的通用查询类
import com.alibaba.fastjson.JSON;
import com.base.search.Pager;
import com.base.dto.other.PageListDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.List;
/**
* author : wb
* File Name: GenericMongoQuery
* Package Name: com.base.domain.mongo
* Date: 2022/5/17 15:24
* Copyright (c) 2022,All Rights Reserved.
*/
@Service
public class GenericMongoQuery {
private Logger logger = LoggerFactory.getLogger(GenericMongoQuery.class);
private MongoTemplate mongoTemplate;
public GenericMongoQuery(MongoTemplate mongoTemplate){
this.mongoTemplate = mongoTemplate;
}
/**
* @description: 根据主键id查询返回
* @param
**/
public <T> T getOne(Long id,Class<T> returnClszz){
return this.mongoTemplate.findById(id, returnClszz);
}
/**
* @description: 根据主键query查询返回
* @param
**/
public <T> T queryOne(Query query,Class<T> returnClszz){
return this.mongoTemplate.findOne(query, returnClszz);
}
/**
* @description: 根据主键query查询返回
* @param
**/
public <T> List<T> findList(Query query,Class<T> returnClszz){
return this.mongoTemplate.find(query,returnClszz);
}
/**
* @description: 分页查询
* @param query mongo查询query
* @param returnClszz 查询返回对象
**/
public <T> PageListDTO<T> page(Query query,
Integer pageNum,
Integer pageSize,
Class<T> returnClszz) {
if(pageNum != null && pageSize != null && pageSize > 0){
//设置起始数
query.skip((pageNum - 1) * pageSize);
//设置查询条数
query.limit(pageSize);
}
logger.info("mongo page param:{}", JSON.toJSONString(query));
List<T> list = this.mongoTemplate.find(query,returnClszz);
int count = count(query,returnClszz);
PageListDTO<T> pageList = new PageListDTO();
if(!CollectionUtils.isEmpty(list)){
pageList.setData(list);
}
Pager pager = new Pager();
pager.setRecordCount(count);
pager.setCurrentPage(pageNum);
pager.setPageSize(pageSize);
if(pageSize != null && pageSize > 0){
pager.setTotalPage(count % pageSize == 0 ? 1 : count / pageSize + 1);
}
pageList.setPager(pager);
return pageList;
}
/**
* @description: 统计总条数
* @param query
* @param collectionClass
**/
public Integer count(Query query, Class<?> collectionClass){
logger.info("count param:{}",JSON.toJSONString(query));
return (int) this.mongoTemplate.count(query, collectionClass);
}
}
9:测试调用
@SpringBootTest
@RunWith(SpringRunner.class)
public class FailMqRecordApplicationTest {
@Autowired
private GenericMongoQuery genericMongoQuery;
/**
* 按ID查询
*/
@Test
public MessageNoticePO getIdMessageNotice() {
return this.genericMongoQuery.getOne(11, MessageNoticePO.class);
}
/**
* 按对象查询
*/
@Test
public MessageNoticePO queryMessageNotice() {
Query query = new Query(); query.addCriteria(Criteria.where(ObjectParseUtils.getBeanFunName(MessageNoticeQuery::getMessageId)).is(messageId));
return this.genericMongoQuery.queryOne(query, MessageNoticePO.class);
}
/**
* 按传递的状态in + 名字查询
*/
@Test
public List<MessageNoticePO> queryMessageNoticeByQueueName(String queueName){
Query query = new Query();
ArrayList<Integer> states = new ArrayList<>();
states.add(1);
states.add(2);
query.addCriteria(Criteria.where(ObjectParseUtils.getBeanFunName(MessageNoticeQuery::getQueueName)).is(queueName) .and(ObjectParseUtils.getBeanFunName(MessageNoticeQuery::getState)).in(states));
return this.genericMongoQuery.findList(query,MessageNoticePO.class);
}
/**
* 查询指定属性是null 并且状态是指定值的
*/
@Test
public List<MessageNoticePO> queryMessageNoticeList(){
Query query = new Query();
Object obj = null;
Criteria criteria = new Criteria();
criteria.andOperator(new Criteria()
.andOperator( Criteria.where(ObjectParseUtils.getBeanFunName(MessageNoticeQuery::getMessageId)).in(obj), Criteria.where(ObjectParseUtils.getBeanFunName(MessageNoticeQuery::getRetryCount)).in(obj)));
criteria.and(ObjectParseUtils.getBeanFunName(MessageNoticeQuery::getState)).is(1);
query.addCriteria(criteria);
return this.genericMongoQuery.findList(query,MessageNoticePO.class);
}
/**
* 分页查询
*/
@Test
public PageList<MessageNoticePO> queryMessageNoticePageList(MessageNoticeQuery recordQuery){
Query query = new Query();
Criteria criteria = new Criteria();
if(recordQuery.isMessageIdIsNotNull()){
String messageId = ObjectParseUtils.getBeanFunName(MessageNoticeQuery::getMessageId);
criteria.andOperator(
Criteria.where(messageId).exists(true),
Criteria.where(messageId).ne(true));
}
if(recordQuery.getState() != null){
criteria.and(ObjectParseUtils.getBeanFunName(MessageNoticeQuery::getState)).is(recordQuery.getState());
}
if(recordQuery.getRequestType() != null){
criteria.and(ObjectParseUtils.getBeanFunName(MessageNoticeQuery::getRequestType)).is(recordQuery.getRequestType());
}
if(StringUtils.isNotEmpty(recordQuery.getQueueName())){
Pattern pattern = Pattern.compile("^.*" + recordQuery.getQueueName() + ".*$", Pattern.CASE_INSENSITIVE);
criteria.and(ObjectParseUtils.getBeanFunName(MessageNoticeQuery::getQueueName)).regex(pattern);
}
if(StringUtils.isNotEmpty(recordQuery.getRequestKey())){
Pattern pattern = Pattern.compile("^.*" + recordQuery.getRequestKey() + ".*$", Pattern.CASE_INSENSITIVE);
criteria.and(ObjectParseUtils.getBeanFunName(MessageNoticeQuery::getRequestKey)).regex(pattern);
}
query.addCriteria(criteria);
if(recordQuery.isCreateTimeSort()){
query.with(new Sort(Sort.Direction.DESC, ObjectParseUtils.getBeanFunName(MessageNoticeQuery::getCreateTime)));
}
if(recordQuery.isUpdateTimeSort()){
query.with(new Sort(Sort.Direction.DESC, ObjectParseUtils.getBeanFunName(MessageNoticeQuery::getLastUpdateTime)));
}
PageListDTO<MessageNoticePO> list = this.genericMongoQuery.page(query,
recordQuery.getPageNum(),
recordQuery.getPageSize(),
IFailMqRecordPO.class);
return 转换通用查询返回的分页数据给业务调用方(list);
}
}
10:新增改查都用的ObjectParseUtils 工具类
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
/**
* author : wb
* File Name: ObjectParseUtils
* Package Name: com.base.util
* Date: 2022/5/16 15:06
* Copyright (c) 2022,All Rights Reserved.
*/
public class ObjectParseUtils {
/**
* @description: 解析主鍵
**/
public static String parsePrimaryKey(Object o){
try{
Field[] fields = o.getClass().getDeclaredFields();
String primaryKey = "";
cas:for(Field fl:fields){
for(Annotation a :fl.getAnnotations()){
if(a instanceof org.springframework.data.annotation.Id){
primaryKey += fl.getName();
break cas;
}
}
}
return primaryKey;
}catch (Exception e){
throw new BusinessValidateException("数据操作失败");
}
}
/**
* @description: 解析对象的属性
* @param: o
* @return: list
**/
public static List<String> getFiledName(Object o) {
try{
Field[] fields = o.getClass().getDeclaredFields();
String[] fieldNames = new String[fields.length];
for (int i = 0; i < fields.length; i++) {
fieldNames[i] = fields[i].getName();
}
return Arrays.asList(fieldNames);
}catch (Exception e){
return new ArrayList<>();
}
}
/**
* @description: 解析对象属性对应的值
* @param: fieldNmae
* @param: o
* @return: object
**/
public static Object getFieldValueByName(String fieldName, Object o) {
try {
Method method = getFieldMethodByName(fieldName,o);
if(method == null){
throw new BusinessValidateException("操作异常");
}
return method.invoke(o, new Object[] {});
} catch (Exception e) {
throw new BusinessValidateException("操作异常");
}
}
/**
* 返回方法体
*/
private static Method getFieldMethodByName(String fieldName, Object o){
try{
String firstLetter = fieldName.substring(0, 1).toUpperCase();
String getter = "get" + firstLetter + fieldName.substring(1);
return o.getClass().getMethod(getter, new Class[] {});
}catch (Exception e){
return null;
}
}
/**
* @description: 解析对象属性对应的值
* @param: fieldNmae
* @param: o
* @return: object
**/
public static Integer getFieldIntValueByName(String fieldName, Object o) {
Integer num = null;
try {
Method method = getFieldMethodByName(fieldName,o);
if(method != null){
num = (Integer)method.invoke(o, new Object[] {});
}
} catch (Exception e) {
}
return num;
}
/**
* 根据匿名函数,获取bean 属性名称 <br>
* <li>BeanMapUtils.getBeanFunName(UserInfoDO::getCreateTime) = createTime</li>
* <li>BeanMapUtils.getBeanFunName(UserInfoDO::isOk) = ok</li>
*
* @date 2021-06-04 15:54
*/
public static <T> String getBeanFunName(Property<T, ?> property) {
try {
Method declaredMethod = property.getClass().getDeclaredMethod("writeReplace");
declaredMethod.setAccessible(Boolean.TRUE);
SerializedLambda serializedLambda = (SerializedLambda) declaredMethod.invoke(property);
String method = serializedLambda.getImplMethodName();
String attr;
if (method.startsWith("get")) {
attr = (char) (method.charAt(3) + 32) + method.substring(4);
} else if (method.startsWith("is")) {
attr = (char) (method.charAt(2) + 32) + method.substring(3);
} else {
throw new IllegalStateException("无法处理的方法名" + method);
}
return attr;
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
public interface Property<T, R> extends Function<T, R>, Serializable {
}
}
11:查询入参对象
import java.io.Serializable;
import java.util.Date;
public class MessageRecordQuery implements Serializable {
private String id;
private String userId;
private String queueName;
private Byte requestType;
private String requestKey;
private Byte state;
/**
* messageId 是否拼接Mongo $ne
*/
private boolean messageIdIsNotNull;
private boolean createTimeSort;
private boolean updateTimeSort;
private String messageId;
private Date lastUpdateTime;
private Date createTime;
private Integer retryCount;
/**
* 页码.
*/
private Integer pageNum;
/**
* 每页大小.
*/
private Integer pageSize;
/**
* 排序条件.
*/
private String orderBy;
//省略N set get
}
12:分页用到的 PageListDTO
import com.base.search.Pager;
import java.io.Serializable;
import java.util.List;
/**
* author : wb
* File Name: PageListDTO
* Package Name: com.base.dto.other
* Date: 2022/5/17 15:59
* Copyright (c) 2022,All Rights Reserved.
*/
public class PageListDTO<T> implements Serializable {
public List<T> data;
public Pager pager;
public List<T> getData() {
return data;
}
public void setData(List<T> data) {
this.data = data;
}
public Pager getPager() {
return pager;
}
public void setPager(Pager pager) {
this.pager = pager;
}
}
13: 分页用到的Pager
/**
* 分页信息,主要用于查询返回的对象.
*
*/
public class Pager extends PagerCondition {
/**
* serialVersionUID.
*/
private static final long serialVersionUID = 1L;
/**
* 总记录数
*/
private Integer recordCount;
/**
* 总页数
*/
private Integer totalPage;
public Pager() {
super();
}
public Pager(Integer currentPage, Integer pageSize, Integer recordCount) {
super(currentPage, pageSize);
this.recordCount = recordCount;
Integer totalPageSize = recordCount / pageSize;
Integer remailder = recordCount % pageSize;
// 如果总记录数与每页显示条数的余数大于0,总页数加1
if (remailder > 0) {
totalPageSize = totalPageSize + 1;
}
totalPage = totalPageSize;
}
/**
* 分页查询时使用
*/
public Pager(PagerCondition pageCondition, Integer recordCount) {
setCurrentPage(pageCondition.getCurrentPage());
setPageSize(pageCondition.getPageSize());
setRecordCount(recordCount);
setTotalPage((recordCount + getPageSize() - 1) / getPageSize());
}
public Integer getRecordCount() {
return recordCount;
}
public void setRecordCount(Integer recordCount) {
this.recordCount = recordCount;
}
public Integer getTotalPage() {
return totalPage;
}
public void setTotalPage(Integer totalPage) {
this.totalPage = totalPage;
}
}