Mybatis-Plus学习

文章目录

    • 一、简介
      • 1. 概述
      • 2. 特点
      • 3. 框架架构
    • 二、入门案例
      • 1. 数据库环境准备
      • 2. SpringBoot工程准备
      • 3. 配置application.yml
      • 4. 项目开发
      • 5. MybatisPlus测试
    • 三、BaseMapper
      • 1. 源码
      • 2. 方法测试
    • 四、IService
      • 1. 简介
      • 2. 使用IService
      • 3. 测试IService
    • 五、MybatisPlus为我们提供的一些注解
      • 1. @TableName
      • 2. @TableId
      • 3. 雪花算法
      • 4. @TableField
      • 5. @TableLogic
    • 六、条件构造器
      • 1. 简介
      • 2. QueryWrapper
      • 2. UpdateWrapper
      • 3. 模拟开发中组装条件的情况
      • 4. LambdaQueryWrapper
      • 4. LambdaUpdateWrapper
    • 七、MybatisPlus提供的插件
      • 1. 分页插件
      • 2. 乐观锁插件
    • 八、MybatisPlus通用枚举
      • 1. 简介
      • 2. 测试
    • 九、代码生成器
      • 1. 简介
      • 2. 简单案例模拟代码生成
    • 十、多数据源
      • 1. 场景
      • 2. 多数据源使用案例

一、简介

1. 概述

Mybatis-Plus是一个Mybatis的增强工具,在Mybatis的基础上只做增强不做改变,为简化开发、提高效率而生。Mybatis-Plus提供了通用的Mapper和Service,可以在不编写任何SQL语句的情况下,快速实现对单表的CRUD、批量、逻辑删除、分页等操作。

2. 特点

  • 润物无声:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 效率至上:只需简单配置,即可快速进行单表CRUD操作,从而节省大量时间
  • 丰富功能:代码生成、自动分页、逻辑删除、自动填充等功能一应俱全

3. 框架架构

在这里插入图片描述

二、入门案例

1. 数据库环境准备

  • 创建数据库和表
CREATE DATABASE `mybatis_plus` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;
use `mybatis_plus`;
CREATE TABLE `user` (
`id` bigint(20) NOT NULL COMMENT '主键ID', `name` varchar(30) DEFAULT NULL COMMENT '姓名', `age` int(11) DEFAULT NULL COMMENT '年龄', `email` varchar(50) DEFAULT NULL COMMENT '邮箱', PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
  • 向表中添加数据
INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');

2. SpringBoot工程准备

  • 项目创建

在这里插入图片描述

  • 导入依赖
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.1</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency>

3. 配置application.yml

spring:datasource:#配置数据源类型type: com.zaxxer.hikari.HikariDataSource#配置数据库连接信息driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=falseusername: rootpassword: 123456

4. 项目开发

  • 创建实体类
@Data
public class User {private Long id;private String name;private Integer age;private String email;
}
  • 创建Mapper接口
//继承了Mybatis-plus提供了的BaserMapper接口
public interface userMapper extends BaseMapper<User> {
}
  • 配置启动函数
@SpringBootApplication
//配置扫描Mapper接口
@MapperScan("com.jack.mybatis_plus.mapper")
public class MybatisPlusApplication {public static void main(String[] args) {SpringApplication.run(MybatisPlusApplication.class, args);}}
  • 创建测试类
@SpringBootTest
public class MyBatisPlusTest {
}

5. MybatisPlus测试

  • 测试查询所有数据
@SpringBootTest
public class MyBatisPlusTest {@Autowiredprivate UserMapper userMapper;@Testpublic void testSelectList(){//通过条件构造器查询一个list集合,没有条件,设置null为参数List<User> users = userMapper.selectList(null);users.forEach(System.out::println);}}

在这里插入图片描述

  • 加入日志功能

要想加入日志功能,只需要在application.yml中加入以下配置即可:

mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

加入日志功能后就可以看到Mybatis-plus帮我们生成的查询语句

在这里插入图片描述

三、BaseMapper

1. 源码

BaseMapper为我们提供了很多数据库的操作方法,我们首先看一下这个接口的源码:

public interface BaseMapper<T> extends Mapper<T> {//插入一条数据int insert(T entity);//根据id删除一条数据int deleteById(Serializable id);int deleteById(T entity);//根据Map集合删除int deleteByMap(@Param("cm") Map<String, Object> columnMap);//根据条件构造器来删除数据int delete(@Param("ew") Wrapper<T> queryWrapper);//根据多个id进行批量删除int deleteBatchIds(@Param("coll") Collection<?> idList);//根据id来进行修改int updateById(@Param("et") T entity);//根据条件构造器修改int update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper);//根据id查询T selectById(Serializable id);//根据多个id进行批量查询List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList);//根据集合查询List<T> selectByMap(@Param("cm") Map<String, Object> columnMap);//查询条件构造器查询单个数据default T selectOne(@Param("ew") Wrapper<T> queryWrapper) {List<T> ts = this.selectList(queryWrapper);if (CollectionUtils.isNotEmpty(ts)) {if (ts.size() != 1) {throw ExceptionUtils.mpe("One record is expected, but the query result is multiple records", new Object[0]);} else {return ts.get(0);}} else {return null;}}//根据条件构造器判断指定的数据是否存在default boolean exists(Wrapper<T> queryWrapper) {Long count = this.selectCount(queryWrapper);return null != count && count > 0L;}//根据条件构造器查询符合条件数据的数量Long selectCount(@Param("ew") Wrapper<T> queryWrapper);//根据条件构造器查询list集合List<T> selectList(@Param("ew") Wrapper<T> queryWrapper);//根据条件构造器将查询到的数据返回成一个map集合List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> queryWrapper);List<Object> selectObjs(@Param("ew") Wrapper<T> queryWrapper);//分页查询<P extends IPage<T>> P selectPage(P page, @Param("ew") Wrapper<T> queryWrapper);<P extends IPage<Map<String, Object>>> P selectMapsPage(P page, @Param("ew") Wrapper<T> queryWrapper);
}

2. 方法测试

  • 添加功能测试
  @Testpublic void testInsert(){User user=new User();user.setName("张三");user.setAge(23);user.setEmail("293492394@qq.com");int insert = userMapper.insert(user);System.out.println(insert);System.out.println("id:"+user.getId());}

MybatisPlus使用学花算法帮user自动生成了id

在这里插入图片描述

  • 测试删除方法
 @Testpublic void testDelete(){//通过id删除用户信息int i = userMapper.deleteById(1761950230170980354L);System.out.println(i);//通过map集合设置条件删除Map<String,Object> map=new HashMap<>();map.put("name","张三");map.put("age",23);int i1 = userMapper.deleteByMap(map);//通过传入的多个id进行批量删除List<Long> longs = Arrays.asList(1L, 2L, 3L);int i2 = userMapper.deleteBatchIds(longs);}

在这里插入图片描述

  • 测试修改方法
    @Testpublic void testUpdate(){User user=new User();user.setId(4L);user.setName("李四");user.setEmail("203402@qq.com");int i = userMapper.updateById(user);System.out.println(i);}

在这里插入图片描述

  • 测试查询功能
@Testpublic void testSelect(){//根据id查询用户信息User user = userMapper.selectById(1L);System.out.println(user);//根据多个id进行批量查询List<Long> longs = Arrays.asList(1L, 2L);List<User> users = userMapper.selectBatchIds(longs);users.forEach(System.out::println);//根据map集合作为条件来查询Map<String,Object> map=new HashMap<>();map.put("name","Tom");map.put("age",28);List<User> users1 = userMapper.selectByMap(map);users1.forEach(System.out::println);}

在这里插入图片描述

四、IService

1. 简介

前面介绍了BaseMapper是MybatisPlus为我们提供的通用的Mapper接口,同样MybatisPlus同样为我们提供了通用的Service接口,即IService接口,进一步封装CRUD采用get查询单行,remove删除,list查询集合等。

2. 使用IService

  • 创建自己的Service接口
public interface UserService extends IService<User> {
}
  • 实现Service接口
public class UserSErvice extends ServiceImpl<UserMapper, User> implements UserService {
}

3. 测试IService

  • 查询表中总的记录数
 @Testpublic void  testGetCount(){long count = userService.count();System.out.println(count);}

在这里插入图片描述

  • 批量添加
 @Testpublic void insertBatch(){List<User> list=new ArrayList<>();for (int i = 0; i < 10; i++) {User user=new User();user.setName("ybc"+i);user.setAge(20+i);list.add(user);}userService.saveBatch(list);}

在这里插入图片描述

五、MybatisPlus为我们提供的一些注解

1. @TableName

前面我们介绍BaseMapper时,它后面会要求我们指定一个范型,而MybatisPlus就是根据我们提供的范型的类型到数据库中去找指定的同名的表,如果表名和类名不同此时就会出现错误,那么怎么解决这个问题呢,第一种方法就是在类上加入@TableName注解来指定表的名称。

@Data
@TableName("user")
public class User {private Long id;private String name;private Integer age;private String email;
}

如果我们的类名还是User,而所有表都有一个前缀t_,此时我们不需要使用@TableName("t_user")来指定表明,可以通过在applicaiton.yml配置通用前缀来解决这个问题。

  global-config:db-config:table-prefix: t_

2. @TableId

我们知道MybatisPlus在增加和修改时会自动将类中的id字段作为主键来操作数据,如果我们的类所对应的表的主键不叫id,那么就出现问题了。而@TableId注解就可以解决这个问题。

@Data
public class User {@TableIdprivate Long id;private String name;private Integer age;private String email;
}

@TableId注解将我们的属性所对应的字段指定为主键。该注解有两个属性:

  • value:指定类主键字段对应的数据库表名的主键字段
  • type:指定主键生成策略,MybatisPlus默认使用的是雪花算法,例如我们可以指定type为媒介类型IdType的值

常用的主键生成策略有:
在这里插入图片描述
上面设置的是某个表主键的生成策略,当然我们同样可以通过配置文件来配置全局生成策略:

mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
#  global-config:
#    db-config:
#      table-prefix: t_global-config:db-config:id-type: assign_id

3. 雪花算法

  • 背景

随着数据规模的增大,我们需要考虑数据库的访问压力和数据量的处理,此时我们就需要扩展数据库的性能。常见的方式有:业务分库主从复制数据库分表等。

  • 数据库分表

将不同业务数据分散存储到不同的数据库服务器,能够支撑百万甚至千万用户规模的业务,但如果业务继续发展,同一业务的单表数据也会达到单台数据库服务器的处理瓶颈。例如淘宝的几亿用户数据,如果全部放在一台数据库服务器的一张表中,肯定是无法满足性能要求的,此时就需要对单表数据进行拆分。单表数据拆分的方式有两种:垂直分表水平分表

在这里插入图片描述

垂直分表适合将表中某些不常用且占了大量空间的列拆分出去,水平分表适合表行数特别大的表。

水平分表相比垂直分表,会引入更多的复杂性,例如要求全局唯一的数据id,处理方式为主键自增、取模和雪花算法。

雪花算法是由Twitter公布的分布式主键生成算法,它能够保证不同表的主键的不重复性,以及相同表的 主键的有序性。核心思想如下:

  1. 生成id的长度为64bit(一个long型)
  2. 64bit中首先是符号位,1bit标识,由于long基本类型在java中是带符号的,最高位位符号位,正数位0,负数是1,所以id一般是正数,最高位位0
  3. 41bit时间戳,存储的是时间戳的差值(当前时间戳-开始时间戳)
  4. 10bit是机器的ID(5个bit是数据中心,5个bit的机器ID,可以部署在1024个节点)
  5. 12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生4096个ID)

整体上按照时间自增排序,并且整个分布式系统不会产生ID碰撞,并且效率高
在这里插入图片描述

4. @TableField

考虑到一种特殊情况,如果我们的数据库表中的字段是下划线命名,而类中属性是驼峰命名,此时就出现了属性名和字段名不一致的情况。这种情况下MybatisPlus有默认配置开启驼峰命名的。如果属性名和字段名完全不一样,此时就可以使用@TableField注解了。

@Data
public class User {private Long id;@TableField("user_name")private String name;private Integer age;private String email;
}

5. @TableLogic

  • 逻辑删除

物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除的数据
逻辑删除:假删除,将对应数据中代表被删除字段的状态修改为被删除状态,之后在数据库中仍然可以看到
使用场景:进行数据恢复

  • 实现逻辑删除
  1. 在数据库中创建逻辑删除状态列,默认设置为0

在这里插入图片描述
2. 实体类中添加逻辑删除属性

@Data
public class User {private Long id;private String name;private Integer age;private String email;@TableLogicprivate Integer isDeleted;
}

此时代码中再执行删除操作,就不会是物理删除了,而是逻辑删除,查询此时不会查到逻辑删除的字段。

六、条件构造器

1. 简介

前面在介绍BaseMapper的方法时看到了有很多方法的参数是一个条件构造器WapperWapper的作用本质上就是用来封装Sql语句中的条件的。

在这里插入图片描述

  • Wapper:条件构造抽象类,最顶端父类
  • AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
  • QueryWrapper : 查询条件封装
  • UpdateWrapper : Update 条件封装
  • AbstractLambdaWrapper : 使用Lambda 语法
  • LambdaQueryWrapper :用于Lambda语法使用的查询Wrapper
  • LambdaUpdateWrapper : Lambda封装更新Wrapper

2. QueryWrapper

  • 组装查询条件
 @Testpublic void test01(){//查询用户名包含a,年龄在20-30,邮箱不为null的用户信息QueryWrapper<User> queryWrapper=new QueryWrapper<>();//模拟查询queryWrapper.like("name","a").between("age",20,30).isNotNull("email");List<User> users = userMapper.selectList(queryWrapper);users.forEach(System.out::println);}

在这里插入图片描述

  • 组装排序条件
   @Testpublic void test02(){//查询用户信息,按照年龄的降序排序,年龄相同则按照id升序排序QueryWrapper<User> queryWrapper=new QueryWrapper<>();queryWrapper.orderByDesc("age").orderByAsc("id");List<User> users = userMapper.selectList(queryWrapper);users.forEach(System.out::println);}

在这里插入图片描述

  • 组装删除条件
    @Testpublic void test03(){//删除邮箱地址为null的数据QueryWrapper<User> queryWrapper=new QueryWrapper<>();queryWrapper.isNull("email");int delete = userMapper.delete(queryWrapper);System.out.println(delete);}

在这里插入图片描述

  • 组装修改功能条件
    @Testpublic void test04(){//将年龄大于20并且用户名中包含a,或邮箱为null的用户信息修改QueryWrapper<User> queryWrapper=new QueryWrapper<>();queryWrapper.like("name","a").gt("age",20).or().isNull("email");User user=new User();user.setName("小明");user.setEmail("test#q.qq.com");userMapper.update(user,queryWrapper);}

针对上面的案例我们改一下需求为:将用户名中包含有a并且(年龄大于20或邮箱为null)的用户信息修改,此时就不能使用上面的条件构造器了,因为出现了条件优先级,正确代码如下:

    @Testpublic void test05(){//将用户名中包含有a并且(年龄大于20或邮箱为null)的用户信息修改QueryWrapper<User> queryWrapper=new QueryWrapper<>();queryWrapper.like("name","a").and(i->i.gt("age",20).or().isNull("email"));User user=new User();user.setName("小明");user.setEmail("test#q.qq.com");userMapper.update(user,queryWrapper);}
  • 组装Select语句

上面代码中,我们执行查询操作时,结果都会将所有字段都查询出来,现在我们需要查询某些指定的字段,那么该怎么办?

    @Testpublic void test06(){//查询用户的用户名、年龄和邮箱信息QueryWrapper<User> queryWrapper=new QueryWrapper<>();queryWrapper.select("name","age","email");List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);maps.forEach(System.out::println);}

在这里插入图片描述

  • 组装子查询
   @Testpublic void test07(){//id<=100的用户信息QueryWrapper<User> queryWrapper=new QueryWrapper<>();queryWrapper.inSql("id","select id form user where id <=100");List<User> users = userMapper.selectList(queryWrapper);users.forEach(System.out::println);}

2. UpdateWrapper

  • 使用UpdateWrapper实现修改功能
    @Testpublic void test07(){UpdateWrapper<User> updateWrapper=new UpdateWrapper<>();updateWrapper.like("name","a").and(i->i.gt("age",20).or().isNull("email"));updateWrapper.set("name","小黑");updateWrapper.set("email","xiaohei.qq.com");int update = userMapper.update(null, updateWrapper);}

可以发现UpdateWrapper更新不再需要手动去创建实体类对象,而是直接设置修改类字段

3. 模拟开发中组装条件的情况

在实际情况下,前端传过来的数据不是每个字段都是有效的,所以我们需要对字段进行判断然后进行组装。

@Testpublic void test08() {String username = "";Integer ageBegin = 20;Integer ageEnd = 30;QueryWrapper<User> queryWrapper = new QueryWrapper<>();if (StringUtils.isNotBlank(username)) {//isNotBlank判断某个字符串是否不为空、不为null且不为空字符queryWrapper.like("name", username);}if (ageBegin != null) {queryWrapper.gt("age", ageBegin);}if (ageEnd != null) {queryWrapper.le("age", ageEnd);}userMapper.selectList(queryWrapper);}

上面这种写法相对来说还是比较复杂的,我们需要对每个字段都进行处理,容易发生一些意想不到的错误,下面就介绍一个比较简单的写法。

    @Testpublic void test10() {String username = "";Integer ageBegin = 20;Integer ageEnd = 30;QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.like(StringUtils.isNotBlank(username),"name",username).ge(ageBegin!=null,"age",ageBegin).le(ageEnd!=null,"age",ageEnd);userMapper.selectList(queryWrapper);}

上面代码就是利用了Condition来组装条件

4. LambdaQueryWrapper

 @Testpublic void test11() {String username = "";Integer ageBegin = 20;Integer ageEnd = 30;LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();lambdaQueryWrapper.like(StringUtils.isNotBlank(username), User::getName, username).ge(ageBegin != null, User::getAge, ageBegin).le(ageEnd != null, User::getAge, ageEnd);userMapper.selectList(lambdaQueryWrapper);}

LambdaQueryWrapper让我们写查询条件时,我们属性名都不用记了

4. LambdaUpdateWrapper

@Testpublic void test12() {LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();updateWrapper.like(User::getName, "a").and(i -> i.gt(User::getAge, 20).or().isNull(User::getEmail));updateWrapper.set(User::getName, "小黑");updateWrapper.set(User::getEmail, "xiaohei.qq.com");int update = userMapper.update(null, updateWrapper);}

七、MybatisPlus提供的插件

1. 分页插件

MyBatis Plus自带分页插件,只要简单的配置即可实现分页功能

  • 添加配置类
@Configuration
public class MybaitsPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor(){MybatisPlusInterceptor mybatisPlusInterceptor=new MybatisPlusInterceptor();mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));return mybatisPlusInterceptor;}
}
  • 添加测试类
@Testpublic void testPage(){//设置分页参数Page<User> page = new Page<>(1, 5);userMapper.selectPage(page, null);//获取分页数据List<User> list = page.getRecords();list.forEach(System.out::println);System.out.println("当前页:"+page.getCurrent());System.out.println("每页显示的条数:"+page.getSize());System.out.println("总记录数:"+page.getTotal());System.out.println("总页数:"+page.getPages());System.out.println("是否有上一页:"+page.hasPrevious());System.out.println("是否有下一页:"+page.hasNext());}

在这里插入图片描述

2. 乐观锁插件

  • 场景

一件商品,成本价是80元,售价是100元。老板先是通知小李,说你去把商品价格增加50元。小 李正在玩游戏,耽搁了一个小时。正好一个小时后,老板觉得商品价格增加到150元,价格太 高,可能会影响销量。又通知小王,你把商品价格降低30元。此时,小李和小王同时操作商品后台系统。小李操作的时候,系统先取出商品价格100元;小王 也在操作,取出的商品价格也是100元。小李将价格加了50元,并将100+50=150元存入了数据 库;小王将商品减了30元,并将100-30=70元存入了数据库。是的,如果没有锁,小李的操作就 完全被小王的覆盖了。现在商品价格是70元,比成本价低10元。几分钟后,这个商品很快出售了1千多件商品,老板亏1 万多。

  • 模拟修改冲突

数据库中增加商品表

CREATE TABLE t_product
(
id BIGINT(20) NOT NULL COMMENT '主键ID',
NAME VARCHAR(30) NULL DEFAULT NULL COMMENT '商品名称', price INT(11) DEFAULT 0 COMMENT '价格',
VERSION INT(11) DEFAULT 0 COMMENT '乐观锁版本号', PRIMARY KEY (id)
);

添加数据

INSERT INTO t_product (id, NAME, price) VALUES (1, '外星人笔记本', 100);

添加实体

import lombok.Data;@Data
public class Product {private Long id;private String name;private Integer price;private Integer version;
}

添加mapper

public interface ProductMapper extends BaseMapper<Product> {
}

测试

@Testpublic void testConcurrentUpdate() {//1、小李Product p1 = productMapper.selectById(1L);System.out.println("小李取出的价格:" + p1.getPrice());//2、小王Product p2 = productMapper.selectById(1L);System.out.println("小王取出的价格:" + p2.getPrice());//3、小李将价格加了50元,存入了数据库p1.setPrice(p1.getPrice() + 50);int result1 = productMapper.updateById(p1);System.out.println("小李修改结果:" + result1);//4、小王将商品减了30元,存入了数据库p2.setPrice(p2.getPrice() - 30);int result2 = productMapper.updateById(p2);System.out.println("小王修改结果:" + result2);//最后的结果Product p3 = productMapper.selectById(1L); //价格覆盖,最后的结果:70System.out.println("最后的结果:" + p3.getPrice());}

上面代码的结果是我们不想看到的,我们最终希望看到的结果是70

在这里插入图片描述

  • Mybatis-Plus实现乐观

版本号机制 一般是说在数据表中加上一个数据库版本号version字段,在表述数据被修改的次数当数据被修改时,它的version 值会加1。 如: 当然线程A需要更新数据值时,在读取数据的同时也会读取 version 值,在提交更新时,若刚才读取到的 version 值为当前数据库中的 version 值相等时才更新,否则重试更新操作,直到更新成功。

实体类上标记乐观锁版本号

@Data
public class Product {private Long id;private String name;private Integer price;@Versionprivate Integer version;
}

配置类中进行乐观锁插件配置

   @Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor(){MybatisPlusInterceptor mybatisPlusInterceptor=new MybatisPlusInterceptor();mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());return mybatisPlusInterceptor;}

测试
在这里插入图片描述
结果是150,并不是120,这是因为小王要修改的时候发现版本号和自己手上数据的版本号不同,所以小王会更新失败。

八、MybatisPlus通用枚举

1. 简介

表中有些字段值是固定的,例如性别(男,女),此时我们可以使用MybatisPlus提供的通用枚举来实现。

2. 测试

  • 修改数据库表
    在这里插入图片描述
  • 创建枚举类
@Getter
public enum SexEnum {MALE(0,"男"),FEMALE(1,"女");@EnumValue //将注解所标示的属性的值设置到数据库中private Integer sex;private String  sexName;SexEnum(Integer sex, String sexName){this.sex=sex;this.sexName=sexName;}
}
  • 修改实体类
@Data
public class User {private Long id;private String name;private Integer age;private String email;private SexEnum sex;@TableLogicprivate Integer isDeleted;
}
  • 修改配置文件
mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImplmap-underscore-to-camel-case: true # 开启驼峰命名转换global-config:db-config:id-type: assign_id#      table-prefix: t_##扫描通用枚举包type-enums-package: com.jack.mybatis_plus.enums
  • 测试
 @Testpublic void test(){User user=new User();user.setName("admin");user.setAge(33);user.setSex(SexEnum.MALE);int insert = userMapper.insert(user);System.out.println(insert);}

在这里插入图片描述

九、代码生成器

1. 简介

Mybatis 逆向工程是指由数据库表生成实体类、Mapper接口和映射文件,MybatisPlus可以通过逆向工程生成更多东西,例如控制层、业务层和持久层。

2. 简单案例模拟代码生成

  • 导入依赖
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.5.1</version>
</dependency>
<dependency><groupId>org.freemarker</groupId><artifactId>freemarker</artifactId><version>2.3.31</version>
</dependency>
  • 创建测试类
public class FastAutoGeneratorTest {public static void main(String[] args) {FastAutoGenerator.create("jdbc:mysql://127.0.0.1:3306/mybatis_plus? characterEncoding=utf-8&userSSL=false", "root", "123456").globalConfig(builder-> {builder.author("jackiechai") // 设置作者 //.enableSwagger() // 开启 swagger 模式.fileOverride() // 覆盖已生成文件.outputDir("/Users/jackchai/Desktop/sundries/Mybatis-plus-learning/mybatis_plus/src/test/java/com/jack/mybatis_plus"); // 指定输出目录}).packageConfig(builder -> {builder.parent("com.jack") // 设置父包.moduleName("mybatis_plus") // 设置父包模块名.pathInfo(Collections.singletonMap(OutputFile.mapperXml, "/Users/jackchai/Desktop/sundries/Mybatis-plus-learning/mybatis_plus/src/test/java/com/jack/mybatis_plus")); // 设置mapperXml生成路径}).strategyConfig(builder -> {builder.addInclude("user");// 设置需要生成的表名}).templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker 引擎模板,默认的是Velocity引擎模板.execute();}
}

可以看到逆向工程生成完毕了

在这里插入图片描述

十、多数据源

1. 场景

MybatisPlus多数据源功能适合多个场景,例如:纯粹多库读写分离一主多从以及混合模式等。目前我们模拟纯粹多库这个场景,其它场景都是类似的。首先我们创建两个库,分别为mybatis_plus与mybatis_plus_1,将mybatis_plus的product表移动到mybatis_plus_1,这样每个库一张表,通过一个测试用例分别获区用户数据和商品数据,如果获取成功就说吗多库模拟成功。

2. 多数据源使用案例

  • 创建数据库和表
CREATE DATABASE `mybatis_plus_1` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;use `mybatis_plus_1`;CREATE TABLE product(id BIGINT(20) NOT NULL COMMENT '主键ID',name VARCHAR(30) NULL DEFAULT NULL COMMENT '商品名称', price INT(11) DEFAULT 0 COMMENT '价格',version INT(11) DEFAULT 0 COMMENT '乐观锁版本号', PRIMARY KEY (id)
);
  • 插入数据
INSERT INTO product (id, NAME, price) VALUES (1, '外星人笔记本', 100);
  • 引入依赖
<dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>3.5.0</version>
</dependency>
  • 配置多数据源
spring:datasource:# 配置数据源信息 datasource:dynamic:# 设置默认的数据源或者数据源组,默认值即为masterprimary: master# 严格匹配数据源,默认false.true未匹配到指定数据源时抛异常,false使用默认数据源 strict: falsestrict: falsedatesource: master:url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=falsedriver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: 123456slave_1:url: jdbc:mysql://localhost:3306/mybatis_plus_1?characterEncoding=utf-8&useSSL=falsedriver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: 123456
  • 创建用户service

public interface UserService extends IService<User> {
}@DS("master")  //指定所操作的数据源
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
  • 创建商品service
public interface ProductService extends IService<Product> {
}
@DS("slave_1")
@Service
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService {
}
  • 测试
@SpringBootTest
public class MybaitisPlusDybamicTest {@Autowiredprivate UserService userService;@Autowiredprivate ProductService productService;@Testpublic void testDynamicDataSource(){System.out.println(userService.getById(1L));System.out.println(productService.getById(1L));}
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/499405.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java玩转《啊哈算法》暴力枚举之炸弹人

当一个人身强力壮、洋溢着青春气息的时候&#xff0c;要做到坚强是比较简单和容易的。只有当生活像铁环一样把你紧紧箍住的时候&#xff0c;能做到坚强才是光荣的。 有目录&#xff0c;不迷路 缘起代码地址炸弹人问题 缘起 各位小伙伴们好呀&#xff01;本人最近看了下《啊哈算…

如何使用Docker部署IT-Tools并结合内网穿透实现公网访问本地工具箱服务

作为程序员&#xff0c;在日常工作中&#xff0c;需要借助一些工具来提高我们工作效率&#xff0c;IT-Tools是为开发人员度身打造的一套便捷在线工具。它提供全面功能&#xff0c;使开发者能以更高效方式完成任务。经由IT-Tools&#xff0c;开发人员能轻松应对各类技术挑战&…

Linux网络编程(四-TCP协议)

目录 一、TCP概念 二、TCP的首部格式 三、TCP可靠传输机制 3.1 确认应答机制 3.2 超时重传机制 3.3 连接管理 3.3.1 三次握手 3.3.2 四次挥手 3.4 流量控制 3.5 拥塞控制 四、TCP效率机制 4.1 滑动窗口 4.2 重发控制 4.3 延迟应答 4.4 捎带应答 五、TCP的…

利用观测云实现 Kubernetes 多集群可观测

简介 观测云的工作空间接入多个 Kubernetes 集群时&#xff0c;是如何区分不同集群&#xff0c;达到多集群的可观测性&#xff1f; 增加Tag NAMESPACE&#xff1a;DataKit 选举空间&#xff0c;需要设置 ENV_NAMESPACE 环境变量&#xff0c;值为非空字符&#xff0c;不同集群…

第三百七十回

文章目录 1. 概念介绍2. 使用方法2.1 获取所有时区2.2 转换时区时间 3. 示例代码4. 内容总结 我们在上一章回中介绍了"分享一些好的Flutter站点"相关的内容&#xff0c;本章回中将介绍timezone包.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 我们在…

力扣递归:路径总和

思路&#xff1a;此题思路为递归实现&#xff0c;递归思路为&#xff1a;在每层递归的过程中将各个节点的数据记录下来&#xff0c;不断将减少目标数据的值准备进行判断&#xff0c;当进行到叶子节点时要进行判断 /*** Definition for a binary tree node.* struct TreeNode {…

服务式办公室联合办公空间,傻傻分不清楚

服务式办公室是一种灵活的办公解决方案&#xff0c;适合各种规模和类型的企业&#xff0c;尤其是那些寻求成本效益和灵活租赁条件的新兴企业。以下是服务式办公室的主要使用者和设施特点&#xff1a; 谁会使用服务式办公室&#xff1f; 新兴初创企业&#xff1a;由于初创企业在…

面试笔记系列四之SpringBoot+SpringCloud+计算机网络基础知识点整理及常见面试题

目录 Spring Boot 什么是 Spring Boot&#xff1f; Spring Boot 有哪些优点&#xff1f; SpringBootApplication注解 Spring Boot 的启动流程 Spring Boot属性加载顺序 springboot自动配置原理是什么&#xff1f;&#xff08;*&#xff09; 如何理解springboot中的start…

Leetcode—64. 最小路径和【中等】

2024每日刷题&#xff08;116&#xff09; Leetcode—64. 最小路径和 实现代码 class Solution { public:int minPathSum(vector<vector<int>>& grid) {int m grid.size();int n grid[0].size();vector<vector<int>> dp(m 1, vector<int&g…

配电房智能辅助监控系统设计

业务背景 工业企业、学校、医院、居民小区等单位有这海量的配电房&#xff0c;这些配电房内的配电设备种类多、运行环境复杂&#xff0c;存在各种各样的安全隐患。目前这些配电房主要依靠人员在场值守或巡检方式进行管理&#xff0c;但单纯的人工运维方式既成本高&#xff0c;…

pycharm基本操作,零基础快速上手

新建项目 pycharm安装完成后&#xff0c;双击pycharm的图标&#xff0c;打开pycharm。如果是首次使用的话选择Create New Project创建一个新项目。 进入pycharm后也可以通过以下方式新建一个项目&#xff0c;点击菜单栏File–New Project。 2. 选择项目路径和python环境&…

C++设计模式——抽象工厂模式

文章目录 抽象工厂模式的主要组成部分抽象工厂模式的一个典型例子抽象工厂模式用于其他场景抽象工厂模式与其他设计模式结合使用 C 中的抽象工厂模式是一种创建型设计模式&#xff0c;它主要用于处理对象家族的创建&#xff0c;这些对象之间可能存在一定的关联关系或属于相同的…