MongoDB在Spring Boot中的使用方式

本文介绍了如何在SpringBoot项目中集成MongoDB,包括添加依赖、配置连接、使用MongoTemplate进行CRUD操作,以及涉及事务管理和分页查询的示例。通过实例代码展示了如何在实际项目中操作MongoDB,帮助开发者快速上手。
摘要由CSDN通过智能技术生成

引言:

常见的数据存储库有很多,例如常见的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>

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

spring:
  data:
    mongodb:
      uri: mongodb://username:password@ip:port/dataBaseName
      # username、password、ip、port、dataBaseName名称需要写自己的

三、使用上,我们定义一个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());
    }

}

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

@Service
@Slf4j
public class ****ServiceImpl extends BaseService<****Entity, String> implements ****Service {
	/**
     * 业务代码
     */
}

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

@Service
@Slf4j
public class ****ServiceImpl extends ServiceImpl<****Mapper, ****Entity> implements *****Service {
    /**
     * 业务代码
     */
}

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

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);

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;

	/*
	 * 其他属性
	 */
}
增:
@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已经存在,则会覆盖掉原来的数据。
 * 用于更新数据
 */
删:
@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());
 */
改:
@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());
   */

简单查:
@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()方法,查询所有的数据
}

分页查询(重点):

@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);
}

//分页查询的使用封装成了一个工具类
@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;
    }
}
@Data
public class PageVO<T> {

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

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

    @Schema(description = "展示行数")
    private Integer size;
     
    @Schema(description = "查询条件")
    private List<ParamDTO> params;
}
  • 10
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Spring Boot使用MongoDB切换数据库,可以通过配置多个MongoDB连接信息和使用`MongoTemplate`的构造函数来实现。下面是具体的步骤: 1. 配置多个MongoDB连接信息 在`application.properties`文件,配置多个MongoDB连接信息,例如: ``` # MongoDB1 spring.data.mongodb.uri=mongodb://localhost:27017/db1 spring.data.mongodb.database=db1 spring.data.mongodb.authentication-database=admin spring.data.mongodb.username=user1 spring.data.mongodb.password=password1 # MongoDB2 spring.data.mongodb2.uri=mongodb://localhost:27017/db2 spring.data.mongodb2.database=db2 spring.data.mongodb2.authentication-database=admin spring.data.mongodb2.username=user2 spring.data.mongodb2.password=password2 ``` 2. 创建多个MongoTemplate 创建多个MongoTemplate,并在构造函数指定要使用数据库,例如: ``` @Configuration public class MongoDbConfig { @Autowired private MongoProperties mongoProperties; @Bean(name = "mongoDb1Template") public MongoTemplate mongoDb1Template() { MongoDbFactory mongoDbFactory = new SimpleMongoDbFactory(new MongoClientURI(mongoProperties.getUri() + "/db1")); return new MongoTemplate(mongoDbFactory); } @Bean(name = "mongoDb2Template") public MongoTemplate mongoDb2Template() { MongoDbFactory mongoDbFactory = new SimpleMongoDbFactory(new MongoClientURI(mongoProperties.getUri() + "/db2")); return new MongoTemplate(mongoDbFactory); } } ``` 3. 在DAO层使用不同的MongoTemplate 在DAO层使用不同的MongoTemplate来操作不同的数据库,例如: ``` @Repository public class UserRepository { @Autowired @Qualifier("mongoDb1Template") private MongoTemplate mongoDb1Template; @Autowired @Qualifier("mongoDb2Template") private MongoTemplate mongoDb2Template; public void saveUser1(User user) { mongoDb1Template.save(user); } public void saveUser2(User user) { mongoDb2Template.save(user); } } ``` 这样就可以在DAO层使用不同的MongoTemplate来操作不同的数据库了。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值