mybatisplus 复合主键
叶儿飞飞 人气:1mybatisplus 复合主键CRUD
需求描述
最近接到个挺有意思的需求,做用户观看学习视频时长的一个数据埋点
储存用户观看视频时长、记录的接口的调用肯定会特别频繁,因为每间隔指定时间每个用户都会调用,如果在这个接口里直接操作数据库将会给我们的数据库带来一定的压力,在我的代码中是不允许的,而我是这样完成这个需求的:
首先将用户观看视频的时长、记录存储到阿里云的日志库里,随后以定时器从阿里云的日志库中拉取用户观看视频的数据同步到我们的数据库中。
而就是最后这一步,同步数据到数据库中,这里的数据量肯定是庞大的,所以我做了分表。
但是尽管做了分表数据量也不少,如果通过自增的主键id去编辑数据那么我在更新数据之前都要先从数据库中查询一次,然后在更新
在数据量大的情况下依然会给我们数据库造成不少压力,且这个定时器的执行时长将会拉大,这是我不能接受的
所以直接使用复合主键,以视频id+用户id去批量更新数据,这样就会快很多,然而mybatisplus却仅支持单一主键操作,这就让我刚屡清楚的思路陷入了僵局
不过还是让我找到了支持复合主键的框架
mybatisplus-plus
是不是看起来挺离谱的?啥玩意就plus-plus?别急,让我们来看看代码先
注意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对象
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> * 用户观看视频时长 * <p/> * * @author dx * @since 2021/6/22 */ @Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) @TableName("UserWatchVideoLog") @ApiModel(value="UserWatchVideoLogPo对象", description="用户观看视频时长表") @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 // 复合主键 @TableField("userId") @ApiModelProperty(value = "用户id") private Integer userId; @TableField("lessonItemId") @ApiModelProperty(value = "子课程id") private Integer lessonItemId; @ApiModelProperty(value = "观看时长 单位秒(s)") @TableField("seconds") private Integer seconds; @ApiModelProperty(value = "科目id") @TableField("subjectId") private Integer subjectId; @ApiModelProperty(value = "视频观看时长 单位秒(s)") @TableField("VideoProgress") private Integer videoProgress; @ApiModelProperty(value = "视频来源 默认 0 ") @TableField("[Resource]") private Integer resource; @ApiModelProperty(value = "类型 默认 0 ") @TableField("[Type]") private Integer type; @ApiModelProperty(value = "创建时间") @TableField("CreateTime") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @JsonDeserialize(using = LocalDateTimeDeserializer.class) @JsonSerialize(using = LocalDateTimeSerializer.class) private LocalDateTime createTime; @ApiModelProperty(value = "修改时间") @TableField("UpdateTime") private LocalDateTime updateTime; }
@MppMultiId 注解即声明为复合主键,并以@TableField 主键 声明表字段
Service接口
package com.youxue.service.lesson; import com.github.jeffreyning.mybatisplus.service.IMppService; import com.youxue.model.lesson.UserWatchVideoLogPo; /** * <p> * 用户观看视频记录 服务类 * </p> * * @author dx * @since 2021-06-22 */ public interface IUserWatchVideoLogService extends IMppService<UserWatchVideoLogPo> { }
Impl类
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> * 用户观看视频记录 服务类实现类 * </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 实现 service
并在启动类上添加 @EnableMPP 注解
随后直接在测试用例中运行(测试用例中未使用分表):
@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 =================="); // 断点01 UserWatchVideoLogPo watchVideoLogPo = userWatchVideoLogService.selectByMultiId(userWatchVideoLogPo); System.out.println(watchVideoLogPo.toString()); System.out.println("================ retrieve end =================="); userWatchVideoLogPo.setSeconds(99999); userWatchVideoLogPo.setResource(22); // 断点03 boolean upd = userWatchVideoLogService.updateByMultiId(userWatchVideoLogPo); System.out.println("upd result :" + upd); System.out.println("================ update end =================="); // 断点03 boolean remove = userWatchVideoLogService.deleteByMultiId(userWatchVideoLogPo); System.out.println("remove result :" + remove); System.out.println("================ remove end =================="); }
我在save 方法后每个方法出都打了断点,下面我们来看看运行结果
可以看到,添加方法打印的SQL与mybatisplus并没有什么区别,随后看一下数据库中的数据
是正常的,我们来看一下查询操作
可以看到,这里的where条件后跟的是两个查询条件,是不是很棒。再看看编辑操作
可以到编辑操作的SQL也是已两个条件操作的,数据也更新过来了,最后删除操作:
至此支持复合组件的CRUD就完成了
而 mybatisplus-plus 作为 mybatisplus 的升级版 新颖的功能肯定不止于此
根据多个字段联合主键增删改查
原生mybatisplus只支持一个主键,
mpp支持多个字段联合主键(复合主键)增删改查,
mapper需要继承MppBaseMapper
实体类中联合主键的字段需要用@MppMultiId注解修饰
如果需要在service使用多主键相关操作包括saveOrUpdateByMultiId和批量操作updateBatchByMultiId和saveOrUpdateBatchByMultiId,可以直接继承IMppService接口
优化分页插件实现在不分页时进行排序操作
原生mybatisplus分页与排序是绑定的,mpp优化了分页插件,使用MppPaginationInterceptor插件
在不分页的情况下支持排序操作
page参数size设置为-1可实现不分页取全量数据,同时设置OrderItem可以实现排序自动填充优化功能 & 自动扫描Entity类构建ResultMap功能
原生mybatisplus只能做%s+1和now两种填充,mybatisplus-plus在插入或更新时对指定字段进行自定义复杂sql填充。
需要在实体类字段上用原生注解@TableField设置fill=FieldFill.INSERT fill=FieldFill.UPDATE或fill=FieldFill.INSERT_UPDATE否则不会触发自定义填充
mybatisplus-plus使用@InsertFill注解触发插入时,执行注解中自定义的sql填充实体类字段
mybatisplus-plus使用@UpdateFill注解触发更新时,执行注解中自定义的sql填充实体类字段
还可以自动填充主键字段,解决原生mybatisplus不支持多个主键的问题
使用ColNameUtil.pn静态方法,获取实体类中读取方法对应的列名称
加载全部内容