栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Mybatis-plus笔记整理

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Mybatis-plus笔记整理

雪花算法

数据库的扩展方式主要包括:业务分库,主从复制,数据库分表

数据库分表

​ 如果业务持续发展,同一业务的单表数据也会达到数据库服务器的处理瓶颈;例如淘宝几亿用户数据,因此需要对单表数据进行拆分。

垂直分表

​ 列也比较多,查询量比较大的时候,通常把经常被查询的字段 和 数据量比较大的字段,拆分到不同的表中,比如age,sex 主要是查询使用,nickname昵称字段和描述字段主要用于展示使用而且本身还比较长,可以将后面两个字段独立到另一个表中去,这样查询的age 和 sex时 能带来一定的性能提升,相当于对表垂直切了一刀,把非主要查询的这两个字段独立出去。也就是把大字段或一般查询用不到的字段分离到另外一张表中。user user_info 用 id进行关联,user id字段自增长,user_info id字段不设策略,user的id是啥 我就是啥

主键一对一关联;

水平分表

适合表行数特别大的表,数据量特别大的时候,也没有明确查询字段之类的需求,关键还要看表的访问性能,一些比较复杂的表,可能超过1000万就要分表了,按数据分表,多少万条数据在一个表上,多少行数据在一个表上,相当于横着切一刀 分出去。水平分表相比垂直分表会引入更多的复杂性。

复杂性体现:主键自增问题 如果分2张表,按照主键范围存在不同的表上,比如1~1千万,1千万零一~2千万,分别在两张表上,如果满了 扩展容易,再加即可,实现了动态扩展,各表内数据量有多有少,现在一般都是负载均衡,会导致服务器访问不均衡。如果解决均衡问题,可以将表的主键起始id分表设为1、 2、 3 步长为3,这样就均衡了,但是扩展又会出问题,如果扩展又会出现数据迁移问题,重新分配布局。两种方式扩展都可能会遇到之前的表里面有数据被删除,然后后面的数据主键自增已经占用了新的主键,扩展的id并不是理论上应该存的id,而是之前的表可能已经用掉了。会不安全

解决办法:

Hash:取模运算,放到哪一个表中,要求初始表数量确定,表数量太多维护比较麻烦,太少又可能导致单表性能出现问题,表分布会比较均匀,但是扩展会很麻烦,所有数据都要重分布,uuid不能和mysql的聚簇索引一起用,不利于mysql查询优化

雪花算法snow flake(分布式id占19个符号位):mybatis-plus中默认的主键策略就是雪花算法

分布式主键生成算法,它能够保证不同表的主键不重复性,以及相同表的主键的有序性

核心思想:

长度64bit一个Long型,分四个部分:

1.第一个位置,符号位,为0,表示为正数,一般都不会用负数

2.第二个位置,时间戳位,41bit,存储的是差值,约定于69.73年,这样生成的时间都是不一样的

3.第三个位置,5bit是数据中心(相当于机房代码),5bit机器ID(表示数据中心存储数据的机器,可以部署在1024个节点,相当于1024台机器),

4.第四个位置,12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生4096个ID),支持并发量很大


mybatis-plus 默认识别 名字叫“id”的字段,自动进行雪花算法插入数据库id,如果数据库是uid实体类也是uid字段, 或者其他则识别不了,无法进行默认的雪花算法插入主键到数据库,会报错。所以此时需要在实体类的字段上加@TableId 告诉mybatis-plus这个字段是id 字段(数据库中是uid);

@TableName(value="t_user")
public class User {
    //默认雪花算法  也可以设置
   // @TableId(type= IdType.ASSIGN_ID) private String id; 也可以
    @TableId
    private Long uid;

或者,数据库名字叫uid,实体类叫做id,也无法完成映射,所以需要加value属性,如同@TableName注解

 @TableName(value="t_user")
public class User {
  //@TableId(value = "uid",type = IdType.AUTO) 主键自增策略
    @TableId(value = "uid")
    private Long id;
    private String name;
    private int age;
    private String email;xxxxxxxxxx @TableId(value = "uid")private Long id;private String name;private int age;private String email;
​

业务中id为空,数据库中也没有设置id;id是主键,不能为空,所以在插入的时候就会报错。如果所有的表都要求主键自增,可以在配置文件中设置

#设置全局主键自增
mybatis-plus.global-config.db-config.id-type=auto

新增两个字段 ,如果类中的列名为驼峰命名

private LocalDateTime createTime;
private LocalDateTime updateTime;
//在数据库中字段为:create_time   update_time mybatis-plus 会自动把驼峰进行转换

如果数据库中的字段名跟实体类不一致,需要用@TableField(value="")

@TableId(value = "uid",type = IdType.AUTO)
private Long id;
@TableField(value = "username")
private String name;
private int age;
private String email;

数据库字段可进行自动填充,创建和更新时间 可以用CURRENT_TIMESTAMP 系统自动管理,更新字段,再勾上默认根据当前时间戳进行更新,这样管理就不需要每次业务里面涉及。也可以在业务层进行设计,自动填充功能,需要在实体类加注解来标识。

@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE) //插入或更新的时候自动填充
private LocalDateTime updateTime;
//但是填充什么呢?需要定义实现自动填充功能 实现元对象处理器接口
@Slf4j
@Component
public class MyMeatObjectHandler implements metaObjectHandler {
    @Override
    public void insertFill(metaObject metaObject) {
        log.info("insert自动填充....");
          //插入数据的时候 识别到注解  @TableField(fill = FieldFill.INSERT) 就会进入这个方法来执行
         this.strictInsertFill(metaObject,"createTime", LocalDateTime.class, LocalDateTime.now());
         this.strictUpdateFill(metaObject,"updateTime", LocalDateTime.class, LocalDateTime.now());
    }
    @Override
    public void updateFill(metaObject metaObject) {
        log.info("update自动填充。。。");
        //更新的时候
        this.strictUpdateFill(metaObject,"updateTime",LocalDateTime.class,LocalDateTime.now());
    }
}
​

//判断当前对象自动填充属性是否包含 当前属性
boolean hasAuthor = metaObject.hasSetter("author");
//如果 有setter的方法 有这个author字段 进行自动填充author
if(hasAuthor) {
    log.info("insert author属性");
    this.strictInsertFill(metaObject, "author", String.class, "石头");
}

年龄也自动填充

@TableField(fill = FieldFill.INSERT)
    private Integer age;
log.info("age 填充为18");
 //对age进行自动填充 当没传age的时候填充18进去 
Object age = this.getFieldValByName("age", metaObject);
//如果业务层赋值了就不用去填充,如果没有赋值去进行填充
if(age==null){
  this.strictInsertFill(metaObject, "age", Integer.class, 18);
        }
​

@TableLogic 逻辑删除

物理删除:真实删除

逻辑删除:假删除,将对应数据中代表是否被删除字段的状态修改为“被删除状态”,之后在数据库中仍旧能看到此条数据记录使用场景,可以进行数据恢复

数据库中此字段 一般默认类型tinyint类型,默认1为已删除,0未删除

在数据库中创建逻辑删除状态列 同时实体字段一般前面不要加is有些框架识别可能出问题

    //private Integer deleted;
    //逻辑删除字段   0表示false 1表述true
    @TableLogic
    @TableField(value = "is_deleted")
    private Boolean deleted;

执行记录 需要数据库中此字段 值为0

Preparing: UPDATE t_user SET is_deleted=1 WHERe uid=? AND is_deleted=0 ==> Parameters: 1499759447969390597(Long)<== Updates: 0

//自定义 1为未删除 -1为已删除 需要去配置
@TableLogic
@TableField(value = "is_deleted")
private Integer deleted;
mybatis-plus.global-config.db-config.logic-delete-field=deleted
mybatis-plus.global-config.db-config.logic-delete-value=-1
mybatis-plus.global-config.db-config.logic-not-delete-value=1

分页插件

1.添加配置类 config包 可以将配置全部写在这个包内

spring

    
    
    
        
            
        
    

    
    

    
        
            
        
    

    
    
spring-boot
@Configuration
@MapperScan("scan.your.mapper.package")
public class MybatisPlusConfig {
​
    
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
        return interceptor;
    }
​
    @Bean
    public ConfigurationCustomizer configurationCustomizer() {
        return configuration -> configuration.setUseDeprecatedExecutor(false);
    }
}
//可将包扫描写在这个类里面 也可以写在启动类上
@Configuration
@MapperScan("mybatisplus.mapper")
public class MybatisPConfig {
    
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }

针对mysql的分页配置类 写好了。进行测试

@SpringBootTest
public class IntercepterTest {
    @Resource
    private UserMapper userMapper;
    @Test
    public void test1(){
        //新建分页参数对象 查询第几页,每页5条记录
        Page userPage = new Page<>(2, 5);
        //这两个对象是一个对象 参数为页对象 及qureyMapper条件构造器 先传null
        //Page userPage1 = userMapper.selectPage(userPage, null);
        //System.out.println("确认是否是一个对象"+(userPage==userPage1)); true
        //优化为
        userMapper.selectPage(userPage, null);
        //当前页码下的所有记录
        List users = userPage.getRecords();
        users.forEach(System.out::println);
    }
}
long total = userPage.getTotal();
System.out.println("总数+"+total); 
​
boolean bn = userPage.hasNext();
System.out.println("下一页??"+bn);
​
boolean bp = userPage.hasPrevious();
System.out.println("上一页?"+bp);
​

在xml中如何使用配置分页?

//@Repository
public interface UserMapper extends baseMapper {
    //自定义一个方法 具体的xml实现在resources下面的mapper文件中
    //扩展mapper
    List selectAllByName(String name);
    //第一个参数为分页对象  第二个为查询条件  根据年龄来查询用户 并分页展示
    IPage selectPageVo(IPage page, Integer age);