当前位置:   article > 正文

MongoDB在Spring Boot中的使用方式_springboot mongodb配置文件

springboot mongodb配置文件

引言:

常见的数据存储库有很多,例如常见的Mysql、Redis、PostgreSql。但在当前敏捷开发的时代,MongoDB不需要设计数据库的结构,省去了设计步骤。在扩展时,支持水平扩展,直接添加新的服务器存储节点,使得系统能够轻松适应大规模的数据和高并发的访问。同样支持索引结构。读了下面的文章,有直截了当的例子,以及实战案例,大家可以直接Ctrl C+V,移植到在自己的代码中。另外例子中富含注释,大家只要花一些时间读一读,都会理解并且快速上手使用。那么下面就由我给大家讲讲。

MongoDB的形式:

MongoDB是基于Json数据结构的非关系型数据库,每一个Json文档有一个唯一的主键。

一、在SpringBoot项目中整合MongoDB,首先需要引入Pom文件依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
  • 1
  • 2
  • 3
  • 4

二、在yaml配置文件中进行配置,Spring会自动扫描配置文件,并且利用。

spring:
  data:
    mongodb:
      uri: mongodb://username:password@ip:port/dataBaseName
      # username、password、ip、port、dataBaseName名称需要写自己的
  • 1
  • 2
  • 3
  • 4
  • 5

三、使用上,我们定义一个Service类,里面封装好Mongo操作,在这里将这个Mongo Service开放给大家,方便大家使用:

//利用泛型传参,有效的使代码结构清晰,代码能够重复利用。另外,这里面的增、删、改、查操作都是在添加事务的情况下。
@Service
public class BaseService<T, ID> {

    private MongoTemplate mongoTemplate;

    private MongoClient client;

    @Resource
    public void setClient(MongoClient client) {
        this.client = client;
    }

    @Resource
    public void setMongoTemplate(MongoTemplate mongoTemplate) {
        this.mongoTemplate = mongoTemplate;
    }


    /**
     * MongoDB事务进行数据一致性的管理。涉及失败回滚操作
     * @param entity 添加数据
     * @param <S>    数据类型
     * @return 添加的数据
     */
    public <S extends T> S insert(S entity) {
        ClientSession clientSession = client.startSession();
        try {
            //开始事务
            clientSession.startTransaction();
            return mongoTemplate.save(entity);
        } catch (Exception e) {
            //回滚
            clientSession.abortTransaction();

            throw new BadRequestException(e.getMessage());
        }finally {
            // 无论是否发生异常,都要结束会话。释放资源
            clientSession.close();
        }
    }

    /**
     * 主键删除
     *
     * @param s   删除数据
     * @param id  主键id
     * @param <S> 删除数据类型
     * @return 删除结果
     */
    public <S extends T> DeleteResult deleteById(S s, ID id) {
        ClientSession clientSession = client.startSession();
        try {
            clientSession.startTransaction();
            Query query = Query.query(Criteria.where("_id").is(id));
            return mongoTemplate.remove(query, s.getClass());
        } catch (Exception e) {
            clientSession.abortTransaction();
            throw new BadRequestException(e.getMessage());
        }
    }


    /**
     * 批量删除
     *
     * @param s   删除数据
     * @param ids 主键id
     * @param <S> 删除数据类型
     * @return 删除结果
     */
    public <S extends T> DeleteResult deleteByIds(S s, List<ID> ids) {
        ClientSession clientSession = client.startSession();
        try {
            clientSession.startTransaction();
            Query query = Query.query(Criteria.where("_id").in(ids));
            return mongoTemplate.remove(query, s.getClass());
        } catch (Exception e) {
            clientSession.abortTransaction();
            throw new BadRequestException(e.getMessage());
        }
    }


    /**
     * 修改
     *
     * @param s   修改数据
     * @param id  主键id
     * @param <S> 修改数据类型
     * @return 修改结果
     */
    public <S extends T> UpdateResult update(S s, String id) {
        ClientSession clientSession = client.startSession();
        try {
            clientSession.startTransaction();
            Query query = Query.query(Criteria.where("_id").is(id));
            Update update = new Update();
            Field[] fields = s.getClass().getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                //获取属性
                String name = field.getName();
                //获取属性值
                Object value = field.get(s);
                if (ObjectUtils.allNotNull(value)) {
                    update.set(name, value);
                }
            }
            return mongoTemplate.updateFirst(query, update, s.getClass());
        } catch (Exception e) {
            clientSession.abortTransaction();
            throw new BadRequestException(e.getMessage());
        }
    }


    /**
     * 根据条件批量修改
     *
     * @param s      修改数据
     * @param query  修改条件
     * @param update 修改内容
     * @param <S>    修改数据类型
     * @return 修改结果
     */
    public <S extends T> UpdateResult updateByQuery(S s, Query query, Update update) {
        ClientSession clientSession = client.startSession();
        try {
            clientSession.startTransaction();
            return mongoTemplate.updateMulti(query, update, s.getClass());
        } catch (Exception e) {
            clientSession.abortTransaction();
            throw new BadRequestException(e.getMessage());
        }
    }

    /**
     * 分页查询
     *
     * @param dto       查询条件
     * @param s         结果类型
     * @param orderName 排序字段
     * @param <S>       类型
     * @return 分页数据
     */
    public <S extends T> PageVO pagination(PageRequestDTO dto, S s, String orderName, String orderRules) {
        return DataToolUtil.pagination(dto, s.getClass(), mongoTemplate, orderName, orderRules);
    }

    /**
     * 根据表id查询数据详情
     *
     * @param id  表id
     * @param s   数据类型
     * @param <S> 返回数据类型
     * @return 详情信息
     */
    public <S extends T> S getInfoById(ID id, S s) {
        return (S) mongoTemplate.findById(id, s.getClass());
    }

    /**
     * 根据多个表Id查询信息
     *
     * @param ids       多个表id
     * @param s         数据类型
     * @param <S>返回数据类型
     * @return 详情集合
     */
    public <S extends T> List<S> getInfoByIds(List<ID> ids, S s) {
        Query query = Query.query(Criteria.where("_id").in(ids));
        return (List<S>) mongoTemplate.find(query, s.getClass());
    }

    /**
     * 条件查询
     *
     * @param query 查询条件
     * @param s     返回结果
     * @param <S>   结果类型
     * @return 列表
     */
    public <S extends T> List<S> listByQuery(Query query, S s) {
        if (null != query) {
            return (List<S>) mongoTemplate.find(query, s.getClass());
        }
        return (List<S>) mongoTemplate.findAll(s.getClass());
    }

}
  • 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
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191

四、然后在要使用Mongo的业务类继承这个Service类,像这样:

@Service
@Slf4j
public class ****ServiceImpl extends BaseService<****Entity, String> implements ****Service {
	/**
     * 业务代码
     */
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

类似于使用Mybatis-Plus,要使用MP中Service层级的方法,需要继承ServiceImpl<Mapper, Entity>,像这样:

@Service
@Slf4j
public class ****ServiceImpl extends ServiceImpl<****Mapper, ****Entity> implements *****Service {
    /**
     * 业务代码
     */
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

五、具体在业务类中如何使用,我在这里举一些例子来进行说明,帮助大家最大程度的理解,并可以快速上手使用。

Query与Criteria的关联:

Criteria用来构建查询条件,类似于Mybatis-Plus中的wrapper。而Query是用来组合一个或者多个Criteria的。

在构建多条件查询时,使用下面的语句进行合并,合并有两种情况,一个是criteria是根据where构建,另一种是由and构建:

Query query = new Query();
Criteria criteria1 = Criteria.where("_id").is(id);
query.addCriteria(criteria1);//where形式

Criteria criteria2 = new Criteria();
criteria2.and("_id").is(id);//and形式
query.addCriteria(criteria2);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

where是单一条件查询,而and可以构建包含多个条件的查询。

在使用时与Mybatis-Plus一样需要对实体类进行操作,使用@Id注解指定主键Id,使用@Collation注解来对实体类指定名字。

像这样:

import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.annotation.Collation;
import java.io.Serializable;

@Data
@Collation("taskLog")
public class TaskLog implements Serializable {

    @Id
    private String logId;

	/*
	 * 其他属性
	 */
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
增:
@Service
@Slf4j
public class TaskLogServiceImpl extends BaseService<TaskLog, String> implements TaskLogService {

    @Autowired
	private MongoTemplate mongoTemplate;
    
    //增加一个
    @Override
    public void save(TaskLog tasklog) {
        this.insert(tasklog);//实际调用在mongoTemplate的save方法
    }
    
    //批量增加
    public void insertMany(List<TaskLog> tasklogs) {
    	mongoTemplate.insert(tasklogs, TaskLog.class);
	}
}
/**
 * mongoTemplate中,insert与save的区别:
 * insert()方法:如果tasklog中主键id已经存在了,会抛出异常,表示插入了重复的主键。如果该主键不存在,则会正常生成。
 * 用于生成新的数据
 * 
 * save()方法:如果主键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
删:
@Service
@Slf4j
public class TaskLogServiceImpl extends BaseService<TaskLog, String> implements TaskLogService {
    
    //根据主键id删除一个
    @Override
    public void delete(String logId) {
        this.deleteById(new TaskLog(), logId);
    }
    
    //根据主键id批量删除
    public void deleteMany(List<String> logIds) {
    	this.deleteByIds(new TaskLog(), logIds);
	}
}
/**
 * 实际使用的是mongoTemplate的remove()方法。
 * Query query = Query.query(Criteria.where("_id").is(id));
 * mongoTemplate.remove(query, s.getClass());
 *
 * Query query = Query.query(Criteria.where("_id").in(ids));
 * mongoTemplate.remove(query, s.getClass());
 */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
改:
@Service
@Slf4j
public class TaskLogServiceImpl extends BaseService<TaskLog, String> implements TaskLogService {
    
    //根据主键id修改
    @Override
    public void update(String logId) {
        this.update(data, data.getId());
    } 
}

   /**
   * 实际使用的是mongoTemplate中的updateFirst()方法
   * Query query = Query.query(Criteria.where("_id").is(id));
   * Update update = new Update();
   * Field[] fields = s.getClass().getDeclaredFields();
   * for (Field field : fields) {
   *     field.setAccessible(true);
   *     //获取属性
   *     String name = field.getName();
   *     //获取属性值
   *     Object value = field.get(s);
   *     if (ObjectUtils.allNotNull(value)) {
   *         update.set(name, value);
   *     }
   * }
   * 利用反射覆盖对应实体类中的每一个字段以及字段值。
   * mongoTemplate.updateFirst(query, update, s.getClass());
   */

  • 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
简单查:
@Service
@Slf4j
public class AnalysisStudyServiceImpl extends BaseService<AnalysisStudy, String> implements AnalysisStudyService {
    
    private MongoTemplate mongoTemplate;

    @Resource
    public void setMongoTemplate(MongoTemplate mongoTemplate) {
        this.mongoTemplate = mongoTemplate;
    }
    
    /**
    * 条件查询
    */
    @Override
    public void find(String patientId, String hospitalId) {
        Query query = Query.query(Criteria.where("patientId").is(patientId).and("hospitalId").is(hospitalId));
        List<AnalysisStudy> analysisStudyList = this.listByQuery(query, new AnalysisStudy());
    }  
    //底层使用的是mongoTemplate的find()方法,查询满足query条件的数据。
 	//如果构建的query为null,listByQuery()会调用findAll()方法,查询所有的数据
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

分页查询(重点):

@Service
@Slf4j
public class AnalysisStudyServiceImpl extends BaseService<AnalysisStudy, String> implements AnalysisStudyService {
    
    @Override
    public PageVO<AnalysisStudy> queryPage(PageRequestDTO pageRequest) {
        return this.pagination(pageRequest, new AnalysisStudy(), "studyDateTime", "asc");
        //第一个参数:查询条件
        //第二个参数:结果类型
        //第三个参数:排序字段
        //第四个参数:排序类型,升序ASC或者降序DESC
    }

}
//BaseService中封装的方法
public <S extends T> PageVO pagination(PageRequestDTO dto, S s, String orderName, String orderRules) {
        return DataToolUtil.pagination(dto, s.getClass(), mongoTemplate, orderName, orderRules);
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
//分页查询的使用封装成了一个工具类
@Slf4j
public class DataToolUtil {
    
	//防止工具类被实例化
    private DataToolUtil() {
        throw new IllegalStateException("Utility class");
    }			

    /**
     * 在mongodb中分页查询,通过 PageVO 对象封装了结果数量和查询结果
     *
     * @param dto           分页条件
     * @param tClass        返回数据类型
     * @param mongoTemplate 查询模板
     * @param <T>           返回数据
     * @param orderName     排序列名
     * @param orderRules    排序规则 asc 正序 desc 倒序
     * @return 分页结果
     */
    public static <T> PageVO<T> pagination(PageRequestDTO dto, Class<T> tClass,
                                           MongoTemplate mongoTemplate,
                                           String orderName, String orderRules) {
        Query query = handParam(dto);
        if (!ObjectUtils.allNotNull(dto.getCurrentPageNum())) {//没有请求的页码,默认是第一页
            dto.setCurrentPageNum(0);
        }
        if (!ObjectUtils.allNotNull(dto.getSize())) {//没有每页展示的数量,默认是展示10个
            dto.setSize(10);
        }

        //用给定的查询对象query来计算文档数量
        long count = mongoTemplate.count(query, tClass);

        /**
         * dto.getCurrentPageNum():用户请求的页码
         * dto.getSize():每页展示的数量
         * offset用来计算偏移量
         * 例如用户请求的是第2页,(2-1)=1。每页展示10个,1*10=10.则从第11个开始展示。
         */
        int offset = (dto.getCurrentPageNum() - 1) * dto.getSize();

        if (StringUtils.isNotEmpty(orderName) && StringUtils.isNotEmpty(orderRules)) {
            String asc = "asc";//升序排序
            String desc = "desc";//降序排序
            if (asc.equals(orderRules)) {
                //排序逻辑
                query.with(Sort.by(Sort.Order.asc(orderName)));
            }
            if (desc.equals(orderRules)) {
                //排序逻辑
                query.with(Sort.by(Sort.Order.desc(orderName)));
            }
        }
        // 分页逻辑
        /**
         * 举例:跳过10个,每页获取10个,展示11~20的数据。
         */
        query.skip(offset).limit(dto.getSize());
        log.info("querySql>>>>" + query);

        //find()用于获取符合query查询条件的文档列表.tClass是目标实体类
        List<T> list = mongoTemplate.find(query, tClass);

        //创建一个用于封装分页查询结果的对象
        PageVO<T> pageDTO = new PageVO<>();

        //给PageVO对象设置查询到的满足条件的数量。
        pageDTO.setTotalNum(count);

        //将查询到的结果设置到PageVO对象中。
        pageDTO.setResults(list);

        return pageDTO;
    }

    /**
     * 根据传入的查询条件进行 MongoDB 数据库的动态查询
     * @param dto
     * @return
     */
    private static Query handParam(PageRequestDTO dto) {
        Query query = new Query();//创建一个MongoDB查询对象
        List<ParamDTO> params = dto.getParams();//获取传入参数的查询条件列表
        Criteria criteria = new Criteria();//创建MongoDB的Criteria对象,用于构建查询条件
        params.forEach(param -> {//遍历查询条件列表
            String operator = param.getOperator();
            switch (operator) {
                //该语句构建了一个等于(eq)的查询条件,使用 is 方法指定字段等于给定的值。
                case "eq" -> criteria.and(param.getFiled()).is(param.getFiledValue());

                //regex表示使用正则表达式来进行查询,Pattern.compile()来构造一个正则表达式。这个正则表达式是‘^.*value.*$’
                //'^'和‘$’是正则表达式的起始和结束标识。‘.*表示匹配字符0次或者多次’
                //Pattern.CASE_INSENSITIVE表示不区分大小写
                case "like" -> criteria.and(param.getFiled()).regex(Pattern.compile("^.*" + param.getFiledValue()
                        + ".*$", Pattern.CASE_INSENSITIVE));

                case "le" -> criteria.and(param.getFiled()).lt(param.getFiledValue());
                case "ge" -> criteria.and(param.getFiled()).gt(param.getFiledValue());

                //转换成String类型的字符串列表
                case "in" -> {
                    List<String> values = JSON.parseArray(param.getFiledValue().toString(), String.class);
                    criteria.and(param.getFiled()).in(values);
                }

                case "between" -> {
                    if (param.getFiledValue() instanceof JSONArray) {
                        List<Object> list = JSON.parseArray(param.getFiledValue().toString(), Object.class);
                        if (CollUtil.isNotEmpty(list)) {
                            //gte:大于等于
                            //lte:小于等于
                            criteria.and(param.getFiled()).gte(list.get(0)).lte(list.get(1));
                        }
                    }
                }
                default -> {//没匹配任何一个case语句,默认执行default语句。
                }
            }
        });
        query.addCriteria(criteria);//将构建好的 Criteria 对象添加到查询对象中。
        return query;
    }
}
  • 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
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
@Data
public class PageVO<T> {

    private Long totalNum;//结果总数
    
    private List<T>results;//查询结果
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
@Data
@Schema(description = "分页查询条件")
public class PageRequestDTO {

    @Schema(description = "当前页数")
    private Integer currentPageNum;

    @Schema(description = "展示行数")
    private Integer size;
     
    @Schema(description = "查询条件")
    private List<ParamDTO> params;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/正经夜光杯/article/detail/834489
推荐阅读
相关标签
  

闽ICP备14008679号