对spring中mongoTemplate的封装

作者: 水煮鱼又失败了 | 来源:发表于2020-10-11 16:07 被阅读0次

    1 场景

    spring集成mongo后,可以使用mongoTemplate进行mongo操作。

    1.1 当前问题

    mongoTemplate写入或者更新数据库时,操作对象可以是Java bean实体类也可以是Map对象

    <T> T insert(T objectToSave);
    <T> T insert(T objectToSave, String collectionName);
    

    例子如下:

    • 使用map作为存储对象
    Map<String,Object> map=new HashMap<>();
    map.put("userName","张三");
    //年龄为字符串:18岁
    map.put("age","18岁");
    mongoTemplate.insert(map,"t_user_info");
    
    Map<String,Object> map1=new HashMap<>();
    map1.put("userName","张三");
    //年龄为数字:18
    map1.put("age",18);
    mongoTemplate.insert(map1,"t_user_info");
    
    • 使用java对象作为存储对象
    @Data
    public class UserInfo{
        private String userName;
        private Integer age;
    }
    
    UserInfo userInfo=new UserInfo();
    userInfo.setUserName("张三");
    //年龄为数字:18
    userInfo.setAge(18);
    mongoTemplate.insert(userInfo,"t_user_info");
    

    因为mongo中,同一个表中,同一个字段不同数据字段类型可以不一样。即如上示例,使用map作为存储对象,不同的记录,字段age类型,既可以是字符类型“18岁”,也可以是数字类型18,数据库中数据格式如下:

    /* 1 */
    {
        "_id" : ObjectId("5f8107c5f34b265ecada41ff"),
        "userName" : "张三",
        "age" : "18岁"
    }
    
    /* 2 */
    {
        "_id" : ObjectId("5f8107c5f34b265ecada4200"),
        "userName" : "张三",
        "age" : 18
    }
    

    如只使用java中的bean对象作为存储对象,则同一个表中所有的数据的数据类型均一致,如下:

    /* 1 */
    {
        "_id" : ObjectId("5f8107c5f34b265ecada4211"),
        "userName" : "张三",
        "age" : 18
    }
    

    1.2 选型

    java中使用map自定义bean都可以作为mongo的存储对象。我们对这两种方式进行对比:

    1.2.1 map作为存储对象
    • 优点

    (1)代码简单,定义map即可存储对象

    (2)不用为每个专门的表定义java对象

    • 缺点

    (1)每次操作数据时,手动定义map的键值。手写代码,容易写错。字段名容易出错,导致名称相似的字段出现;字段值的类型容易出错,导致一个同一个字段不同数据的字段类型不一致,此问题是非常致命的。

    (2)mongo增加字段,无需更改表结构。需额外维护mongo表结构说明文档,人工通过文档定义字段的名称、含义和类型

    (3)mongo中增加字段无需更改表结构,不需要DBA授权,对mongo字段如果无限制,如果管理不善,容易出现字段的数量暴增

    (4)需要人工定义表名称的常量集合。

    1.2.1 自定义bean作为存储对象
    • 优点

    (1)通过mongo表的映射java对象,可以清晰知道mongo表的表结构(字段名称、字段类型、表名称)

    (2)不需要人工定义表名称的常量集合。映射对象上通过注解或者默认名称,可以自动获取mongo表的名称。

    • 缺点

    (1)需要手动为每个表建立专门的java对象

    (2)原始的mongoTemplate中api接口无法限制所有的操作必须使用java对象进行操作。需要人工进行接口的二次封装(且只能使用封装的接口)。

    经过对比,我们选择使用自定义bean作为存储对象。

    2 版本

    springBoot:2.2.9.RELEASE

    mongodb:4.0

    3 步骤

    3.1 基础代码

    (1)定义java bean的父类

    import lombok.Data;
    
    import java.io.Serializable;
    
    /**
     * mongo的bean定义
     * <br>实现此接口的类,可以进行mongo数据库操作
     **/
    @Data
    public class MongoBean implements Serializable {
        
        /**
         * mongo主键(对应数据库中自动生成的字段:_id)
         * 此字段不可设置(mongo自己生成)
         */
        private String id;
        
        /**
         * 删除标志(false:正常;true:删除)
         */
        private Boolean delFlag;
        
        /**
         * 创建人ID
         */
        private String createUserId;
        
        /**
         * 创建人姓名
         */
        private String createUserName;
        
        /**
         * 创建时间
         */
        private Integer createDate;
        
        /**
         * 更新人ID
         */
        private String updateUserId;
        
        /**
         * 更新人姓名
         */
        private String updateUserName;
        
        /**
         * 更新时间
         */
        private Integer updateDate;
    }
    

    所有的mongo表映射的java对象,均需继承此父类。

    如下:

    import org.springframework.data.mongodb.core.mapping.Document;
    
    /**
     * 用户表
     */
    @Document("t_user_info")
    public class TUserInfo extends MongoBean {
        
        /**
         * 用户名
         */
        private String userName;
        
        /**
         * 年龄
         */
        private Integer age;
    }
    

    (2)定义mongo游标执行器

    /**
     * mongo游标执行器
     **/
    public interface Executor<T> {
        
        /**
         * 执行
         * @param cModel 执行实体类
         * @return void 
         */
        void invoke(T cModel) throws Exception;
    }
    

    3.2 部分API封装

    3.2.1 插入数据
    /**
     * 插入数据
     * @param objectToSave 
     * @return T 
     */
    public <T extends MongoBean> T insert(T objectToSave){
        return mongoTemplate.insert(objectToSave);
    }
    
    /**
     * 批量插入数据
     * @param batchToSave 
     * @return java.util.Collection<T> 
     */
    public <T extends MongoBean> Collection<T> insertAll(Collection<? extends T> batchToSave){
        return mongoTemplate.insertAll(batchToSave);
    }
    
    3.2.2 条件删除
    /**
     * 条件删除
     * @param query
     * @param entityClass
     * @return
     */
    public <T extends MongoBean> DeleteResult remove(Query query, Class<T> entityClass){
        return mongoTemplate.remove(query,entityClass);
    }
    
    3.2.3 查询数据
    /**
     * 查询满足条件记录
     * @param query
     * @param entityClass
     * @return java.util.List<T>
     */
    public <T extends MongoBean> List<T> find(Query query, Class<T> entityClass){
        return mongoTemplate.find(query,entityClass);
    }
    
    /**
     * 查询第一条
     * @param query 
     * @param entityClass 
     * @return T 
     */
    public <T extends MongoBean> T findOne(Query query, Class<T> entityClass){
        return mongoTemplate.findOne(query,entityClass);
    }
    
    /**
     * 根据主键查询
     * @param id 
     * @param entityClass 
     * @return T 
     */
    public <T extends MongoBean> T findById(Object id, Class<T> entityClass){
        return mongoTemplate.findById(id,entityClass);
    }
    
    /**
     * 查询总数
     * @param query 
     * @param entityClass 
     * @return long 
     */
    public <T extends MongoBean> long count(Query query, Class<T> entityClass){
        return mongoTemplate.count(query,entityClass);
    }
    
    3.2.4 更新数据
    /**
     * 更新第一条
     * @param query 查询条件
     * @param mongoBean 要更新的实体
     * @param updateFields 
     * @return com.mongodb.client.result.UpdateResult 
     */
    public <T extends MongoBean> UpdateResult extUpdateFirst(Query query, T mongoBean,String... updateFields) throws Exception{
        Update update=getUpdateFromBean(mongoBean,updateFields);
        return mongoTemplate.updateFirst(query,update,mongoBean.getClass());
    }
    
    /**
     * 批量更新
     * @param query 查询条件
     * @param mongoBean 要更新的实体
     * @param updateFields 要更新的字段(有参数时,更新指定的字段;无此参数时,更新mongoBean所有不为空的字段)
     * @return com.mongodb.client.result.UpdateResult 
     */
    public <T extends MongoBean> UpdateResult extUpdateMulti(Query query, T mongoBean,String... updateFields) throws Exception{
        Update update=getUpdateFromBean(mongoBean,updateFields);
        return mongoTemplate.updateMulti(query,update,mongoBean.getClass());
    }
    
    3.2.5 游标查询
    /**
     * 获取mongo游标(需要手动关闭)
     * @param query 查询对象
     * @param entityClass 查询实体
     * @param batchSize 批次大小(默认1000,需大于0)
     * @param pageNum 当前页数
     * @param pageSize 每页大小
     * @return com.mongodb.client.MongoCursor<org.bson.Document> 
     */
    <T extends MongoBean> MongoCursor<Document> extGetMongoCursor(Query query, Class<T> entityClass, Integer batchSize, Integer pageNum, Integer pageSize){
        if(query==null || entityClass==null){
            return null;
        }
        MongoCollection<Document> collection=mongoTemplate.getCollection(mongoTemplate.getCollectionName(entityClass));
        FindIterable<Document> findIterable=collection.find(query.getQueryObject());
        ////----------填充游标属性----------
        //(1)游标不超时
        findIterable.noCursorTimeout(true);
        //(2)批次拉取大小(默认1000)
        if(batchSize==null || batchSize<=0){
            batchSize=DEFAULT_CURSOR_BATCH_SIZE;
        }
        findIterable.batchSize(batchSize);
        //(3)排序
        findIterable.sort(query.getSortObject());
        //(4)跳过记录数
        if(pageNum!=null && pageSize!=null){
            findIterable.skip((pageNum - 1) * pageSize);
            findIterable.limit(pageSize);
        }
        
        return findIterable.cursor();
    }
    
    /**
     * 执行游标查询
     * @param query 查询器
     * @param entityClass 查询实体
     * @param batchSize 批次大小
     * @param pageNum 当前页
     * @param pageSize 每次大小
     * @param executor 执行器
     * @return void 
     */
    public <T extends MongoBean> void extCursorQueryExe(Query query, Class<T> entityClass, Integer batchSize, Integer pageNum, Integer pageSize, Executor<T> executor) throws Exception{
        if(executor==null){
            return ;
        }
        try (MongoCursor<Document> cursor = this.extGetMongoCursor(query,entityClass,batchSize,pageNum,pageSize)) {
            if(cursor==null){
                return ;
            }
            T model;
            while (cursor.hasNext()) {
                model = mongoConverter.read(entityClass, cursor.next());
                executor.invoke(model);
            }
        } catch (Exception e) {
            throw e;
        }
    }
    

    ----------完整代码,可私信联系博主----------

    相关文章

      网友评论

        本文标题:对spring中mongoTemplate的封装

        本文链接:https://www.haomeiwen.com/subject/xruepktx.html