mybatisplus?復(fù)合主鍵(多主鍵)?CRUD示例詳解
mybatisplus 復(fù)合主鍵CRUD
需求描述
最近接到個(gè)挺有意思的需求,做用戶觀看學(xué)習(xí)視頻時(shí)長(zhǎng)的一個(gè)數(shù)據(jù)埋點(diǎn)
儲(chǔ)存用戶觀看視頻時(shí)長(zhǎng)、記錄的接口的調(diào)用肯定會(huì)特別頻繁,因?yàn)槊块g隔指定時(shí)間每個(gè)用戶都會(huì)調(diào)用,如果在這個(gè)接口里直接操作數(shù)據(jù)庫(kù)將會(huì)給我們的數(shù)據(jù)庫(kù)帶來(lái)一定的壓力,在我的代碼中是不允許的,而我是這樣完成這個(gè)需求的:
首先將用戶觀看視頻的時(shí)長(zhǎng)、記錄存儲(chǔ)到阿里云的日志庫(kù)里,隨后以定時(shí)器從阿里云的日志庫(kù)中拉取用戶觀看視頻的數(shù)據(jù)同步到我們的數(shù)據(jù)庫(kù)中。
而就是最后這一步,同步數(shù)據(jù)到數(shù)據(jù)庫(kù)中,這里的數(shù)據(jù)量肯定是龐大的,所以我做了分表。
但是盡管做了分表數(shù)據(jù)量也不少,如果通過(guò)自增的主鍵id去編輯數(shù)據(jù)那么我在更新數(shù)據(jù)之前都要先從數(shù)據(jù)庫(kù)中查詢一次,然后在更新
在數(shù)據(jù)量大的情況下依然會(huì)給我們數(shù)據(jù)庫(kù)造成不少壓力,且這個(gè)定時(shí)器的執(zhí)行時(shí)長(zhǎng)將會(huì)拉大,這是我不能接受的
所以直接使用復(fù)合主鍵,以視頻id+用戶id去批量更新數(shù)據(jù),這樣就會(huì)快很多,然而mybatisplus卻僅支持單一主鍵操作,這就讓我剛屢清楚的思路陷入了僵局
不過(guò)還是讓我找到了支持復(fù)合主鍵的框架
mybatisplus-plus
是不是看起來(lái)挺離譜的?啥玩意就plus-plus?別急,讓我們來(lái)看看代碼先
注意mybatisplus與mybatisplus-plus的版本兼容性
首先引入jar包
<dependency>
<groupId>com.github.jeffreyning</groupId>
<artifactId>mybatisplus-plus</artifactId>
<version>1.5.1-RELEASE</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.1.0</version>
</dependency>
PO對(duì)象
package com.youxue.model.lesson;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.github.jeffreyning.mybatisplus.anno.MppMultiId;
import com.youxue.sharding.annotation.TableIndex;
import com.youxue.sharding.annotation.TableIndices;
import com.youxue.sharding.model.BaseShardingPo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* <p>
* 用戶觀看視頻時(shí)長(zhǎng)
* <p/>
*
* @author dx
* @since 2021/6/22
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("UserWatchVideoLog")
@ApiModel(value="UserWatchVideoLogPo對(duì)象", description="用戶觀看視頻時(shí)長(zhǎng)表")
@TableIndices({
@TableIndex(name = "IX_USERID" ,ddl = "CREATE NONCLUSTERED INDEX [IX_USERID] ON UserWatchVideoLog ( [UserId] DESC)" ),
@TableIndex(name = "IX_LESSONITEMID_USERID" ,ddl = "CREATE NONCLUSTERED INDEX [IX_LESSONITEMID_USERID] ON UserWatchVideoLog (LessonItemId ASC,UserId ASC)" )
})
public class UserWatchVideoLogPo implements Serializable, BaseShardingPo {
@MppMultiId // 復(fù)合主鍵
@TableField("userId")
@ApiModelProperty(value = "用戶id")
private Integer userId;
@TableField("lessonItemId")
@ApiModelProperty(value = "子課程id")
private Integer lessonItemId;
@ApiModelProperty(value = "觀看時(shí)長(zhǎng) 單位秒(s)")
@TableField("seconds")
private Integer seconds;
@ApiModelProperty(value = "科目id")
@TableField("subjectId")
private Integer subjectId;
@ApiModelProperty(value = "視頻觀看時(shí)長(zhǎng) 單位秒(s)")
@TableField("VideoProgress")
private Integer videoProgress;
@ApiModelProperty(value = "視頻來(lái)源 默認(rèn) 0 ")
@TableField("[Resource]")
private Integer resource;
@ApiModelProperty(value = "類(lèi)型 默認(rèn) 0 ")
@TableField("[Type]")
private Integer type;
@ApiModelProperty(value = "創(chuàng)建時(shí)間")
@TableField("CreateTime")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
private LocalDateTime createTime;
@ApiModelProperty(value = "修改時(shí)間")
@TableField("UpdateTime")
private LocalDateTime updateTime;
}@MppMultiId 注解即聲明為復(fù)合主鍵,并以@TableField 主鍵 聲明表字段
Service接口
package com.youxue.service.lesson;
import com.github.jeffreyning.mybatisplus.service.IMppService;
import com.youxue.model.lesson.UserWatchVideoLogPo;
/**
* <p>
* 用戶觀看視頻記錄 服務(wù)類(lèi)
* </p>
*
* @author dx
* @since 2021-06-22
*/
public interface IUserWatchVideoLogService extends IMppService<UserWatchVideoLogPo> {
}
Impl類(lèi)
package com.youxue.service.lesson.impl;
import com.github.jeffreyning.mybatisplus.service.MppServiceImpl;
import com.youxue.dao.lesson.UserWatchVideoLogMapper;
import com.youxue.model.lesson.UserWatchVideoLogPo;
import com.youxue.service.lesson.IUserWatchVideoLogService;
import org.springframework.stereotype.Service;
/**
* <p>
* 用戶觀看視頻記錄 服務(wù)類(lèi)實(shí)現(xiàn)類(lèi)
* </p>
*
* @author dx
* @since 2021/6/22
*/
@Service
public class UserWatchVideoLogServiceImpl extends MppServiceImpl<UserWatchVideoLogMapper, UserWatchVideoLogPo> implements IUserWatchVideoLogService {
}
Mapper接口
package com.youxue.dao.lesson;
import com.github.jeffreyning.mybatisplus.base.MppBaseMapper;
import com.youxue.model.lesson.UserWatchVideoLogPo;
/**
* <p>
* 用戶觀看視頻記錄 Mapper 接口
* </p>
*
* @author dx
* @since 2021-06-22
*/
public interface UserWatchVideoLogMapper extends MppBaseMapper<UserWatchVideoLogPo> {
}service 繼承 IMppService ,mapper 繼承 MppBaseMapper,impl 繼承 MppServiceImpl 實(shí)現(xiàn) service
并在啟動(dòng)類(lèi)上添加 @EnableMPP 注解
隨后直接在測(cè)試用例中運(yùn)行(測(cè)試用例中未使用分表):
@Autowired
private IUserWatchVideoLogService userWatchVideoLogService;
@Test
public void testUserWatchVideo() {
UserWatchVideoLogPo userWatchVideoLogPo = new UserWatchVideoLogPo()
.setUserId(6202238)
.setLessonItemId(56303)
.setSeconds(8888)
.setResource(11);
boolean create = userWatchVideoLogService.save(userWatchVideoLogPo);
System.out.println(create);
System.out.println("create result :" + create);
System.out.println("================ create end ==================");
// 斷點(diǎn)01
UserWatchVideoLogPo watchVideoLogPo = userWatchVideoLogService.selectByMultiId(userWatchVideoLogPo);
System.out.println(watchVideoLogPo.toString());
System.out.println("================ retrieve end ==================");
userWatchVideoLogPo.setSeconds(99999);
userWatchVideoLogPo.setResource(22);
// 斷點(diǎn)03
boolean upd = userWatchVideoLogService.updateByMultiId(userWatchVideoLogPo);
System.out.println("upd result :" + upd);
System.out.println("================ update end ==================");
// 斷點(diǎn)03
boolean remove = userWatchVideoLogService.deleteByMultiId(userWatchVideoLogPo);
System.out.println("remove result :" + remove);
System.out.println("================ remove end ==================");
}我在save 方法后每個(gè)方法出都打了斷點(diǎn),下面我們來(lái)看看運(yùn)行結(jié)果

可以看到,添加方法打印的SQL與mybatisplus并沒(méi)有什么區(qū)別,隨后看一下數(shù)據(jù)庫(kù)中的數(shù)據(jù)

是正常的,我們來(lái)看一下查詢操作

可以看到,這里的where條件后跟的是兩個(gè)查詢條件,是不是很棒。再看看編輯操作


可以到編輯操作的SQL也是已兩個(gè)條件操作的,數(shù)據(jù)也更新過(guò)來(lái)了,最后刪除操作:

至此支持復(fù)合組件的CRUD就完成了
而 mybatisplus-plus 作為 mybatisplus 的升級(jí)版 新穎的功能肯定不止于此
根據(jù)多個(gè)字段聯(lián)合主鍵增刪改查
原生mybatisplus只支持一個(gè)主鍵,
mpp支持多個(gè)字段聯(lián)合主鍵(復(fù)合主鍵)增刪改查,
mapper需要繼承MppBaseMapper
實(shí)體類(lèi)中聯(lián)合主鍵的字段需要用@MppMultiId注解修飾
如果需要在service使用多主鍵相關(guān)操作包括saveOrUpdateByMultiId和批量操作updateBatchByMultiId和saveOrUpdateBatchByMultiId,可以直接繼承IMppService接口
優(yōu)化分頁(yè)插件實(shí)現(xiàn)在不分頁(yè)時(shí)進(jìn)行排序操作
原生mybatisplus分頁(yè)與排序是綁定的,mpp優(yōu)化了分頁(yè)插件,使用MppPaginationInterceptor插件
在不分頁(yè)的情況下支持排序操作
page參數(shù)size設(shè)置為-1可實(shí)現(xiàn)不分頁(yè)取全量數(shù)據(jù),同時(shí)設(shè)置OrderItem可以實(shí)現(xiàn)排序自動(dòng)填充優(yōu)化功能 & 自動(dòng)掃描Entity類(lèi)構(gòu)建ResultMap功能
原生mybatisplus只能做%s+1和now兩種填充,mybatisplus-plus在插入或更新時(shí)對(duì)指定字段進(jìn)行自定義復(fù)雜sql填充。
需要在實(shí)體類(lèi)字段上用原生注解@TableField設(shè)置fill=FieldFill.INSERT fill=FieldFill.UPDATE或fill=FieldFill.INSERT_UPDATE否則不會(huì)觸發(fā)自定義填充
mybatisplus-plus使用@InsertFill注解觸發(fā)插入時(shí),執(zhí)行注解中自定義的sql填充實(shí)體類(lèi)字段
mybatisplus-plus使用@UpdateFill注解觸發(fā)更新時(shí),執(zhí)行注解中自定義的sql填充實(shí)體類(lèi)字段
還可以自動(dòng)填充主鍵字段,解決原生mybatisplus不支持多個(gè)主鍵的問(wèn)題
使用ColNameUtil.pn靜態(tài)方法,獲取實(shí)體類(lèi)中讀取方法對(duì)應(yīng)的列名稱(chēng)
mybatisplus-plus更多詳細(xì)用法【碼云倉(cāng)庫(kù)地址】
到此這篇關(guān)于mybatisplus 復(fù)合主鍵(多主鍵) CRUD的文章就介紹到這了,更多相關(guān)mybatisplus 復(fù)合主鍵內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- MyBatis-Plus的基本CRUD操作大全
- MyBatisPlus如何優(yōu)化千萬(wàn)級(jí)數(shù)據(jù)的CRUD
- MyBatis-Plus通用CRUD操作的實(shí)現(xiàn)
- MyBatis Plus Mapper CRUD接口測(cè)試方式
- Mybatis-Plus?CRUD操作方法
- MyBatisPlus中CRUD使用方法詳解
- MyBatisPlus標(biāo)準(zhǔn)數(shù)據(jù)層CRUD的使用詳解
- MyBatis-Plus使用ActiveRecord(AR)實(shí)現(xiàn)CRUD
- MyBatis-Plus 實(shí)現(xiàn)單表 CRUD的示例代碼
相關(guān)文章
3行代碼快速實(shí)現(xiàn)Spring Boot Oauth2服務(wù)功能
oauthserver是一個(gè)基于Spring Boot Oauth2的完整的獨(dú)立的Oauth服務(wù)器。僅僅需要?jiǎng)?chuàng)建相關(guān)數(shù)據(jù)表,修改數(shù)據(jù)庫(kù)的連接信息,你就可以得到一個(gè)Oauth服務(wù)器。這篇文章給大家介紹3行代碼快速實(shí)現(xiàn)Spring Boot Oauth2服務(wù)功能,需要的朋友參考下吧2018-04-04
SpringBoot+MinIO實(shí)現(xiàn)文件切片極速詳解
在現(xiàn)代Web應(yīng)用中,文件上傳是一個(gè)常見(jiàn)的需求,尤其是對(duì)于大文件的上傳,如視頻、音頻或大型文檔,所以本文就來(lái)為大家介紹一下如何使用Spring Boot和MinIO實(shí)現(xiàn)文件切片極速上傳技術(shù)吧2023-12-12
Java各種比較對(duì)象的方式的對(duì)比總結(jié)
比較對(duì)象是面向?qū)ο缶幊陶Z(yǔ)言的一個(gè)基本特征.在本教程中,我們將介紹Java語(yǔ)言的一些特性,這些特性允許我們比較對(duì)象.此外,我們還將研究外部庫(kù)中的這些特性,需要的朋友可以參考下2021-06-06
SpringBoot用配置影響B(tài)ean加載@ConditionalOnProperty
這篇文章主要為大家介紹了SpringBoot用配置影響B(tài)ean加載@ConditionalOnProperty示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04
Java基礎(chǔ)之位運(yùn)算知識(shí)總結(jié)
最近接觸到了java位運(yùn)算,之前對(duì)位運(yùn)算的了解僅僅停留在表現(xiàn)結(jié)果上,乘2除以2,對(duì)背后的原理并不了解,現(xiàn)在學(xué)習(xí)記錄一下,需要的朋友可以參考下2021-05-05
spring-boot-maven-plugin:unknown的完美解決方法
這篇文章主要介紹了spring-boot-maven-plugin:unknown的完美解決方法,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11
SpringBoot+Mybatis分頁(yè)插件PageHelper實(shí)現(xiàn)分頁(yè)效果
這篇文章主要介紹了SpringBoot+Mybatis實(shí)現(xiàn)分頁(yè)效果,本案例是采用Mybatis分頁(yè)插件PageHelper實(shí)現(xiàn),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-11-11
Spring中@Value注解獲取不到配置值問(wèn)題及解決
這篇文章主要介紹了Spring中@Value注解獲取不到配置值問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09
Java時(shí)間工具類(lèi)Date的常用處理方法
在Java中獲取當(dāng)前時(shí)間,可以使用 java.util.Date 類(lèi)和 java.util.Calendar 類(lèi)完成。其中,Date 類(lèi)主要封裝了系統(tǒng)的日期和時(shí)間的信息,下面將詳細(xì)介紹Date類(lèi)的常用處理方法,需要的可以參考一下2022-05-05

