MyBatis Plus
开发方式:
基于MyBatis使用MyBatisPlus
基于Spring使用MyBatisPlus
基于SpringBoot使用MyBatisPlus
SpringBoot整合MyBatis开发过程(复习):
1.创建SpringBoot工程
2.勾选配置使用的技术
3.设置dataSource相关属性(JDBC参数)
4.定义数据层接口映射配置
创建新模块,选择Spring初始化,并配置模块相关基础信息
2.选择当前模块需要使用的技术集(仅保留JDBC)
3.在pom.xml中
1 <dependency> 2 <groupId>com.baomidou</groupId> 3 <artifactId>mybatis-plus-boot-starter</artifactId> 4 <version>3.4.1</version> 5 </dependency> 6 <dependency> 7 <groupId>com.alibaba</groupId> 8 <artifactId>druid</artifactId> 9 <version>1.1.16</version> 10 </dependency>
注:
由于mp并未被收录到idea的系统内置配置,无法直接选择加入
如果使用Druid数据源,需要导入对应坐标
4.制作实体类与表结构(类名与表名对应,属性名与字段名对应)
1 public class User { 2 private Long id; 3 private String name; 4 private String password; 5 private Integer age; 6 private String tel; 7 ... //添加getter、setter、toString()等方法 8 }
5.
1 spring: 2 datasource: 3 type: com.alibaba.druid.pool.DruidDataSource 4 driver-class-name: com.mysql.cj.jdbc.Driver 5 url: jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC 6 username: root 7 password: root
6.
1 @Mapper 2 public interface UserDao extends BaseMapper<User> { 3 }
7.
1 @SpringBootTest 2 public class Mybatisplus01QuickstartApplicationTests { 3 4 @Autowired 5 private UserDao userDao; 6 7 @Test 8 void testGetAll() { 9 List<User> userList = userDao.selectList(null); 10 System.out.println(userList); 11 } 12 }
MyBatisPlus(简称MP)是基于MyBatis框架基础上开发的增强型工具,旨在简化开发、提高效率
官网:https://mybatis.plus/ https://mp.baomidou.com/
无侵入:只做增强不做改变,不会对现有工程产生影响
强大的 CRUD 操作:内置通用 Mapper,少量配置即可实现单表CRUD 操作
支持 Lambda:编写查询条件无需担心字段写错
支持主键自动生成
内置分页插件
……
1 @SpringBootTest 2 class Mybatisplus01QuickstartApplicationTests { 3 @Autowired 4 private UserDao userDao; 5 6 @Test 7 void testSave() { 8 User user = new User(); 9 user.setName("程序员"); 10 user.setPassword("it"); 11 user.setAge(12); 12 user.setTel("4006184000"); 13 userDao.insert(user); 14 } 15 16 @Test 17 void testDelete() { 18 userDao.deleteById(1401856123725713409L); 19 } 20 21 @Test 22 void testUpdate() { 23 User user = new User(); 24 user.setId(1L); 25 user.setName("Tom888"); 26 user.setPassword("tom888"); 27 userDao.updateById(user); 28 } 29 30 @Test 31 void testGetById() { 32 User user = userDao.selectById(2L); 33 System.out.println(user); 34 } 35 36 @Test 37 void testGetAll() { 38 List<User> userList = userDao.selectList(null); 39 System.out.println(userList); 40 } 41 }
1 //查询指定多条数据 2 List<Long> list = new ArrayList<>(); 3 list.add(1L); 4 list.add(3L); 5 list.add(4L); 6 userDao.selectBatchIds(list);
1 @Configuration 2 public class MybatisPlusConfig { 3 @Bean 4 public MybatisPlusInterceptor mybatisPlusInterceptor(){ 5 //1 创建MybatisPlusInterceptor拦截器对象 6 MybatisPlusInterceptor mpInterceptor=new MybatisPlusInterceptor(); 7 //2 添加分页拦截器 8 mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor()); 9 return mpInterceptor; 10 } 11 }
2.执行分页查询
1 @Test 2 void testSelectPage(){ 3 //1 创建IPage分页对象,设置分页参数 4 IPage<User> page=new Page<>(1,3); 5 //2 执行分页查询 6 userDao.selectPage(page,null); 7 //3 获取分页结果 8 System.out.println("当前页码值:"+page.getCurrent()); 9 System.out.println("每页显示数:"+page.getSize()); 10 System.out.println("总页数:"+page.getPages()); 11 System.out.println("总条数:"+page.getTotal()); 12 System.out.println("当前页数据:"+page.getRecords()); 13 }
1 # 开启mp的日志(输出到控制台) 2 mybatis-plus: 3 configuration: 4 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
取消初始化spring日志打印
做法:在resources下新建一个logback.xml文件,名称固定,内容如下(configuration里不写内容):
1 <?xml version="1.0" encoding="UTF-8"?> 2 <configuration> 3 4 </configuration>
spring:
main:
banner-mode: off # 关闭SpringBoot启动图标(banner)
# mybatis-plus日志控制台输出
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
banner: off # 关闭mybatisplus启动图标
Lombok,一个Java类库,提供了一组注解,简化POJO实体类开发。
1 <dependency> 2 <groupId>org.projectlombok</groupId> 3 <artifactId>lombok</artifactId> 4 <version>1.18.12</version> 5 <scope>provided</scope> <!--不参与打包--> 6 </dependency>
常用注解:@Data
为当前实体类在编译期设置对应的get/set方法,无参构造方法,toString方法,hashCode方法,equals方法等。只有加上@AllArgsConstructor:生成全参构造; @NoArgsConstructor:生成空参构造。
1 /* 2 1 生成getter和setter方法:@Getter、@Setter 3 生成toString方法:@ToString 4 生成equals和hashcode方法:@EqualsAndHashCode 5 6 2 统一成以上所有:@Data 7 8 3 生成空参构造: @NoArgsConstructor 9 生成全参构造: @AllArgsConstructor 10 11 4 lombok还给我们提供了builder的方式创建对象,好处就是可以链式编程。 @Builder【扩展】 12 */ 13 @Data 14 public class User { 15 private Long id; 16 private String name; 17 private String password; 18 private Integer age; 19 private String tel; 20 }
1 QueryWrapper<User> qw = new QueryWrapper<User>(); 2 //查询年龄大于等于18岁,小于65岁的用户 3 qw.lt("age",65); 4 qw.gt("age",18); 5 List<User> userList = userDao.selectList(qw); 6 System.out.println(userList);
格式二:链式编程格式
1 QueryWrapper<User> qw = new QueryWrapper<User>(); 2 //查询年龄大于等于18岁,小于65岁的用户 3 qw.lt("age",65).gt("age",18); 4 List<User> userList = userDao.selectList(qw); 5 System.out.println(userList);
格式三:lambda格式
1 QueryWrapper<User> qw = new QueryWrapper<User>(); 2 //查询年龄大于等于18岁,小于65岁的用户 3 qw.lambda().lt(User::getAge,65).gt(User::getAge,18); 4 List<User> userList = userDao.selectList(qw); 5 System.out.println(userList);
格式四:lambda格式(推荐)
1 LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>(); 2 //查询年龄大于等于18岁,小于65岁的用户 3 lqw.lt(User::getAge,65).gt(User::getAge,18); 4 List<User> userList = userDao.selectList(lqw); 5 System.out.println(userList);
1 LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>(); 2 //并且关系:10到30岁之间 3 lqw.lt(User::getAge, 30).gt(User::getAge, 10); 4 List<User> userList = userDao.selectList(lqw); 5 System.out.println(userList);
或者关系(or)
1 LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>(); 2 //或者关系:小于10岁或者大于30岁 3 lqw.lt(User::getAge, 10).or().gt(User::getAge, 30); 4 List<User> userList = userDao.selectList(lqw); 5 System.out.println(userList);
NULL值处理
1 Integer minAge=10; //将来有用户传递进来,此处简化成直接定义变量了 2 Integer maxAge=null; //将来有用户传递进来,此处简化成直接定义变量了 3 LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>(); 4 if(minAge!=null){ 5 lqw.gt(User::getAge, minAge); 6 } 7 if(maxAge!=null){ 8 lqw.lt(User::getAge, maxAge); 9 } 10 List<User> userList = userDao.selectList(lqw); 11 userList.forEach(System.out::println);
1 Integer minAge=10; //将来有用户传递进来,此处简化成直接定义变量了 2 Integer maxAge=null; //将来有用户传递进来,此处简化成直接定义变量了 3 LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>(); 4 //参数1:如果表达式为true,那么查询才使用该条件 5 lqw.gt(minAge!=null,User::getAge, minAge); 6 lqw.lt(maxAge!=null,User::getAge, maxAge); 7 List<User> userList = userDao.selectList(lqw); 8 userList.forEach(System.out::println);
1 Integer minAge=10; //将来有用户传递进来,此处简化成直接定义变量了 2 Integer maxAge=null; //将来有用户传递进来,此处简化成直接定义变量了 3 LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>(); 4 //参数1:如果表达式为true,那么查询才使用该条件 5 lqw.gt(minAge!=null,User::getAge, minAge) 6 .lt(maxAge!=null,User::getAge, maxAge); 7 List<User> userList = userDao.selectList(lqw); 8 userList.forEach(System.out::println);
1 LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>(); 2 lqw.select(User::getId,User::getName,User::getAge); 3 List<User> userList = userDao.selectList(lqw); 4 System.out.println(userList);
或
1 QueryWrapper<User> lqw = new QueryWrapper<User>(); 2 lqw.select("id", "name", "age", "tel"); 3 List<User> userList = userDao.selectList(lqw); 4 System.out.println(userList);
1 QueryWrapper<User> lqw = new QueryWrapper<User>(); 2 lqw.select("count(*) as count, tel"); 3 lqw.groupBy("tel"); 4 List<Map<String, Object>> userList = userDao.selectMaps(lqw); 5 System.out.println(userList);
范围匹配(> 、 = 、between)
模糊匹配(like)
空判定(null)
包含性匹配(in)
分组(group)
排序(order)
……
1 LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>(); 2 //等同于= 3 lqw.eq(User::getName, "Jerry").eq(User::getPassword, "jerry"); 4 User loginUser = userDao.selectOne(lqw); 5 System.out.println(loginUser);
购物设定价格区间、户籍设定年龄区间(le ge匹配 或 between匹配)
1 LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>(); 2 //范围查询 lt le(等同于lt + eq) gt ge(等同于gt + eq) eq between 3 lqw.between(User::getAge, 10, 30); 4 List<User> userList = userDao.selectList(lqw); 5 System.out.println(userList);
查信息,搜索新闻(非全文检索版:like匹配)
1 LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>(); 2 //模糊匹配 like likeLeft(等同于like '%J') likeRight(等同于 like 'J%') 3 lqw.likeLeft(User::getName, "J"); 4 List<User> userList = userDao.selectList(lqw); 5 System.out.println(userList);
统计报表(分组查询聚合函数)
1 QueryWrapper<User> qw = new QueryWrapper<User>(); 2 qw.select("gender","count(*) as nums"); 3 qw.groupBy("gender"); 4 List<Map<String, Object>> maps = userDao.selectMaps(qw); 5 System.out.println(maps);
更多查询条件设置参看 https://mybatis.plus/guide/wrapper.html#abstractwrapper
2.
3.
4.
@TableField
类型:属性注解
位置:模型类属性定义上方
作用:设置当前属性对应的数据库表中的字段关系
相关属性:
value:设置数据库表字段名称
exist:设置属性在数据库表字段中是否存在,默认为true。此属性无法与value合并使用
select:设置属性是否参与查询,此属性与select()映射配置不冲突
@TableName
类型:类注解
位置:模型类定义上方
作用:设置当前类对应与数据库表关系
相关属性:
value:设置数据库表名称
1 @Data 2 @TableName("tbl_user") 3 public class User { 4 /* 5 id为Long类型,因为数据库中id为bigint类型, 6 并且mybatis有自己的一套id生成方案,生成出来的id必须是Long类型 7 */ 8 private Long id; 9 private String name; 10 @TableField(value = "pwd",select = false) 11 private String password; 12 private Integer age; 13 private String tel; 14 @TableField(exist = false) //表示online字段不参与CRUD操作 15 private Boolean online; 16 }
@TableId
类型:属性注解
位置:模型类中用于表示主键的属性定义上方
作用:设置当前类中主键属性的生成策略
相关属性:
value:设置数据库主键名称
type:设置主键属性的生成策略,值参照IdType枚举值
1 public class User { 2 @TableId(type = IdType.AUTO) 3 private Long id; 4 }
AUTO(0):使用数据库id自增策略控制id生成
NONE(1):不设置id生成策略
INPUT(2):用户手工输入id
ASSIGN_ID(3):雪花算法生成id(可兼容数值型与字符串型)
ASSIGN_UUID(4):以UUID生成算法作为id生成策略
默认ASSIGN_ID,雪花算法:
id生成策略全局配置:
表名前缀全局配置:
全局配置:
1 mybatis-plus: 2 global-config: 3 db-config: 4 id-type: assign_id #配置所有id生成策略为:ASSIMG_ID。 5 table-prefix: tbl_ #表示实体类上的表名可以不用写tbl_,直接写实体类名(例:@TableName("user"))
1 List<Long> list = new ArrayList<>(); 2 list.add(1402551342481838081L); 3 list.add(1402553134049501186L); 4 list.add(1402553619611430913L); 5 userDao.deleteBatchIds(list);
删除操作业务问题:业务数据从数据库中丢弃。
逻辑删除:为数据设置是否可用状态字段,删除时设置状态字段为不可用状态,数据保留在数据库中。
1 @Data 2 public class User { 3 private Long id; 4 //逻辑删除字段,标记当前记录是否被删除(value表示默认逻辑未删除值,delval表示默认逻辑删除值) 5 @TableLogic(value='0',delval='1') 6 private Integer deleted; 7 }
3.可批量
1 mybatis-plus: 2 global-config: 3 db-config: 4 table-prefix: tbl_ 5 # 逻辑删除字段名 6 logic-delete-field: deleted 7 # 逻辑删除字面值:未删除为0 8 logic-not-delete-value: 0 9 # 逻辑删除字面值:删除为1 10 logic-delete-value: 1
逻辑删除本质:逻辑删除的本质其实是修改操作。如果加了逻辑删除字段,查询数据时也会自动带上逻辑删除字段。
2.
1 @Data 2 public class User { 3 private Long id; 4 @Version 5 private Integer version; 6 }
3.
1 @Configuration 2 public class MpConfig { 3 @Bean 4 public MybatisPlusInterceptor mpInterceptor() { 5 //1.定义Mp拦截器 6 MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor(); 7 //2.添加乐观锁拦截器 8 mpInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); 9 return mpInterceptor; 10 } 11 }
4.使用乐观锁机制在修改前必须先获取到对应数据的verion方可正常进行
方式一:
1 @Test 2 public void testUpdate() { 3 User user = new User(); 4 user.setId(3L); 5 user.setName("Jock666"); 6 user.setVersion(1); //需设置此条数据version字段的值 7 userDao.updateById(user); 8 }
方式二:
1 @Test 2 public void testUpdate() { 3 //1.先通过要修改的数据id将当前数据查询出来,间接获取到version的值 4 User user = userDao.selectById(3L); 5 //2.将要修改的属性逐一设置进去 6 user.setName("Jock888"); 7 userDao.updateById(user); 8 }
原理
1 @Test 2 public void testUpdate() { 3 //1.先通过要修改的数据id将当前数据查询出来 4 User user = userDao.selectById(3L); //当前执行条件version=3 5 User user2 = userDao.selectById(3L); //当前执行条件version=3 6 user2.setName("Jock aaa"); 7 userDao.updateById(user2); //执行此句后version变为4 8 user.setName("Jock bbb"); 9 userDao.updateById(user); //执行条件verion=3 条件已不成立,无法执行该语句 10 }
模板:MyBatisPlus提供
数据库相关配置:读取数据库获取信息
开发者自定义配置:手工配置
第一步:创建SpringBoot工程,在pom.xml中添加代码生成器相关依赖,其他依赖自行添加
1 <!--代码生成器--> 2 <dependency> 3 <groupId>com.baomidou</groupId> 4 <artifactId>mybatis-plus-generator</artifactId> 5 <version>3.4.1</version> 6 </dependency> 7 8 <!--velocity模板引擎--> 9 <dependency> 10 <groupId>org.apache.velocity</groupId> 11 <artifactId>velocity-engine-core</artifactId> 12 <version>2.3</version> 13 </dependency>
第二步:编写代码生成器类
1 public class Generator { 2 public static void main(String[] args) { 3 //1. 创建代码生成器对象,执行生成代码操作 4 AutoGenerator autoGenerator = new AutoGenerator(); 5 6 //2. 数据源相关配置:读取数据库中的信息,根据数据库表结构生成代码 7 DataSourceConfig dataSource = new DataSourceConfig(); 8 dataSource.setDriverName("com.mysql.cj.jdbc.Driver"); 9 dataSource.setUrl("jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC"); 10 dataSource.setUsername("root"); 11 dataSource.setPassword("root"); 12 autoGenerator.setDataSource(dataSource); 13 14 //3. 执行生成操作 15 autoGenerator.execute(); 16 } 17 }
设置全局配置
1 //设置全局配置 2 GlobalConfig globalConfig = new GlobalConfig(); 3 globalConfig.setOutputDir(System.getProperty("user.dir")+"/mybatisplus_04_generator/src/main/java"); //设置代码生成位置 4 globalConfig.setOpen(false); //设置生成完毕后是否打开生成代码所在的目录 5 globalConfig.setAuthor("程序员"); //设置作者 6 globalConfig.setFileOverride(true); //设置是否覆盖原始生成的文件 7 globalConfig.setMapperName("%sDao"); //设置数据层接口名,%s为占位符,指代模块名称 8 globalConfig.setIdType(IdType.ASSIGN_ID); //设置Id生成策略 9 autoGenerator.setGlobalConfig(globalConfig);
设置包名相关配置
1 //设置包名相关配置 2 PackageConfig packageInfo = new PackageConfig(); 3 packageInfo.setParent("com.aaa"); //设置生成的包名,与代码所在位置不冲突,二者叠加组成完整路径 4 packageInfo.setEntity("domain"); //设置实体类包名 5 packageInfo.setMapper("dao"); //设置数据层包名 6 autoGenerator.setPackageInfo(packageInfo);
策略设置
1 //策略设置 2 StrategyConfig strategyConfig = new StrategyConfig(); 3 strategyConfig.setInclude("tbl_user"); //设置当前参与生成的表名,参数为可变参数 4 strategyConfig.setTablePrefix("tbl_"); //设置数据库表的前缀名称,模块名 = 数据库表名 - 前缀名 例如: User = tbl_user - tbl_ 5 strategyConfig.setRestControllerStyle(true); //设置是否启用Rest风格 6 strategyConfig.setVersionFieldName("version"); //设置乐观锁字段名 7 strategyConfig.setLogicDeleteFieldName("deleted"); //设置逻辑删除字段名 8 strategyConfig.setEntityLombokModel(true); //设置是否启用lombok 9 autoGenerator.setStrategy(strategyConfig);