亲宝软件园·资讯

展开

mybatisplus 复合主键

叶儿飞飞 人气:1

mybatisplus 复合主键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 方法后每个方法出都打了断点,下面我们来看看运行结果

img

可以看到,添加方法打印的SQL与mybatisplus并没有什么区别,随后看一下数据库中的数据

img

是正常的,我们来看一下查询操作

img

可以看到,这里的where条件后跟的是两个查询条件,是不是很棒。再看看编辑操作

img

img

可以到编辑操作的SQL也是已两个条件操作的,数据也更新过来了,最后删除操作:

img

至此支持复合组件的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静态方法,获取实体类中读取方法对应的列名称

mybatisplus-plus更多详细用法【码云仓库地址】

加载全部内容

相关教程
猜你喜欢
用户评论