banner
NEWS LETTER

Mybatis-Plus

Scroll down

Mybatis-Plus

Mybatis-Plus介绍

mybatis-plus是一个Mybatis的增强工具,在Mybatis的基础上只做增强不做改变,为简化开发提高效率而生。

官网:https://baomidou.com/

基础增删改查

mybatis-plus作为一个简化mybatis开发的增强工具,他到底增强到哪了?

首先我们之前在dao层中需要书写大量的sql语句来执行增删改查操作。但是用mybatis-plus就完全不必我们只需要继承一个*BaseMapper<?>*接口其中问号是pojo类对象

现在我们的Mapper层可以这样写

1
2
3
4
@Mapper
public interface UserMapper extends BaseMapper<User> {

}

Service层

1
2
3
public interface UserService extends IService<User>{
public User getById(Long id);
}

Service层实现类

1
2
3
4
5
6
7
8
9
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService{
@Autowired
private UserMapper usermapper;

public User getById(Long id){
User user=usermapper.selectById(id);
}
}

pojo类User

1
2
3
4
5
6
7
8
9
10
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private String password;
private int age;
private String tel;
}

image-20230326153619922

分页功能实现

分页拦截器实现

  1. 设置分页拦截器作为Spring管理的bean

    MybatisPlusConfig

1
2
3
4
5
6
7
8
9
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor mybatisPlusInterceptor=new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return mybatisPlusInterceptor;
}
}

测试类中

1
2
3
4
5
6
7
8
9
10
@Test
void testGetByPage(){
IPage page=new Page(1,2);
userDao.selectPage(page,null);
System.out.println("当前页码值:"+page.getCurrent());
System.out.println("每页显示数:"+page.getSize());
System.out.println("一共多少页:"+page.getPages());
System.out.println("一共多少条数据:"+page.getTotal());
System.out.println("数据:"+page.getRecords());
}

PageHelper实现

  1. 导入依赖

    1
    2
    3
    4
    5
    <dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.1.2</version>
    </dependency>
  2. 调用pagehelper

    controller

    注意:PageHelper.startPage的后面必须直接接上查询,否则会出现添加limit的情况

    1
    2
    3
    4
    5
    //在调用数据库的方法之前调用pageHelper方法
    PageHelper.startPage(page,size); //pageHelp的方法,参数分别为第几页和每页显示几条数据
    //调用数据库的查询语句
    List<MeetingRoom> meetingRooms = meetingRoomService.findAllMeetingRoom();
    PageInfo pageInfo = new PageInfo(meetingRooms);
  3. pagehelper中常用的参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    //当前页
    private int pageNum;
    //每页的数量
    private int pageSize;
    //当前页的数量
    private int size;
    //当前页面第一个元素在数据库中的行号
    private int startRow;
    //当前页面最后一个元素在数据库中的行号
    private int endRow;
    //总记录数
    private long total;
    //总页数
    private int pages;

多条件查询(querywapper)

1
2
3
4
5
6
7
8
9
10
11
12
//声明接口LambdaQueryWrapper
LambdaQueryWrapper<User> lqw=new LambdaQueryWrapper<User>();

//采用流式编程设置条件
//查询年龄大于23或者小于20的
lqw.gt(User::getAge,23).or().lt(User::getAge,20);
List<User> userlist=userDao.selectList(lqw);

//查询年龄在23到20中间的
lqw.between(User::getAge,20,23);
List<User> userlist=userDao.selectList(lqw);
System.out.println(userlist);

解决用户设置数据为空的问题

问题概述:在用户选择时,有可能选择多少多少以下的或者多少多少以上的,则条件查询中的另外一个查询条件就不需要

要想实现就要先创建一个模型类user2用来规定最低数量

1
2
3
public class User2 extends User{
private Integer age2;
}

在测试类中:

1
2
3
4
5
6
7
8
9
User2 user2=new User2();
user2.setAge(32);
user2.setAge2(22);

LambdaQueryWrapper<User> lqw=new LambdaQueryWrapper<User>();
//第一个参数是判断是否执行
lqw.lt(null != user2.getAge2(), User::getAge,23).gt(User::getAge,20);
List<User> userlist=userDao.selectList(lqw);
System.out.println(userlist);

DQL编程控制

查询结果中包含pojo类中的属性

1
2
3
4
LambdaQueryWrapper<User> lqw=new LambdaQueryWrapper<User>();
lqw.select(User::getId,User::getName,User::getAge);
List<Map<String,Object>> userlist=userDao.selectMaps(lqw);
System.out.println(userlist);

查询结果:

[{name=蔡徐坤, id=0, age=12}, {name=陈立农, id=1, age=20}, {name=范丞丞, id=2, age=23}, {name=黄明昊, id=3, age=22}, {name=林彦俊, id=4, age=22}, {name=朱正廷, id=5, age=25}, {name=王子异, id=6, age=21}, {name=王琳凯, id=7, age=22}, {name=尤长靖, id=8, age=23}]

查询结果中包含pojo未定义的属性

示例:相同的值分组

1
2
3
4
5
QueryWrapper<User> lqw=new QueryWrapper<User>();
lqw.select("count(*) as count,age");
lqw.groupBy("age");
List<Map<String,Object>> userlist=userDao.selectMaps(lqw);
System.out.println(userlist);

查询结果:

[{count=1, age=12}, {count=1, age=20}, {count=1, age=21}, {count=3, age=22}, {count=2, age=23}, {count=1, age=25}]

登录注册验证

示例:查询名字是陈立农密码123的用户

1
2
3
4
LambdaQueryWrapper<User> lqw=new LambdaQueryWrapper<User>();
lqw.eq(User::getName,"陈立农").eq(User::getPassword,"123");
User loginUser=userDao.selectOne(lqw);
System.out.println(loginUser);

模糊查询

1
2
3
4
5
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.likeLeft(User::getName,"蔡徐坤");
//.likeRight是查以什么为结尾的
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);

映射pojo匹配兼容

如果数据库表名和pojo类名称不一样怎么办?如果pojo类中的数据名和数据库中的数据名不一致怎么办?如果pojo类中存在表中没有的元素怎么办?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//解决表名不一致
@TableName("tbl_user")
public class User {
//解决生成id长度丢失问题
@JsonSerialize(using = ToStringSerializer.class)
private int id;
private String name;
//解决字段名不一致
@TableField(value = "pwd",select = false)
private String password;
private int age;
private String tel;
//解决有多余的属性
@TableField(exist = false)
private int online;
}

ID生成策略控制

主要用来设置id自动递增的值

  • AUTO(0):使用数据库自己的自增策略控制id生成
  • NONE(1):不设置id生成策略
  • INPUT(2):用户手动输入id
  • ASSIGN_ID(3):雪花算法生成id(可兼容数值型和字符串型)

具体用法:

  1. 自动生成
1
2
@TableId(type = IdType.AUTO)
private int id;
  1. 用户自定义
1
2
@TableId(type = IdType.INPUT)
private int id;

同时也必须带上.setId()

1
2
User user=new User();
user.setId(16);
  1. 利用雪花算法自动生成id

    雪花算法:自动生成的一种64位的二进制,第一位代表正负数,后面的41位是时间戳,精确到毫秒。再往后十位是机器码,最后的这些则是12位的序列号

1
2
@TableId(type = IdType.ASSIGN_ID)
private int id;

为所有pojo设置id生成策略

在yaml文件中写入:

1
2
3
4
mybatis-plus:
global-config:
db-config:
id-type: input

公共字段自动填充

在表中有些字段属于公共字段,也就是很多表中都有的字段。这些字段会在不同的功能模块中进行不同的改写,能不能对于这些字段在某个地方进行统一的处理来简化开发呢?

答案就是MybatisPlus提供的公共字段自动填充功能

实现步骤:

  1. 在指定的字段上添加@TableFileld注解,指定自动填充的策略

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    //插入时填写字段
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;

    /*插入和更新时填写字段*/
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;

    //插入时填写字段
    @TableField(fill = FieldFill.INSERT)
    private long createUser;

    /*插入和更新时填写字段*/
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private long updateUser;
  2. 按照框架要求编写元数据对象处理器,在此类中统一为公共字段赋值,此类需要实现MetaObjectHandler接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    /*
    自定义源数据对象处理器
    */
    public class MyMetaObjecthandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
    metaObject.setValue("createTime", LocalDateTime.now());
    metaObject.setValue("updateTime",LocalDateTime.now());
    metaObject.setValue("createUser",BaseContext.getCurrentId());
    metaObject.setValue("updateUser",BaseContext.getCurrentId());
    }
    @Override
    public void updateFill(MetaObject metaObject) {

    }
    }

    这样就能自动进行字段的赋值

多数据操作

删除多个

使用.deleteBatchIds通过id删除过个数据

1
2
3
4
5
6
7
8
@Test
void testDelete(){
List<Integer> list=new ArrayList<>();
list.add(10);
list.add(11);
list.add(12);
userDao.deleteBatchIds(list);
}

查询多个

使用.selectBatchIds通过id删除过个数据

1
2
3
4
5
6
7
8
9
@Test
void testById(){
List<Integer> list=new ArrayList<>();
list.add(1);
list.add(3);
list.add(5);
List<User> list2=userDao.selectBatchIds(list);
System.out.println(list2);
}

逻辑删除

所谓逻辑删除就是并不删除数据,而是增加一个字段通过0和1来区分这条数据该不该被查出来

pojo类中

1
2
3
//第一个指默认值,第二个是确定为什么值时表示删除
@TableLogic(value = "0",delval = "1")
private int deleted;

现在在执行删除也就不会真正删除而是会给他附上逻辑删除值

等到查询的时候也同样不会查到

乐观锁

乐观锁主要解决的就是类似于秒杀一样的场景,可以解决低于2000人同时访问。主要做的就是将

  1. sql库中添加字段

    version int类型

  2. 实体类中添加对应字段,并设定当前字段为逻辑删除标记字段

    1
    2
    @Version
    private Integer version;
  3. 配置乐观锁拦截器实现锁机制对应的动态sql语句拼装

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Configuration
    public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
    MybatisPlusInterceptor mybatisPlusInterceptor=new MybatisPlusInterceptor();
    mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
    //添加乐观锁
    mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
    return mybatisPlusInterceptor;
    }

    }
  4. 使用乐观锁机制在修改前必须先获取到对应数据的verion方可正常进行

    1
    2
    3
    4
    5
    6
    7
    8
    @Test
    void testUpdate(){
    //先查询数据,获取到version数据
    User user=userDao.selectById(1);
    //执行数据修改操作
    user.setName("Tom and Jerry")
    userDao.updateById(user);
    }

乐观锁和悲观锁

乐观锁的概念

乐观锁:指的是在操作数据的时候非常乐观,乐观地认为别人不会同时修改数据,因此乐观锁默认是不会上锁的,只有在执行更新的时候才会去判断在此期间别人是否修改了数据,如果别人修改了数据则放弃操作,否则执行操作。

冲突比较少的时候, 使用乐观锁(没有悲观锁那样耗时的开销) 由于乐观锁的不上锁特性,所以在性能方面要比悲观锁好,比较适合用在DB的读大于写的业务场景。

悲观锁的概念

悲观锁:指的是在操作数据的时候比较悲观,悲观地认为别人一定会同时修改数据,因此悲观锁在操作数据时是直接把数据上锁,直到操作完成之后才会释放锁,在上锁期间其他人不能操作数据。

冲突比较多的时候, 使用悲观锁(没有乐观锁那么多次的尝试)对于每一次数据修改都要上锁,如果在DB读取需要比较大的情况下有线程在执行数据修改操作会导致读操作全部被挂载起来,等修改线程释放了锁才能读到数据,体验极差。所以比较适合用在DB写大于读的情况。

读取频繁使用乐观锁,写入频繁使用悲观锁。

快速代码生成器

  1. 导入依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.4.1</version>
    </dependency>
    <dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity-engine-core</artifactId>
    <version>2.3</version>
    </dependency>
  2. 调用及配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    //1.获取代码生成器的对象
    AutoGenerator autoGenerator = new AutoGenerator();

    //设置数据库相关配置
    DataSourceConfig dataSource = new DataSourceConfig();
    dataSource.setDriverName("com.mysql.cj.jdbc.Driver");
    dataSource.setUrl("jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC");
    dataSource.setUsername("root");
    dataSource.setPassword("root");
    autoGenerator.setDataSource(dataSource);

    //设置全局配置
    GlobalConfig globalConfig = new GlobalConfig();
    globalConfig.setOutputDir(System.getProperty("user.dir")+"/mybatisplus_04_generator/src/main/java"); //设置代码生成位置
    globalConfig.setOpen(false); //设置生成完毕后是否打开生成代码所在的目录
    globalConfig.setAuthor("黑马程序员"); //设置作者
    globalConfig.setFileOverride(true); //设置是否覆盖原始生成的文件
    globalConfig.setMapperName("%sDao"); //设置数据层接口名,%s为占位符,指代模块名称
    globalConfig.setIdType(IdType.ASSIGN_ID); //设置Id生成策略
    autoGenerator.setGlobalConfig(globalConfig);

    //设置包名相关配置
    PackageConfig packageInfo = new PackageConfig();
    packageInfo.setParent("com.aaa"); //设置生成的包名,与代码所在位置不冲突,二者叠加组成完整路径
    packageInfo.setEntity("domain"); //设置实体类包名
    packageInfo.setMapper("dao"); //设置数据层包名
    autoGenerator.setPackageInfo(packageInfo);

    //策略设置
    StrategyConfig strategyConfig = new StrategyConfig();
    strategyConfig.setInclude("tbl_user"); //设置当前参与生成的表名,参数为可变参数
    strategyConfig.setTablePrefix("tbl_"); //设置数据库表的前缀名称,模块名 = 数据库表名 - 前缀名 例如: User = tbl_user - tbl_
    strategyConfig.setRestControllerStyle(true); //设置是否启用Rest风格
    strategyConfig.setVersionFieldName("version"); //设置乐观锁字段名
    strategyConfig.setLogicDeleteFieldName("deleted"); //设置逻辑删除字段名
    strategyConfig.setEntityLombokModel(true); //设置是否启用lombok
    autoGenerator.setStrategy(strategyConfig);
    //2.执行生成操作
    autoGenerator.execute();
Other Articles
cover
MySql数据库
  • 23/09/18
  • 18:15
  • 2.4k
  • 9
cover
Linux指令
  • 23/09/18
  • 18:15
  • 2.5k
  • 10