学习MyBatis-Plus
目录
一、MyBatis-Plus概述
1、简介
2、特性
3、框架结构
4、快速指南
二、MybBatis-Plus的增删改查
1、项目搭建
(1)创建表及数据导入
(2)、导入相应的依赖
(3)连接数据库
(4)编写实体类 User.java
(5)编写Mapper类 UserMapper.java
(6)测试一下
2、CRUD
(1)insert
(2)selectById
(3)selectByMap
(4)selectBatchlds
(5)updateById
(6)deleteById
(7)deleteByMap
(8)deleteBatchIds
三、MyBatis-Plus注解
1、@TableId
2、@TableName
3、@TableFieId
FieldStrategy
FieldFill
4、@Version(乐观锁)
5、@EnumValue
6、@TableLogic
7、@KeySequence
8、OrderBy
四、 MyBatis-Plus条件构造器
1、构造器简介
五、MyBatis-Plus代码生成器
1、代码生成器简介
2、 添加依赖
3、添加配置
4、代码生成器编写
5、代码生成器方法
(1)save
(2)saveOrUpdate
(3)remove
(4)update
(6)list
(7)page
(8)count
(9)chain
六、MyBatis-Plus插件
1、@Version(乐观锁)
乐观锁实现方式:
2、分页插件
3、性能分析插件
七、MyBatis-Plus的扩展功能
1、逻辑删除
2、通用枚举
使用注解方式
使用实现接口模式
3、字段类型处理器
4、自动填充功能(fill):
5、Sql注入器
6、自定义ID生成器
7、Sequence主键
一、MyBatis-Plus概述
1、简介
mybatis-plus(简称MP)是一个MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
我们的愿景是成为 MyBatis 最好的搭档,就像魂斗罗中的1P、2P,基友搭配,效率翻倍。
2、特性
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本 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 操作智能分析阻断,也可自定义拦截规则,预防误操作
3、框架结构
4、快速指南
我们将通过一个简单的 Demo 来阐述 MyBatis-Plus 的强大功能,在此之前,我们假设您已经:
- 拥有 Java 开发环境以及相应 IDE
- 熟悉 Spring Boot
- 熟悉 Maven
全新的 MyBatis-Plus3.0 版本基于 JDK8,提供了 lambda
形式的调用,所以必须是JDK 8+,这里用spring boot集成mybatis-plus
- maven
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.0</version> </dependency>
注意:引入MyBatis-Plus之后不要再次引用MyBatis和MyBatis-Spring,以避免因版本差异导致的问题
二、MybBatis-Plus的增删改查
1、项目搭建
(1)创建表及数据导入
--创建表
DROP TABLE IF EXISTS user;CREATE TABLE user
(id BIGINT(20) NOT NULL COMMENT '主键ID',name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',age INT(11) NULL DEFAULT NULL COMMENT '年龄',email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',PRIMARY KEY (id)
);--导入相应数据
DELETE FROM user;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)、导入相应的依赖
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency>
<!--mybatis-plus 是自己开发的,非官方的!--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.0</version></dependency>
<!--lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.10</version><scope>provided</scope></dependency>
(3)连接数据库
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.jdbc.Driver# 如果是mysql 8需要增加时区的配置:com.mysql.cj.jdbc.Driver#配置日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdouImpl
(4)编写实体类 User.java
@Data
public class User {private Long id;private String name;private Integer age;private String email;
}
(5)编写Mapper类 UserMapper.java
//在对应的Mapper上继承基本的类baseMapper
public interface UserMapper extends BaseMapper<User> {}
在Spring Boot启动类中添加@MapperScan注解,扫描Mapper文件夹:
@SpringBootApplication
@MapperScan("com.jacko.mapper")//扫描mapper文件夹
public class Application {public static void main(String[] args) {SpringApplication.run(QuickStartApplication.class, args);}}
(6)测试一下
@SpringBootTest
public class MPApplicationTests {@Autowiredprivate UserMapper userMapper;@Testpublic void testSelect() {//参数是一个Wrapper,条件结构器,这里先不用 填null//查询所有的用户 List<User> userList = userMapper.selectList(null);userList .forEach(System.out::println);}}
UserMapper 中的
selectList()
方法的参数为 MP 内置的条件封装器Wrapper
,所以不填写就是无任何条件
2、CRUD
(1)insert
需求描述:插入一个用户,姓名为“zhangsan”、年龄为18岁、邮箱为"zhangsan@qq.com"
@Test
void testInsert() {int result = userMapper.insert(new User(null,"zhangsan",18,"zhangsan@qq.com"));System.out.println("result:" + result);
}
(2)selectById
需求描述:查询id为1的用户信息
@Test
void testSelectById() {Useruser= userMapper.selectById(1);System.out.println(user);
}
(3)selectByMap
需求描述:年龄在18岁的用户信息
@Test
void testSelectByMap() {Map<String, Object> map = new HashMap<>();map.put("age",18);List<User> users= userMapper.selectByMap(map);users.forEach(System.out::println);
}
(4)selectBatchlds
需求描述:查询id分别为1、2、3的用户信息
@Test
void testSelectBatchIds() {List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));users.forEach(System.out::println);
}
(5)updateById
需求描述:将id为1的用户姓名改为“jacko”
@Test
void testUpdateById() {// 先查询User user = UserMapper.selectById(1);user .setLastName("jacko");// 再修改int result = UserMapper.updateById(user);System.out.println(result);
}
(6)deleteById
需求描述:删除id为1的用户信息
@Test
void testDeleteById() {int result = userMapper.deleteById(1);System.out.println(result);
}
(7)deleteByMap
需求描述:删除年龄为18岁且姓名为jacko的用户信息
@Test
void testDeleteByMap() {Map<String, Object> map = new HashMap<>();map.put("name", "jakoc");map.put("age", 18);int result = userMapper.deleteByMap(map);System.out.println(result);
}
(8)deleteBatchIds
需求描述:删除id分别为4、5、6的员工的信息
@Test
void testDeleteBatchIds() {int result = userMapper.deleteBatchIds(Arrays.asList(4, 5));System.out.println(result);
}
三、MyBatis-Plus注解
1、@TableId
序号 | 属性 | 类型 | 默认值 | 描述 |
---|---|---|---|---|
01 | value | String | "" | 主键字段名 |
02 | type | Enum | IdType.NONE | 主键类型 |
值 | 描述 |
---|---|
AUTO | 数据库ID自增 |
NONE | 无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT) |
INPUT | insert前自行set主键值 |
ASSIGN_ID |
分配ID(主键类型为Number(Long和Integer)或String)(since 3.3.0),使用接口IdentifierGenerator 的方法nextId (默认实现类为DefaultIdentifierGenerator 雪花算法)
|
ASSIGN_UUID |
分配UUID,主键类型为String(since 3.3.0),使用接口IdentifierGenerator 的方法nextUUID (默认default方法)
|
ID_WORKER |
分布式全局唯一ID 长整型类型(please use ASSIGN_ID )
|
UUID |
32位UUID字符串(please use ASSIGN_UUID )
|
ID_WORKER_STR |
分布式全局唯一ID 字符串类型(please use ASSIGN_ID )
|
2、@TableName
序号 | 属性 | 类型 | 默认值 | 描述 |
---|---|---|---|---|
01 | value | String | "" | 表名 |
02 | schema | String | "" | schema |
03 | keepGlobalPrefix | boolean | false | 是否保持使用全局的 tablePrefix 的值(如果设置了全局 tablePrefix 且自行设置了 value 的值) |
04 | resultMap | String | "" | xml中resultMap的id |
05 | autoResultMap | boolean | false | 是否自动构建resultMap并使用(如果设置resultMap则不会进行resultMap的自动构建并注入) |
06 | excludeProperty | String[] | {} | 需要排除的属性名(@since 3.3.1) |
3、@TableFieId
序号 | 属性 | 类型 | 默认值 | 描述 |
---|---|---|---|---|
01 | value | String | "" | 数据库的字段名 |
02 | el | String | "" | 映射为原生#{...}逻辑,相当于写在xml里的#{...}部分 |
03 | exist | boolean | true | 是否为数据库表的字段 |
04 | condition | String | "" |
字段 where 实体查询比较条件,有值设置则按设置的值为准,没有则为默认全局的 %s=#{%s} ,
|
05 | update | String | "" |
字段 update set 部分注入, 例如:update="%s+1":表示更新时会set version=version+1(该属性优先级高于 el 属性)
|
06 | insertStrategy | Enum | DEFAULT |
举例:NOT_NULL: insert into table_a(<if test="columnProperty != null">column</if>) values (<if test="columnProperty != null">#{columnProperty}</if>)
|
07 | updateStrategy | Enum | DEFAULT |
举例:IGNORED: update table_a set column=#{columnProperty}
|
08 | whereStrategy | Enum | DEFAULT |
举例:NOT_EMPTY: where <if test="columnProperty != null and columnProperty!=''">column=#{columnProperty}</if>
|
09 | fill | Enum | FieldFill.DEFAULT | 字段自动填充策略 |
10 | select | boolean | true | 是否进行 select 查询 |
11 | keepGlobalFormat | boolean | false | 是否保持使用全局的 format 进行处理 |
12 | jdbcType | JdbcType | JdbcType.UNDEFINED | JDBC类型 (该默认值不代表会按照该值生效) |
13 | typeHandler | Class<? extends TypeHandler> | UnknownTypeHandler.class | 类型处理器 (该默认值不代表会按照该值生效) |
14 | numericScale | String | "" | 指定小数点后保留的位数 |
FieldStrategy
序号 | 值 | 描述 |
---|---|---|
01 | IGNORED | 忽略判断 |
02 | NOT_NULL | 非NULL判断 |
03 | NOT_EMPTY | 非空判断(只对字符串类型字段,其他类型字段依然为非NULL判断) |
04 | DEFAULT | 追随全局配置 |
FieldFill
具体使用MyBatis-Puls的扩展功能
序号 | 值 | 描述 |
---|---|---|
01 | DEFAULT | 默认不处理 |
02 | INSERT | 插入时填充字段 |
03 | UPDATE | 更新时填充字段 |
04 | INSERT_UPDATE | 插入和更新时填充字段 |
4、@Version(乐观锁)
描述:乐观锁注解、标记 @Verison
在字段上,具体使用MyBatis-Puls的扩展功能
5、@EnumValue
描述:通枚举类注解(注解在枚举字段上),具体使用MyBatis-Puls的扩展功能
6、@TableLogic
描述:表字段逻辑处理注解(逻辑删除),具体使用MyBatis-Puls的扩展功能
序号 | 属性 | 类型 | 默认值 | 描述 |
---|---|---|---|---|
01 | value | String | "" | 逻辑未删除值 |
02 | delval | String | "" | 逻辑删除值 |
7、@KeySequence
- 描述:序列主键策略
oracle
,具体使用MyBatis-Puls的扩展功能 - 属性:value、resultMap
序号 属性 类型 默认值 描述 01 value String "" 序列名 02 clazz Class Long.class id的类型, 可以指定String.class,这样返回的Sequence值是字符串"1
8、OrderBy
- 描述:内置 SQL 默认指定排序,优先级低于 wrapper 条件查询
序号 | 属性 | 类型 | 默认值 | 描述 |
---|---|---|---|---|
01 | isDesc | boolean | 是 | 是否倒序查询 |
02 | sort | short | Short.MAX_VALUE | 数字越小越靠前 |
四、 MyBatis-Plus条件构造器
1、构造器简介
MyBatis-Plus 通过 EntityWrapper(简称 EW,MP 封装的一个查询条件构造器)或者 Condition(与 EW 类似) 来让用户自由的构建查询条件,简单便捷,没有额外的负担, 能够有效提高开发效率,它主要用于处理 sql 拼接,排序,实体参数查询等。
注意:使用的是数据库字段,不是 Java 属性!
@Test
void testSelectList1() {QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.allEq({name:"老王",age:24}) //name = '老王' and age= 24,.eq("age",24) //等于,age=24.ne("age",24) //不等于,age<>24.gt("age",24); //大于,age>24.ge("age",24); //大于等于,age>=24.lt("age",24); //小于,age<24.le("age",24); //小于等于,age<=24的.between("age", 18, 30) //age between 18 and 30.notBetween("age", 18, 30) //age not between 18 and 30.Like("name", "王") //模糊查询,name like '%王%.notLike("name", "王") //模糊查询,name not like '%王%.likeLeft("name", "王") //模糊查询,name like '%王'.likeRight("name", "王") //模糊查询,name like '王%'.isNull("name") //name is null.isNotNull("name") //name is not null.in("age",{1,2,3}) //age in (1,2,3).notIn("age", 1, 2, 3) //age not in (1,2,3).inSql("id", "select id from table where id < 3")//id in (select id from table where id < 3).notInSql("id", "select id from table where id < 3")//id not in (select id from table where id < 3).groupBy("id", "name") //分组,group by id,name.orderByAsc("id", "name") //排序,order by id ASC,name ASC.orderByDesc("id", "name") //排序,order by id DESC,name DESC.having("sum(age) > 10") //having sum(age) > 10.or(i -> i.eq("name", "jacko").ne("status", "活着"))//or (name = '李白' and status <> '活着').and(i -> i.eq("name", "jacko").ne("status", "活着"))//and (name = '李白' and status <> '活着').select("id", "name", "age").set("name", "老李头")List<User> users= userMapper.selectList(queryWrapper);users.forEach(System.out::println);
}
五、MyBatis-Plus代码生成器
1、代码生成器简介
AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。
2、 添加依赖
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.0</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.49</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.4.0</version></dependency><dependency><groupId>org.apache.velocity</groupId><artifactId>velocity-engine-core</artifactId><version>2.2</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.12</version><scope>provided</scope></dependency>
</dependencies>
<build><!-- 插件管理 --><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-resources-plugin</artifactId><version>2.5</version></plugin></plugins><!-- 资源管理 --><resources><resource><directory>src/main/java</directory><includes><include>**/*.properties</include><include>**/*.xml</include><include>**/*.conf</include></includes><filtering>false</filtering></resource><resource><directory>src/main/resources</directory><includes><include>**/*.properties</include><include>**/*.xml</include><include>**/*.conf</include></includes><filtering>false</filtering></resource></resources>
</build>
3、添加配置
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.jdbc.Driver# 如果是mysql 8需要增加时区的配置:com.mysql.cj.jdbc.Driver#配置日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdouImpl#mybatis-plus
mybatis-plus.mapper-locations=classpath*:**/mapper/xml/*.xml
4、代码生成器编写
public class CodeGenerator {public static String scanner(String tip) {Scanner scanner = new Scanner(System.in);StringBuilder help = new StringBuilder();help.append("请输入" + tip + ":");System.out.println(help.toString());if (scanner.hasNext()) {String ipt = scanner.next();if (StringUtils.isNotBlank(ipt)) {return ipt;}}throw new MybatisPlusException("请输入正确的" + tip + "!");}public static void main(String[] args) {// 代码生成器AutoGenerator mpg = new AutoGenerator();// 全局配置GlobalConfig gc = new GlobalConfig();String projectPath = System.getProperty("user.dir");gc.setOutputDir(projectPath + "/src/main/java");//设置代码生成路径gc.setFileOverride(true);//是否覆盖以前文件gc.setOpen(false);//是否打开生成目录gc.setAuthor("jacko");//设置项目作者名称gc.setIdType(IdType.AUTO);//设置主键策略gc.setBaseResultMap(true);//生成基本ResultMapgc.setBaseColumnList(true);//生成基本ColumnListgc.setServiceName("%sService");//去掉服务默认前缀mpg.setGlobalConfig(gc);// 数据源配置DataSourceConfig dsc = new DataSourceConfig();dsc.setUrl("jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf8");dsc.setDriverName("com.mysql.jdbc.Driver");dsc.setUsername("root");dsc.setPassword("123456");mpg.setDataSource(dsc);// 包配置PackageConfig pc = new PackageConfig();pc.setParent("com.jacko");pc.setMapper("mapper");pc.setXml("mapper.xml");pc.setEntity("entity");pc.setService("service");pc.setServiceImpl("service.impl");pc.setController("controller");mpg.setPackageInfo(pc);// 策略配置StrategyConfig sc = new StrategyConfig();sc.setNaming(NamingStrategy.underline_to_camel);sc.setColumnNaming(NamingStrategy.underline_to_camel);sc.setEntityLombokModel(true);//自动配置lomboksc.setRestControllerStyle(true);sc.setControllerMappingHyphenStyle(true);sc.setTablePrefix("tbl_");sc.setInclude(scanner("表名,多个英文逗号分割").split(","));//设置要映射的表名mpg.setStrategy(sc);// 生成代码mpg.execute();}}
5、代码生成器方法
通用 Service CRUD 封装 IService 接口,进一步封装 CRUD 采用 get 查询单行 remove 删除 list 查询集合 page 分页 前缀命名方式区分 Mapper 层避免混淆,泛型 T 为任意实体对象,建议如果存在自定义通用 Service 方法的可能,请创建自己的 IBaseService 继承 Mybatis-Plus 提供的基类,对象 Wrapper 为 条件构造器。
(1)save
// 插入一条记录(选择字段,策略插入)
boolean save(T entity);
// 插入(批量)
boolean saveBatch(Collection<T> entityList);
// 插入(批量)
boolean saveBatch(Collection<T> entityList, int batchSize);
参考说明:
类型 | 参数名 | 描述 |
---|---|---|
T | entity | 实体对象 |
Collection<T> | entityList | 实体对象集合 |
int | batchSize | 插入批次数量 |
(2)saveOrUpdate
// TableId 注解存在更新记录,否插入一条记录
boolean saveOrUpdate(T entity);
// 根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法
boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper);
// 批量修改插入
boolean saveOrUpdateBatch(Collection<T> entityList);
// 批量修改插入
boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);
参考说明:
类型 | 参数名 | 描述 |
---|---|---|
T | entity | 实体对象 |
Wrapper<T> | updateWrapper | 实体对象封装操作类 UpdateWrapper |
Collection<T> | entityList | 实体对象集合 |
int | batchSize | 插入批次数量 |
(3)remove
// 根据 entity 条件,删除记录
boolean remove(Wrapper<T> queryWrapper);
// 根据 ID 删除
boolean removeById(Serializable id);
// 根据 columnMap 条件,删除记录
boolean removeByMap(Map<String, Object> columnMap);
// 删除(根据ID 批量删除)
boolean removeByIds(Collection<? extends Serializable> idList);
参考说明:
类型 | 参数名 | 描述 |
---|---|---|
Wrapper<T> | queryWrapper | 实体包装类 QueryWrapper |
Serializable | id | 主键ID |
Map<String, Object> | columnMap | 表字段 map 对象 |
Collection<? extends Serializable> | idList | 主键ID列表 |
(4)update
// 根据 UpdateWrapper 条件,更新记录 需要设置sqlset
boolean update(Wrapper<T> updateWrapper);
// 根据 whereEntity 条件,更新记录
boolean update(T entity, Wrapper<T> updateWrapper);
// 根据 ID 选择修改
boolean updateById(T entity);
// 根据ID 批量更新
boolean updateBatchById(Collection<T> entityList);
// 根据ID 批量更新
boolean updateBatchById(Collection<T> entityList, int batchSize);
参考说明:
类型 | 参数名 | 描述 |
---|---|---|
Wrapper<T> | updateWrapper | 实体对象封装操作类 UpdateWrapper |
T | entity | 实体对象 |
Collection<T> | entityList | 实体对象集合 |
int | batchSize | 更新批次数量 |
(5)get
// 根据 ID 查询
T getById(Serializable id);
// 根据 Wrapper,查询一条记录。结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last("LIMIT 1")
T getOne(Wrapper<T> queryWrapper);
// 根据 Wrapper,查询一条记录
T getOne(Wrapper<T> queryWrapper, boolean throwEx);
// 根据 Wrapper,查询一条记录
Map<String, Object> getMap(Wrapper<T> queryWrapper);
// 根据 Wrapper,查询一条记录
<V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);
参考说明:
(6)list
// 查询所有
List<T> list();
// 查询列表
List<T> list(Wrapper<T> queryWrapper);
// 查询(根据ID 批量查询)
Collection<T> listByIds(Collection<? extends Serializable> idList);
// 查询(根据 columnMap 条件)
Collection<T> listByMap(Map<String, Object> columnMap);
// 查询所有列表
List<Map<String, Object>> listMaps();
// 查询列表
List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper);
// 查询全部记录
List<Object> listObjs();
// 查询全部记录
<V> List<V> listObjs(Function<? super Object, V> mapper);
// 根据 Wrapper 条件,查询全部记录
List<Object> listObjs(Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录
<V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);
参考说明:
类型 | 参数名 | 描述 |
---|---|---|
Wrapper<T> | queryWrapper | 实体对象封装操作类 QueryWrapper |
Collection<? extends Serializable> | idList | 主键ID列表 |
Map<?String, Object> | columnMap | 表字段 map 对象 |
Function<? super Object, V> | mapper | 转换函数 |
(7)page
// 无条件分页查询
IPage<T> page(IPage<T> page);
// 条件分页查询
IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper);
// 无条件分页查询
IPage<Map<String, Object>> pageMaps(IPage<T> page);
// 条件分页查询
IPage<Map<String, Object>> pageMaps(IPage<T> page, Wrapper<T> queryWrapper);
参数说明:
类型 | 参数名 | 描述 |
---|---|---|
IPage<T> | page | 翻页对象 |
Wrapper<T> | queryWrapper | 实体对象封装操作类 QueryWrapper |
(8)count
// 查询总记录数
int count();
// 根据 Wrapper 条件,查询总记录数
int count(Wrapper<T> queryWrapper);
参数说明:
类型 | 参数名 | 描述 |
---|---|---|
Wrapper<T> | queryWrapper | 实体对象封装操作类 QueryWrapper |
(9)chain
query
// 链式查询 普通
QueryChainWrapper<T> query();
// 链式查询 lambda 式。注意:不支持 Kotlin
LambdaQueryChainWrapper<T> lambdaQuery(); // 示例:
query().eq("column", value).one();
lambdaQuery().eq(Entity::getId, value).list();
update
// 链式更改 普通
UpdateChainWrapper<T> update();
// 链式更改 lambda 式。注意:不支持 Kotlin
LambdaUpdateChainWrapper<T> lambdaUpdate();// 示例:
update().eq("column", value).remove();
lambdaUpdate().eq(Entity::getId, value).update(entity);
六、MyBatis-Plus插件
创建包:com.jacko.config
新建类:com.jacko.config.MybatisPlusConfig
1、@Version(乐观锁)
- 乐观锁:顾名思义十分乐观,它总是被认为不会出现问题,无论干什么都不去上锁!如果出现了问题,再次更新测试
- 悲观锁:顾名思义十分悲观,它总是出现问题,无论干什么都会上锁!再去操作!
乐观锁实现方式:
- 取出记录时,获取当前version
- 更新时,带上这个version
- 执行更新时,set version=newVersion where version =oldVersion
- 如果version不对,就更新失败
(1)表中添加乐观锁字段version 默认值为1
(2)同步实体类
@Version
private Integer version;
(3)注册组件
@EnableTransactionManagement
@Configuration//配置类
public class MyBatisPlusConfig{//注册乐观锁插件@Beanpublic OptimisticLockerInterceptor optimisticLockerInterceptor() {return new OptimisticLockerInterceptor();}
}
注意:
- 支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
- 整数类型下
newVersion = oldVersion + 1
- 仅支持
updateById(id)
与update(entity, wrapper)
方法 - 在
update(entity, wrapper)
方法下,wrapper
不能复用!!!
测试一下:
@Test
public void testOptimisticLocker(){//假设线程1User user = userMapper.selectById(1L);user.setName("zhangsan111");user.setEmail("123456789@qq.com");//假设线程2,执行插队操作User user2 = userMapper.selectById(1L);user2.setName("zhangsan222");user2.setEmail("123456789@qq.com");userMapper.updateById(user2);//如果没有乐观锁就会覆盖插队线程2的值userMapper.updateById(user2);}
2、分页插件
添加插件:
@EnableTransactionManagement
@Configuration
public class MybatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();// 添加分页插件interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));return interceptor;}}
属性设置:
属性名 | 类型 | 默认值 | 描述 |
---|---|---|---|
overflow | boolean | false |
溢出总页数后是否进行处理(默认不处理,参见 插件#continuePage 方法)
|
maxLimit | Long |
单页分页条数限制(默认无限制,参见 插件#handlerLimit 方法)
|
|
dbType | DbType |
数据库类型(根据类型获取应使用的分页方言,参见 插件#findIDialect 方法)
|
|
dialect | IDialect |
方言实现类(参见 插件#findIDialect 方法)
|
测试一下:
@Test
void testPagination() {Page<Employee> page = new Page<>(1,5);Page<User> usersPage = userMapper.selectPage(page, null);usersPage.getRecords().forEach(System.out::println);System.out.println("当前页:" + usersPage.getCurrent());System.out.println("总页数:" + usersPage.getPages());System.out.println("记录数:" + usersPage.getTotal());System.out.println("是否有上一页:" + usersPage.hasPrevious());System.out.println("是否有下一页:" + usersPage.hasNext());
}
3、性能分析插件
作用:性能分析拦截器,用于输出每条SQL语句及执行时间,MP也提供性能分析插件,如果超过这和时间就会停止运行
添加插件:
@EnableTransactionManagement
@Configuration
public class MybatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {...}/*** SQL执行效率插件*/@Bean@Profile({"dev","test"})// 设置 dev test 环境开启public PerformanceInterceptor performanceInterceptor() {PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();performanceInterceptor.setMaxTime(100);// ms 设置sql执行的最大时间,如果超过就停止performanceInterceptor.setFormat(true);return new PerformanceInterceptor();}
}
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.jdbc.Driver# 如果是mysql 8需要增加时区的配置:com.mysql.cj.jdbc.Driver#配置日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdouImpl#mybatis-plus
mybatis-plus.mapper-locations=classpath*:**/mapper/xml/*.xmlspring.profiles.active=dev
测试一下:
@Test
void testPagination() {Page<Employee> page = new Page<>(1,5);Page<User> usersPage = userMapper.selectPage(page, null);usersPage.getRecords().forEach(System.out::println);System.out.println("当前页:" + usersPage.getCurrent());System.out.println("总页数:" + usersPage.getPages());System.out.println("记录数:" + usersPage.getTotal());System.out.println("是否有上一页:" + usersPage.hasPrevious());System.out.println("是否有下一页:" + usersPage.hasNext());
}
七、MyBatis-Plus的扩展功能
1、逻辑删除
- 物理删除:从数据库中直接移除
- 逻辑删除:在数据库中没有被移除,而是通过一个变量来让他失效!delete=0 => delete=1
管理员可以查看被删除的记录!防止数据的丢失,类似于回收站!
(1)在数据表中添加deleted字段
(2)实体类中添加deleted属性
//逻辑删除字段
private Integer deleted;
(3)配置一下
# 配置逻辑删除
mybatis-plus.global-config.db-config.logic-delete-field=deleted # 全局逻辑删除的实体字段名
mybatis-plus.global-config.db-config.logic-delete-value=1 # 逻辑已删除值(默认为 1)
mybatis-plus.global-config.db-config.logic-not-delete-value=0 # 逻辑未删除值(默认为 0)
(4)测试一下
@Test
void testPagination() {userMapper.deleteById(2);
}
2、通用枚举
通用枚举解决了繁琐的配置,让 mybatis 优雅的使用枚举属性!实现自定义枚举有两种方式,一种是使用注解而另一种是使用实现接口IEnum的方式
使用注解方式
(1)添加配置
mybatis-plus.type-enums-package=com.jacko.myenum
mybatis-plus.configuration.default-enum-type-handler=org.apache.ibatis.type.EnumOrdinalTypeHandler
(2)创建枚举类com.jacko.myenum.GradeEnum
public enum GradeEnum {PRIMARY(1, "小学"),SECONDORY(2, "中学"),HIGH(3, "高中");private int code;@EnumValue//描述作为枚举值保存到数据库private String desc;GradeEnum(int code, String desc) {this.code = code;this.desc = desc;}public int getCode() {return code;}public void setCode(int code) {this.code = code;}public String getDesc() {return desc;}public void setDesc(String desc) {this.desc = desc;}
}
(3)修改实体类com.jacko.entity.Student
/*** 学生年级*/
private GradeEnum grade;
(4)测试一下
@Test
void testGradeEnum() {Student student = new Student();student.setName("王五");student.setGrade(GradeEnum.HIGH);int result = studentMapper.insert(student);System.out.println(result);
}
使用实现接口模式
(1)添加配置
mybatis-plus.type-enums-package=com.jacko.myenum
mybatis-plus.configuration.default-enum-type-handler=org.apache.ibatis.type.EnumOrdinalTypeHandler
(2)创建枚举类com.jacko.myenum.AgeEnum
public enum AgeEnum implements IEnum<Integer> {ONE(1, "一岁"),TWO(2, "二岁"),THREE(3, "三岁");private int value;private String desc;AgeEnum(int value, String desc) {this.value = value;this.desc = desc;}@Overridepublic Integer getValue() {//数值作为枚举值保存到数据库return this.value;}
}
(3)修改实体类com.jacko.entity.Student
/*** 学生年龄*/
private AgeEnum age;
(4)测试一下
@Test
void testAgeEnum() {Student student = new Student();student.setName("李四");student.setAge(AgeEnum.THREE);int result = studentMapper.insert(student);System.out.println(result);
}
3、字段类型处理器
字段类型处理器,用于 JavaType 与 JdbcType 之间的转换,用于 PreparedStatement 设置参数值和从 ResultSet 或 CallableStatement 中取出一个值,本文讲解 mybaits-plus
内置常用类型处理器如何通过TableField
注解快速注入到 mybatis
容器中。
@Data
@Accessors(chain = true)
@TableName(autoResultMap = true)
public class User {private Long id;.../*** 注意!! 必须开启映射注解** @TableName(autoResultMap = true)** 以下两种类型处理器,二选一,也可以同时存在** 注意!! 选择对应的 JSON 处理器也必须存在对应 JSON 解析依赖包*/@TableField(typeHandler = JacksonTypeHandler.class)// @TableField(typeHandler = FastjsonTypeHandler.class)private OtherInfo otherInfo;}
4、自动填充功能(fill):
创建时间、修改时间!这些个操作一遍都是自动化完成,我们不希望手动更新!
阿里巴巴开发手册:所有的数据库表几乎所有的表都要配置上!而且需要自动化
方式一:数据库级别
在表中新增字段create_time、update_time(默认CURRENT_TIMESIAMP)
方式二:代码级别
①实体类上的属性增加注解@TableField
//创建时间
@TableField(fill = FieldFill.INSERT)
private Date createTime;//更新时间
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
②编写处理器处理这个注解
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {//插入时候的填充策略@Override@Componcent //一定不要忘记吧处理器加到IOC容器中public void insertFill(MetaObject metaObject) {log.info("start insert fill ...."); //日志//设置字段的值(String fieldName字段名,Object fieldVal要传递的值,MetaObject metaObject)this.setFieldVaLByName("createTime",new Date(),metaObject);this.setFieldVaLByName("updateTime",new Date(),metaObject);//this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐使用)// this.fillStrategy(metaObject, "createTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug请升级到之后的版本如`3.3.1.8-SNAPSHOT`)/* 上面选其一使用,下面的已过时(注意 strictInsertFill 有多个方法,详细查看源码) *///this.setFieldValByName("operator", "Jerry", metaObject);//this.setInsertFieldValByName("operator", "Jerry", metaObject);}//更新时间的填充策略@Overridepublic void updateFill(MetaObject metaObject) {log.info("start update fill ....");this.setFieldVaLByName("updateTime",new Date(),metaObject);//this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐使用)// this.fillStrategy(metaObject, "updateTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug请升级到之后的版本如`3.3.1.8-SNAPSHOT`)/* 上面选其一使用,下面的已过时(注意 strictUpdateFill 有多个方法,详细查看源码) *///this.setFieldValByName("operator", "Tom", metaObject);//this.setUpdateFieldValByName("operator", "Tom", metaObject);}
}
5、Sql注入器
根据 MybatisPlus 的 AutoSqlInjector 可以自定义各种你想要的 SQL 注入到全局中,相当于自定义 MybatisPlus 自动注入的方法。之前需要在 xml 中进行配置的 SQL 语句,现在通过扩展 AutoSqlInjector 在加载 mybatis 环境时就注入
(1)在mapper接口上添加@Repository注解
@Repository
public interface UserMapper extends BaseMapper<User> {public void deleteAll();}
注意:我们不需要编写xml映射,因为我们会采用sql注入的形式,在 MybatisPlus 启动的时候就注入。
(2)创建对象 com.jacko.injector.MyMappedStatement、MySqlInjector
public class MyMappedStatement extends AbstractMethod {@Overridepublic MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {// 接口中的方法名String method = "deleteAll";// 该方法执行语句String sql = "delete from " + tableInfo.getTableName();// 创建SqlSourceSqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);// 构造一个删除的MappedStatement并返回return this.addDeleteMappedStatement(mapperClass, method, sqlSource);}
}
public class MySqlInjector extends DefaultSqlInjector {@Overridepublic List<AbstractMethod> getMethodList(Class<?> mapperClass) {List<AbstractMethod> methodList = super.getMethodList(mapperClass);methodList.add(new MyMappedStatement());return methodList;}
}
(3)在MybatisPlusConfig添加自定义sql注入对象
@Configuration
public class MybatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {...}@Beanpublic MySqlInjector mySqlInjector() {return new MySqlInjector();}}
6、自定义ID生成器
Tip:自3.3.0开始,默认使用雪花算法+UUID(不含中划线)
重写方法:
方法 | 主键生成策略 | 主键类型 | 说明 |
---|---|---|---|
nextId | ASSIGN_ID,ID_WORKER,ID_WORKER_STR | Long,Integer,String | 支持自动转换为String类型,但数值类型不支持自动转换,需精准匹配,例如返回Long,实体主键就不支持定义为Integer |
nextUUID | ASSIGN_UUID,UUID | String | 默认不含中划线的UUID生成 |
(1)创建类:com.jacko.incrementer.CustomIdGenerator
public class CustomIdGenerator implements IdentifierGenerator {private final AtomicLong al = new AtomicLong(1);@Overridepublic Long nextId(Object entity) {//可以将当前传入的class全类名来作为bizKey或者提取参数来生成bizKey进行分布式Id调用生成String bizKey = entity.getClass().getName();MetaObject metaObject = SystemMetaObject.forObject(entity);String name = (String) metaObject.getValue("pname");final long id = al.getAndAdd(1);System.out.println("为" + name + "生成主键值->:" + id);return id;}
}
(2)注册类:MybatisPlusConfig
@Bean
public IdentifierGenerator customIdGenerator(){return new CustomIdGenerator();
}
(3)修改主键策略
/*** 商品主键*/
@TableId(value = "pid", type = IdType.ASSIGN_ID)
private Long id;
(4)测试一下
@Autowired
private ProductMapper productMapper;@Test
void testCustomIdGenerator() {Product product = new Product();product.setPname("手机");int result = productMapper.insert(product);System.out.println(result);
}
7、Sequence主键
Tip:主键生成策略必须使用INPUT
支持父类定义@KeySequence子类继承使用
支持主键类型指定(3.3.0开始自动识别主键类型)
内置支持:
- DB2KeyGenerator
- H2KeyGenerator
- KingbaseKeyGenerator
- OracleKeyGenerator
- PostgreKeyGenerator
如果内置支持不满足你的需求,可实现IKeyGenerator接口来进行扩展.
在实际开发中,我们经常会使用到MySQL和Oracle数据库,但是这两种数据库对于主键有不同的策略,如下:
- MySQL:支持主键自增,type = IdType.Auto
- Oracle:支持序列自增,type = IdType.INPUT
实现:
(1)在实体类对象上添加注解@KeySequence(value=”序列名”, clazz=主键属性类型.class)
(2)在实体类对象的主键字段上添加注解@TableId(value = “主键名称”, type = IdType.INPUT)
@KeySequence(value = "SEQ_ORACLE_INTEGER_KEY", clazz = Integer.class)
public class YourEntity {@TableId(value = "ID", type = IdType.INPUT)private Integer id;}
(3)在全局配置中注册com.baomidou.mybatisplus.incrementer.OracleKeyGenerator
@Bean
public IKeyGenerator keyGenerator() {return new oracleKeyGenerator();
}
学习MyBatis-Plus相关推荐
- 跟小博老师一起学习MyBatis ——MyBatis搭建运行环境
数据库 上回小傅老师与大伙聊了MyBatis是一款非常优秀的操作数据库的框架产品,所以大家先要安装好数据库,以便MyBatis能操作它.小傅老师用的是MYSQL数据库产品,当然你也可以使用MSSQL. ...
- 小白大学生学习MyBatis(二)
小白大学生学习MyBatis(二) 1 . 接(一)解释测试代码 主要类的介绍 https://blog.csdn.net/hjs_75187712/article/details/118344894 ...
- 学习Mybatis第三天
学习Mybatis第三天 1. 日志 1.1 日志工厂 如果一个数据库操作出现了异常,我们需要排错,日志就是最好的助手了! 曾今:sout,debug 现在:日志工厂 SLF4J LOG4J [掌握] ...
- 快速学习MyBatis|实战项目详解
作者主页:橙子! 主页 系列专栏:JavaWeb基础教程系列 精彩回顾:HTTP协议详解 文章目的:快速学习MyBatis及实战项目详解 文章目录 1.什么是MyBatis? 2. JDBC存在的缺点 ...
- MyBatis构架设计学习------MyBatis的整体架构
一.基础框架图 二.各模块的基本作用 --------------------基础支持层-------------------- 1.反射模块 MyBatis的反射模块中专门对原生的java反射进行了 ...
- 学习Mybatis与mysql数据库的示例笔记
目录结构: pom.xml文件 1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xml ...
- 深入学习Mybatis框架(二)- 进阶
1.动态SQL 1.1 什么是动态SQL? 动态SQL就是通过传入的参数不一样,可以组成不同结构的SQL语句. 这种可以根据参数的条件而改变SQL结构的SQL语句,我们称为动态SQL语句.使用动态SQ ...
- [转载] 快速学习-Mybatis框架概述
参考链接: Java在竞争性编程中的快速I/O 第1章 框架概述 1.1 什么是框架 1.1.1 什么是框架 框架(Framework)是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互 ...
- mybatis mysql schema_学习Mybatis与mysql数据库的示例笔记
目录结构: pom.xml文件 1 <?xml version="1.0" encoding="UTF-8"?> 2 3 xmlns:xsi=&qu ...
- 黑马程序员视频教程学习mybatis框架常用注释SQL语句学习笔记?
mybatis学习笔记 常用注释增删改查SQL语句 常用注释拓展SQL语句 解决实体类属性和数据库表中的属性名称不相同的问题: mybatis注解之一对一查询: mybatis注解之一对多查询: my ...
最新文章
- ​Highmaps网页图表教程之绘图区显示标签显示数据标签定位
- 45号:公钥,私钥和数字签名
- 转载CopyOnWriteArrayList
- heartbeat+drbd+mysql:实现最廉价的高可用组合
- 6.Vue Class 与 Style 绑定
- gradle 配置java 项目maven 依赖
- python怎么用split字符串全部分开_请教一下python怎么用split对一句话拆分两次?...
- Matlab命令汇总
- 《一张图看懂华为云BigData Pro鲲鹏大数据解决方案》
- 一个软件系统哪些可独立实现
- Cocoa中Core Data的简单介绍
- php是什么电器元件,第三代计算机采用的主要电子元器件是什么
- 双向DC/DC变换器设计-硬件主拓扑
- FusionStorage原理及组件
- 苹果严打iMessage垃圾短信
- C语言-OFDM过采样获得中频IF信号
- 没人谈论的 3 种从 ChatGPT 赚钱的方法,我测试过的建立被动收入流的行之有效的方法
- ElasticSearch底层Lucene的执行流程与细节
- Hexo博客框架的搭建与使用
- 帝国CMS7.5微信扫码登录插件 帝国cms插件分享