赞
踩
her~~llo,我是你们的好朋友Lyle,是名梦想成为计算机大佬的男人!
博客是为了记录自我的学习历程,加强记忆方便复习,如有不足之处还望多多包涵!非常欢迎大家的批评指正。
今天开始学习MyBatis Plus。主要有以下内容。
- MyBatisPlus简介
- 标准数据层开发
- DQL控制
- DML控制
- 快速开发
我们开始吧。
目录
MyBatisPlus(简称MP)是基于MyBatis框架基础上开发的增强型工具,旨在简化开发、提高效率。
官网:
MyBatis-Plus (baomidou.com)
https://baomidou.com/
特性:
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
- 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
- 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
- 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
- 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
- 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
- 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
- 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
- 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
- 内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
- 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
①创建新模块,选择Spring初始化,并配置模块相关基础信息
②选择当前模块需要使用的技术集(仅保留JDBC)
③手动添加MP起步依赖
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.2</version> </dependency>④设置jdbc参数(application.yml)
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/brand?useSSL=false username: root password: 123456 type: com.alibaba.druid.pool.DruidDataSource⑤制作实体类与表结构(类名与表名对应,属性名与字段名对应)
⑥定义数据接口,继承BaseMapper<User>
@Mapper public interface BookDao extends BaseMapper<Book> { }⑦测试类中注入dao接口,测试功能
@SpringBootTest class MybatisPlus01QuickstartApplicationTests { @Autowired private BookDao bookDao; @Test void testGetAll() { List<Book> books = bookDao.selectList(null); books.forEach(System.out::println); } }注意事项:
- 由于MP并未被收录到idea的系统内置配置,无法直接选择加入
- 如果使用Druid数据源,需要导入对应坐标
@Test void testSelectById(){ System.out.println(bookDao.selectById(1)); } @Test void testSave(){ Book book = new Book(); book.setName("从你的全世界路过"); book.setPrice(100.00); book.setType("言情"); bookDao.insert(book); } @Test void testDelete(){ List<Integer> integerList = new ArrayList<Integer>(); integerList.add(12); integerList.add(13); bookDao.deleteBatchIds(integerList); } @Test void testUpdate(){ Book book = new Book(); book.setId(9); book.setPrice(50.00); bookDao.updateById(book); }
Lombok,一个Java类库,提供了一组注解,简化POJ0实体类开发。
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.24</version> <scope>provided</scope> </dependency>
@Data //功能包含下面的注解 //@Setter //@Getter //@ToString //@NoArgsConstructor //@AllArgsConstructor //@EqualsAndHashCode public class Book { private String name; private Double price; private Integer id; private String type; }常用注解:@Data
为当前实体类在编译期设置对应的get/set方法,无参/有参构造方法,toString方法,hashCode方法,equals方法等。
①设置分页拦截器作为Spring管理的bean
@Configuration public class MpConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){ //定义MP拦截器 MybatisPlusInterceptor mybatisPlusInterceptor =new MybatisPlusInterceptor(); //添加具体的拦截器 mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor()); return mybatisPlusInterceptor; } }②执行分页查询
@Test void testGetByPage(){ IPage page = new Page(1,5); bookDao.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()); }开启日志(application.yml中)
#开启MP的日志输出 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
MyBatisPlus将书写复杂的SQL查询条件进行了封装,使用编程的形式完成查询条件的组合
查询方式
查询格式一:常规格式
QueryWrapper queryWrapper=new QueryWrapper(); queryWrapper.lt("price",60); queryWrapper.gt("price",20); List<Book> books = bookDao.selectList(queryWrapper); books.forEach(System.out::println);查询格式二:lambda格式
QueryWrapper<Book> queryWrapper=new QueryWrapper<Book>(); queryWrapper.lambda().lt(Book::getPrice,50); List<Book> books1 = bookDao.selectList(queryWrapper); books1.forEach(System.out::println);查询格式三:lambda格式
LambdaQueryWrapper<Book> lambdaQueryWrapper = new LambdaQueryWrapper<Book>(); //价格大于20小于50 lambdaQueryWrapper.gt(Book::getPrice,20); lambdaQueryWrapper.lt(Book::getPrice,50); //价格小于40或者大于50 lambdaQueryWrapper.gt(Book::getPrice,50).or().lt(Book::getPrice,40); List<Book> books2 = bookDao.selectList(lambdaQueryWrapper); books2.forEach(System.out::println);都支持链式编程。
QueryWrapper queryWrapper=new QueryWrapper(); queryWrapper.lt("price",60).gt("price",20); List<Book> books = bookDao.selectList(queryWrapper); books.forEach(System.out::println);查询关系
并且(and)连着写就可以。
或者(or)在中间加个or()。
lambdaQueryWrapper.gt(Book::getPrice,50); lambdaQueryWrapper.or(); lambdaQueryWrapper.lt(Book::getPrice,40);处理null值
if条件控制(不推荐),就是在当前代码行前加if语句。
推荐使用:条件参数控制(条件为true,连接当前条件,否则不连接)。
lambdaQueryWrapper.gt(null!=book.getPrice(),Book::getPrice,20);
查询结果包含模型类中部分属性
//查询投影(lambda格式) LambdaQueryWrapper<Book> lambdaQueryWrapper = new LambdaQueryWrapper<Book>(); lambdaQueryWrapper.select(Book::getPrice,Book::getId); List<Book> books1 = bookDao.selectList(lambdaQueryWrapper); books1.forEach(System.out::println);查询结果包含模型类中未定义的属性(比如查询总共多少条数据)
QueryWrapper<Book> queryWrapper = new QueryWrapper<Book>(); queryWrapper.select("count(*) as count,type"); queryWrapper.groupBy("type"); List<Map<String, Object>> maps = bookDao.selectMaps(queryWrapper); maps.forEach(System.out::println);
等号匹配eq
LambdaQueryWrapper<Book> lambdaQueryWrapper = new LambdaQueryWrapper<Book>(); lambdaQueryWrapper.eq(Book::getName,"我是神").eq(Book::getType,"玄幻"); Book book = bookDao.selectOne(lambdaQueryWrapper); System.out.println(book);范围查询 lt le gt ge eq between
//范围查询 lt le gt ge eq between LambdaQueryWrapper<Book> lambdaQueryWrapper = new LambdaQueryWrapper<Book>(); lambdaQueryWrapper.between(Book::getPrice,40,60); List<Book> bookList = bookDao.selectList(lambdaQueryWrapper); bookList.forEach(System.out::println);模糊匹配
//模糊匹配 LambdaQueryWrapper<Book> lambdaQueryWrapper = new LambdaQueryWrapper<Book>(); lambdaQueryWrapper.like(Book::getName,"我"); lambdaQueryWrapper.likeLeft(Book::getName,"我"); lambdaQueryWrapper.likeRight(Book::getName,"我"); List<Book> bookList = bookDao.selectList(lambdaQueryWrapper); bookList.forEach(System.out::println);分组查询聚合函数
QueryWrapper<Book> queryWrapper = new QueryWrapper<Book>(); queryWrapper.select("count(*) as count,type"); queryWrapper.groupBy("type"); List<Map<String, Object>> maps = bookDao.selectMaps(queryWrapper); maps.forEach(System.out::println);其他条件查询可以自行搜索学习。
问题一:表字段与编码属性设计不同步
问题二:编码中添加了数据库中未定义的属性
问题三:采用默认查询开放了更多的字段查看权限
问题四:表名与编码开发设计不同步
名称:@TableField
类型:属性注解
位置:模型类属性定义上方
作用:设置当前属性对应的数据库表中的字段关系
相关属性:
- value (默认):设置数据库表字段名称
- exist:设置属性在数据库表字段中是否存在,默认为true。此属性无法与value合并使用
- select:设置属性是否参与查询,此属性与select()映射配置不冲突
名称:@TableName
类型:类注解
位置:模型类定义上方
作用:设置当前类对应与数据库表关系
相关属性
- value:设置数据库表名称
public class Book { @TableId(type = IdType.AUTO) private Integer id; }名称:@TableId
类型:属性注解
位置:模型类中用于表示主键的属性定义上方作用:设置当前类中主键属性的生成策略
相关属性
- value:设置数据库主键名称
- type:设置主键属性的生成策略,值参照IdType枚举值
IdType枚举值:
- AUTO(0)︰使用数据库id自增策略控制id生成
- NONE(1)︰不设置id生成策略
- INPUT(2):用户手工输入id
- ASSIGN_ID(3)︰雪花算法生成id(可兼容数值型与字符串型)
- ASSIGN_UUID(4):以UUID生成算法作为id生成策略
对雪花算法以及UUID生成算法感兴趣的小伙伴请自行查看学习。
还可以在application.yml中进行id生成策略以及表名前缀的全局设定(好用):
mybatis-plus: global-config: db-config: #id生成策略 id-type: auto #表名前缀 table-prefix: tb_
按照主键删除多条记录
List<Integer> integerList = new ArrayList<Integer>(); integerList.add(1); integerList.add(2); bookDao.deleteBatchIds(integerList);根据主键查询多条记录
List<Integer> integerList = new ArrayList<Integer>(); integerList.add(1); integerList.add(2); bookDao.selectBatchIds(integerList);
删除操作业务问题:删除某些记录时,相关的业务数据从数据库中丢弃,而不应该被丢弃。
逻辑删除:为数据设置是否可用状态字段,删除时设置状态字段为不可用状态,数据保留在数据库中
逻辑删除案例
①数据库表中添加逻辑删除标记字段
(可选)②实体类中添加对应字段,并设定当前字段为逻辑删除标记字段
public class Book { @TableLogic(value = "0",delval = "1") private Integer deleted; }(可选,推荐)③配置逻辑删除字段值
mybatis-plus: global-config: db-config: logic-delete-field: deleted logic-not-delete-value: 0 logic-delete-value: 1底层实现其实还是update。
业务并发现象带来的问题:秒杀
乐观锁案例
①数据库表中添加锁标记字段
②实体类中添加对应字段,并设定当前字段为逻辑删除标记字段
public class Book { @Version private Integer version; }③配置乐观锁拦截器实现锁机制对应的动态SQL语句拼装
@Configuration public class MpConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){ //定义MP拦截器 MybatisPlusInterceptor mybatisPlusInterceptor =new MybatisPlusInterceptor(); //添加乐观锁拦截器 mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return mybatisPlusInterceptor; } }④使用乐观锁机制在修改前必须先获取到对应数据的verion方可正常进行
@Test void testUpdate(){ Book book1 = bookDao.selectById(5); Book book2 = bookDao.selectById(5); book2.setName("NININIaaa"); bookDao.updateById(book2); book1.setName("NININIbbb"); bookDao.updateById(book1); }
模板:MyBatisPlus提供
数据库相关配置:读取数据库获取信息开发者自定义配置: 手工配置
①导入坐标
<!--代码生成器--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.4.0</version> </dependency> <!--velocity模板引擎--> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <version>2.3</version> </dependency>②//创建代码生成器对象,进行配置,执行生成代码操作
public class Generator { public static void main(String[] args) { //为AutoGenerator添加dataSource,读取数据库 AutoGenerator autoGenerator = new AutoGenerator(); DataSourceConfig dataSource = new DataSourceConfig(); dataSource.setDriverName("com.mysql.cj.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/brand?serverTimezone=UTC"); dataSource.setUsername("root"); dataSource.setPassword("123456"); autoGenerator.setDataSource(dataSource); //设置全局配置 GlobalConfig globalConfig = new GlobalConfig(); globalConfig.setOutputDir(System.getProperty("user.dir")+"/MybatisPlus_01_quickstart/src/main/java");//设置生成代码的目录 globalConfig.setOpen(true); //设置生成完毕是否打开生成代码所在目录 globalConfig.setAuthor("ylm");//设置作者名 globalConfig.setFileOverride(true);//设置是否覆盖原始生成的文件 globalConfig.setMapperName("%sDao");//设置数据层接口名,%s为占位符,指模块名称 globalConfig.setIdType(IdType.ASSIGN_ID); autoGenerator.setGlobalConfig(globalConfig); //设置包名相关配置 PackageConfig packageConfig =new PackageConfig(); packageConfig.setParent("com.aaa");//设置父类包名 packageConfig.setEntity("domain");//设置实体类包名 packageConfig.setMapper("dao");//设置数据层包名 autoGenerator.setPackageInfo(packageConfig); //策略设置 StrategyConfig strategyConfig = new StrategyConfig(); strategyConfig.setInclude("tb_book");//设置哪些表生成 strategyConfig.setTablePrefix("tb_");//告诉它前缀是什么,生成实体类就不带前缀了 strategyConfig.setRestControllerStyle(true);//是否启用REST风格 strategyConfig.setVersionFieldName("version");//设置乐观锁字段 strategyConfig.setLogicDeleteFieldName("deleted");//设置逻辑删除字段 strategyConfig.setEntityLombokModel(true);//设置是否启用lombok autoGenerator.setStrategy(strategyConfig); //执行代码生成操作 autoGenerator.execute(); } }
补充,MP还提供了IService<Book>来简化业务层的开发,同数据层类似,但是一半不用,因为业务层需要添加别的代码。有应用可以用。
结语:
SSM框架学习大致就到此结束了,复习复习就开始下一阶段咯。冲!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。