對(duì)spring中mongoTemplate的封裝

1 場(chǎng)景

spring集成mongo后政冻,可以使用mongoTemplate進(jìn)行mongo操作。

1.1 當(dāng)前問(wèn)題

mongoTemplate寫(xiě)入或者更新數(shù)據(jù)庫(kù)時(shí)贼陶,操作對(duì)象可以是Java bean實(shí)體類(lèi)也可以是Map對(duì)象卖宠。

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

例子如下:

  • 使用map作為存儲(chǔ)對(duì)象
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","張三");
//年齡為數(shù)字:18
map1.put("age",18);
mongoTemplate.insert(map1,"t_user_info");
  • 使用java對(duì)象作為存儲(chǔ)對(duì)象
@Data
public class UserInfo{
    private String userName;
    private Integer age;
}
UserInfo userInfo=new UserInfo();
userInfo.setUserName("張三");
//年齡為數(shù)字:18
userInfo.setAge(18);
mongoTemplate.insert(userInfo,"t_user_info");

因?yàn)閙ongo中,同一個(gè)表中二打,同一個(gè)字段不同數(shù)據(jù)字段類(lèi)型可以不一樣掂榔。即如上示例继效,使用map作為存儲(chǔ)對(duì)象,不同的記錄衅疙,字段age類(lèi)型莲趣,既可以是字符類(lèi)型“18歲”鸳慈,也可以是數(shù)字類(lèi)型18饱溢,數(shù)據(jù)庫(kù)中數(shù)據(jù)格式如下:

/* 1 */
{
    "_id" : ObjectId("5f8107c5f34b265ecada41ff"),
    "userName" : "張三",
    "age" : "18歲"
}

/* 2 */
{
    "_id" : ObjectId("5f8107c5f34b265ecada4200"),
    "userName" : "張三",
    "age" : 18
}

如只使用java中的bean對(duì)象作為存儲(chǔ)對(duì)象,則同一個(gè)表中所有的數(shù)據(jù)的數(shù)據(jù)類(lèi)型均一致走芋,如下:

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

1.2 選型

java中使用map自定義bean都可以作為mongo的存儲(chǔ)對(duì)象绩郎。我們對(duì)這兩種方式進(jìn)行對(duì)比:

1.2.1 map作為存儲(chǔ)對(duì)象
  • 優(yōu)點(diǎn)

(1)代碼簡(jiǎn)單,定義map即可存儲(chǔ)對(duì)象

(2)不用為每個(gè)專(zhuān)門(mén)的表定義java對(duì)象

  • 缺點(diǎn)

(1)每次操作數(shù)據(jù)時(shí)翁逞,手動(dòng)定義map的鍵值肋杖。手寫(xiě)代碼,容易寫(xiě)錯(cuò)挖函。字段名容易出錯(cuò)状植,導(dǎo)致名稱(chēng)相似的字段出現(xiàn);字段值的類(lèi)型容易出錯(cuò)怨喘,導(dǎo)致一個(gè)同一個(gè)字段不同數(shù)據(jù)的字段類(lèi)型不一致津畸,此問(wèn)題是非常致命的。

(2)mongo增加字段必怜,無(wú)需更改表結(jié)構(gòu)肉拓。需額外維護(hù)mongo表結(jié)構(gòu)說(shuō)明文檔,人工通過(guò)文檔定義字段的名稱(chēng)梳庆、含義和類(lèi)型暖途。

(3)mongo中增加字段無(wú)需更改表結(jié)構(gòu),不需要DBA授權(quán)膏执,對(duì)mongo字段如果無(wú)限制驻售,如果管理不善,容易出現(xiàn)字段的數(shù)量暴增更米。

(4)需要人工定義表名稱(chēng)的常量集合欺栗。

1.2.1 自定義bean作為存儲(chǔ)對(duì)象
  • 優(yōu)點(diǎn)

(1)通過(guò)mongo表的映射java對(duì)象,可以清晰知道m(xù)ongo表的表結(jié)構(gòu)(字段名稱(chēng)、字段類(lèi)型纸巷、表名稱(chēng))

(2)不需要人工定義表名稱(chēng)的常量集合镇草。映射對(duì)象上通過(guò)注解或者默認(rèn)名稱(chēng),可以自動(dòng)獲取mongo表的名稱(chēng)瘤旨。

  • 缺點(diǎn)

(1)需要手動(dòng)為每個(gè)表建立專(zhuān)門(mén)的java對(duì)象

(2)原始的mongoTemplate中api接口無(wú)法限制所有的操作必須使用java對(duì)象進(jìn)行操作梯啤。需要人工進(jìn)行接口的二次封裝(且只能使用封裝的接口)。

經(jīng)過(guò)對(duì)比存哲,我們選擇使用自定義bean作為存儲(chǔ)對(duì)象因宇。

2 版本

springBoot:2.2.9.RELEASE

mongodb:4.0

3 步驟

3.1 基礎(chǔ)代碼

(1)定義java bean的父類(lèi)

import lombok.Data;

import java.io.Serializable;

/**
 * mongo的bean定義
 * <br>實(shí)現(xiàn)此接口的類(lèi),可以進(jìn)行mongo數(shù)據(jù)庫(kù)操作
 **/
@Data
public class MongoBean implements Serializable {
    
    /**
     * mongo主鍵(對(duì)應(yīng)數(shù)據(jù)庫(kù)中自動(dòng)生成的字段:_id)
     * 此字段不可設(shè)置(mongo自己生成)
     */
    private String id;
    
    /**
     * 刪除標(biāo)志(false:正常祟偷;true:刪除)
     */
    private Boolean delFlag;
    
    /**
     * 創(chuàng)建人ID
     */
    private String createUserId;
    
    /**
     * 創(chuàng)建人姓名
     */
    private String createUserName;
    
    /**
     * 創(chuàng)建時(shí)間
     */
    private Integer createDate;
    
    /**
     * 更新人ID
     */
    private String updateUserId;
    
    /**
     * 更新人姓名
     */
    private String updateUserName;
    
    /**
     * 更新時(shí)間
     */
    private Integer updateDate;
}

所有的mongo表映射的java對(duì)象察滑,均需繼承此父類(lèi)。

如下:

import org.springframework.data.mongodb.core.mapping.Document;

/**
 * 用戶(hù)表
 */
@Document("t_user_info")
public class TUserInfo extends MongoBean {
    
    /**
     * 用戶(hù)名
     */
    private String userName;
    
    /**
     * 年齡
     */
    private Integer age;
}

(2)定義mongo游標(biāo)執(zhí)行器

/**
 * mongo游標(biāo)執(zhí)行器
 **/
public interface Executor<T> {
    
    /**
     * 執(zhí)行
     * @param cModel 執(zhí)行實(shí)體類(lèi)
     * @return void 
     */
    void invoke(T cModel) throws Exception;
}

3.2 部分API封裝

3.2.1 插入數(shù)據(jù)
/**
 * 插入數(shù)據(jù)
 * @param objectToSave 
 * @return T 
 */
public <T extends MongoBean> T insert(T objectToSave){
    return mongoTemplate.insert(objectToSave);
}

/**
 * 批量插入數(shù)據(jù)
 * @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 查詢(xún)數(shù)據(jù)
/**
 * 查詢(xún)滿(mǎn)足條件記錄
 * @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);
}

/**
 * 查詢(xún)第一條
 * @param query 
 * @param entityClass 
 * @return T 
 */
public <T extends MongoBean> T findOne(Query query, Class<T> entityClass){
    return mongoTemplate.findOne(query,entityClass);
}

/**
 * 根據(jù)主鍵查詢(xún)
 * @param id 
 * @param entityClass 
 * @return T 
 */
public <T extends MongoBean> T findById(Object id, Class<T> entityClass){
    return mongoTemplate.findById(id,entityClass);
}

/**
 * 查詢(xún)總數(shù)
 * @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 更新數(shù)據(jù)
/**
 * 更新第一條
 * @param query 查詢(xún)條件
 * @param mongoBean 要更新的實(shí)體
 * @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 查詢(xún)條件
 * @param mongoBean 要更新的實(shí)體
 * @param updateFields 要更新的字段(有參數(shù)時(shí)修肠,更新指定的字段贺辰;無(wú)此參數(shù)時(shí),更新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 游標(biāo)查詢(xún)
/**
 * 獲取mongo游標(biāo)(需要手動(dòng)關(guān)閉)
 * @param query 查詢(xún)對(duì)象
 * @param entityClass 查詢(xún)實(shí)體
 * @param batchSize 批次大星妒(默認(rèn)1000饲化,需大于0)
 * @param pageNum 當(dāng)前頁(yè)數(shù)
 * @param pageSize 每頁(yè)大小
 * @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());
    ////----------填充游標(biāo)屬性----------
    //(1)游標(biāo)不超時(shí)
    findIterable.noCursorTimeout(true);
    //(2)批次拉取大小(默認(rèn)1000)
    if(batchSize==null || batchSize<=0){
        batchSize=DEFAULT_CURSOR_BATCH_SIZE;
    }
    findIterable.batchSize(batchSize);
    //(3)排序
    findIterable.sort(query.getSortObject());
    //(4)跳過(guò)記錄數(shù)
    if(pageNum!=null && pageSize!=null){
        findIterable.skip((pageNum - 1) * pageSize);
        findIterable.limit(pageSize);
    }
    
    return findIterable.cursor();
}

/**
 * 執(zhí)行游標(biāo)查詢(xún)
 * @param query 查詢(xún)器
 * @param entityClass 查詢(xún)實(shí)體
 * @param batchSize 批次大小
 * @param pageNum 當(dāng)前頁(yè)
 * @param pageSize 每次大小
 * @param executor 執(zhí)行器
 * @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;
    }
}

----------完整代碼吗伤,可私信聯(lián)系博主----------

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末吃靠,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子足淆,更是在濱河造成了極大的恐慌巢块,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件巧号,死亡現(xiàn)場(chǎng)離奇詭異容为,居然都是意外死亡竿奏,警方通過(guò)查閱死者的電腦和手機(jī)挤忙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)逊桦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人卜高,你說(shuō)我怎么就攤上這事弥姻。” “怎么了掺涛?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵庭敦,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我薪缆,道長(zhǎng)秧廉,這世上最難降的妖魔是什么伞广? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮疼电,結(jié)果婚禮上嚼锄,老公的妹妹穿的比我還像新娘。我一直安慰自己蔽豺,他們只是感情好区丑,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著修陡,像睡著了一般沧侥。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上魄鸦,一...
    開(kāi)封第一講書(shū)人閱讀 51,554評(píng)論 1 305
  • 那天宴杀,我揣著相機(jī)與錄音,去河邊找鬼拾因。 笑死旺罢,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的盾致。 我是一名探鬼主播主经,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼荣暮,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼庭惜!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起穗酥,我...
    開(kāi)封第一講書(shū)人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤护赊,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后砾跃,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體骏啰,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年抽高,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了判耕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡翘骂,死狀恐怖壁熄,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情碳竟,我是刑警寧澤草丧,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站莹桅,受9級(jí)特大地震影響昌执,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一懂拾、第九天 我趴在偏房一處隱蔽的房頂上張望煤禽。 院中可真熱鬧,春花似錦岖赋、人聲如沸呜师。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)汁汗。三九已至,卻和暖如春栗涂,著一層夾襖步出監(jiān)牢的瞬間知牌,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工斤程, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留角寸,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓忿墅,卻偏偏與公主長(zhǎng)得像扁藕,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子疚脐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355