1.MybatisPlus简介
MybatisPlus(简称MP)是Mybatis增强工具,增强不做改变,简化开发,官网 http://mp.baomidou.com/
2.特性
无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer2005、SQLServer 等多种数据库
支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
支持 XML 热加载:Mapper 对应的 XML 支持热加载,对于简单的 CRUD 操作,甚至可以无 XML 启动
支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD操作
支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
支持关键词自动转义:支持数据库关键词(order、key......)自动转义,还可自定义关键词
内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通List 查询
内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
内置 Sql 注入剥离器:支持 Sql 注入剥离,有效预防 Sql 注入攻击
无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer2005、SQLServer 等多种数据库支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题支持 XML 热加载:Mapper 对应的 XML 支持热加载,对于简单的 CRUD 操作,甚至可以无 XML 启动支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD操作支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )支持关键词自动转义:支持数据库关键词(order、key......)自动转义,还可自定义关键词内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通List 查询
3.快速入门
3.1 创建数据库
数据库名称mybatis_plus
3.2 创建表
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) ); 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');
3.3 创建springboot项目
导入maven依赖
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> <version>1.18.20</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.0.5</version> </dependency>
配置mysql
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=GMT%2B8&characterEncoding-utf-8 spring.datasource.username=root spring.security.user.password=root
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
创建实体类User
@Data public class User { private Long id; private String name; private Integer age; private String email; }
创建UserMapper继承BaseMapper
public interface UserMapper extends BaseMapper<User> { }
在启动类添加@MapperScan("com.gh")
3.4 基本crud
查询所有
@Test void testSelect() { List<User> list = userMapper.selectList(null); list.forEach(System.out::println); }
添加
@Test public void insert() { User user = new User(); user.setName("建国"); user.setAge(74); user.setEmail("jianguo@qq.com"); userMapper.insert(user); }
查询
@Test public void TestSelect() { User user = userMapper.selectById(1); System.out.println(user); List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 4)); users.forEach(System.out::println); }
修改
@Test public void testUpdate() { User user = new User(); user.setId(1L); user.setAge(74); int result = userMapper.updateById(user); System.out.println(result); }
删除
@Test public void delete(){ int result = userMapper.deleteById(1L); System.out.println(result); }
通用service
编写UserService接口,继承IService接口
public interface UserService extends IService<User> { }
编写UserServiceImpl实现类继承ServiceImpl类,实现UserService接口
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { }
查询总数
@Test public void count() { int count = userService.count(null); System.out.println(count); }
批量添加
@Test public void saveBatch() { List<User> list = new ArrayList<>(); for (int i = 0; i < 5; i++) { User user = new User(); user.setName("张三" + i); user.setAge(20 + i); list.add(user); } boolean flag = userService.saveBatch(list); System.out.println(flag); }
4.自定义mapper
当通用mapper无法满足我们的需求时,我们可以自定义基于mapper接口的xml文件,并在xml文件中配置SQL语句
4.1 定义接口方法
在UserMapper里定义方法
List<User> selectAllByName(String name);
4.2 创建xml文件
在resources目录下创建mapper/UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.gh.mpdemos.mapper.UserMapper"> <sql id="BaseColumn"> a.id AS id, a.name AS name, a.age AS age, a.email AS email </sql> <select id="selectAllByName" resultType="User"> select <include refid="BaseColumn"/> from user AS a where a.name = #{name} </select> </mapper>
在配置文件进行xml扫描
mybatis-plus.mapper-locations=classpath:mapper/*.xml
4.3 创建测试类
@Test public void testSelectByName(){ List<User> list = userMapper.selectAllByName("Jack"); list.forEach(System.out::println); }
5.常用注解
5.1 @TableName
value属性
实体类的名称是User,数据库的表名是t_user,此时我们需要在实体类上加上@TableName注解
@TableName(value = "t_user") public class User { }
5.2 @TableId
type属性
type属性可以指定id生成策略,这里的指定雪花算法生成
@TableId(type = IdType.ID_WORKER_STR) private Long id;
5.3 @TableField
value属性
可以通过value属性指定字段名
@TableField(value = "username") private String name;
5.4 自动填充
在数据库添加上create_time和update_time两个字段,并在实体类加上如下代码
@TableField(fill = FieldFill.INSERT) private LocalDateTime createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private LocalDateTime updateTime;
配置自动填充插件
在字段上面加上注解@TableField(fill=FillField.INSERT) 添加时,@TableField(fill=FillField.UPDATE)修改时候,时间跟着修改
必备条件:创建类MyMetaObjectHandler实现类MetaObjectHandler,重写insertFill和updateFill方法,具体写法如下
@Component public class MyMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { this.setFieldValByName("createTime", LocalDateTime.now(),metaObject); this.setFieldValByName("updateTime",LocalDateTime.now(),metaObject); } @Override public void updateFill(MetaObject metaObject) { this.setFieldValByName("updateTime",LocalDateTime.now(),metaObject); } }
5.5 @TableLogic
逻辑删除:假删除,将对应数据中代表是否被删除字段的状态修改为“被删除状态”,之后在数据库中仍然能够看到此条数据记录,使用场景:可以进行数据恢复。
首先在数据库创建字段is_deleted,类型为tinyint然后在实体类添加属性deleted
@TableLogic @TableField(value = "is_deleted") private Boolean deleted;
然后在配置类添加mybatisplus逻辑删除插件,注意:mp版本为3.3.*的省去这一步
0代表未删除,1代表已删除
@Configuration public class MpConfig { /** * 逻辑删除 * @return */ @Bean public ISqlInjector sqlInjector(){ return new LogicSqlInjector(); } }
6.分页插件
6.1 配置
在配置文件中进行如下配置
@Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); }
新建一个mybatis的数据源配置文件,代码如下
@Configuration public class DataSourceConfig { @Autowired private PaginationInterceptor paginationInterceptor; @Primary @Bean(name = "helmetSqlSessionFactory") public SqlSessionFactory helmetSqlSessionFactory( DataSource helmetDataSource) throws Exception { MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean(); sqlSessionFactory.setDataSource(helmetDataSource); // 关键代码 设置 MyBatis-Plus 分页插件 Interceptor[] plugins = {paginationInterceptor}; sqlSessionFactory.setPlugins(plugins); return sqlSessionFactory.getObject(); } }
6.2 分页插件使用
page里的第一个参数代表当前页,第二个参数代表每页显示的条数
@Test public void selectByPage(){ Page<User> page=new Page<>(1,2); userService.page(page, null); page.getRecords().forEach(System.out::println); long total = page.getTotal(); System.out.println(total); }
关于xml中的分页,我们只需在接口方法中传递page对象就可以了,mybatisplus会自动帮我们进行分页
IPage<User> selectPageVo(Page<?> page,Integer age);
对应的xml语句如下,不需要自己进行手动分页
<select id="selectPageVo" resultType="com.gh.mpdemos.entity.User"> select <include refid="BaseColumn"></include> from user where age > #{age} </select>
7.乐观锁
数据库中添加version字段:取出记录时,获取当前version,更新时,version + 1,如果where语句中的version版本不对,则更新失败
@Version private Integer version;
添加乐观锁插件
@Bean public OptimisticLockerInterceptor optimisticLockerInterceptor(){ return new OptimisticLockerInterceptor(); }
8.条件构造器
8.1 wrapper家族
在MP中我们可以使用通用Mapper(BaseMapper)实现基本查询,也可以使用自定义Mapper(自定义XML)来实现更高级的查询。当然你也可以结合条件构造器来方便的实现更多的高级查询。
8.2 QueryWrapper
查询组装条件
/** * 查询名字中包含n,年龄大于等于10且小于等于20,email不为空的用户 */ @Test public void test1(){ QueryWrapper<User> queryWrapper=new QueryWrapper<>(); queryWrapper.like("name","n"); queryWrapper.le("age",20); queryWrapper.ge("age",10); queryWrapper.isNotNull("email"); List<User> list = userService.list(queryWrapper); list.forEach(System.out::println); }
排序组装条件
/** * 按年龄降序查询用户,如果年龄相同则按id升序排列 */ @Test public void test2() { QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.orderByDesc("age") .orderByAsc("id"); List<User> list = userService.list(queryWrapper); list.forEach(System.out::println); }
删除组装条件
/** * 删除email为空的数据 */ @Test public void test3() { QueryWrapper<User> queryWrapper=new QueryWrapper<>(); queryWrapper.isNull("email"); boolean remove = userService.remove(queryWrapper); System.out.println(remove); }
条件的优先级
/** * 查询名字中包含n,且(年龄小于18或email为空的用户),并将这些用户的年龄设置为18,邮箱设置为 user@atguigu.com */ @Test public void test4() { QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.like("name", "n") .and(i -> i.lt("age", 18).or().isNull("email"));//lambda表达式的逻辑优先计算 User user = new User(); user.setAge(18); user.setEmail("user@atguigu.com"); boolean update = userService.update(user, queryWrapper); System.out.println(update); }
组装select子句
/** * 查询所有用户的用户名和年龄 */ @Test public void test5(){ QueryWrapper<User> queryWrapper=new QueryWrapper<>(); queryWrapper.select("name","age"); List<Map<String, Object>> list = userMapper.selectMaps(queryWrapper); list.forEach(System.out::println); }
实现子查询
/** * 查询id不大于3的所有用户的id列表 */ @Test public void test6() { QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.inSql("id", "select id from t_user where id<=3"); List<User> list = userMapper.selectList(queryWrapper); list.forEach(System.out::println); }
对应的sql语句是
SELECT id,name,age,email,create_time,update_time,is_deleted AS deleted FROM t_user WHERE is_deleted=0 AND (id IN (select id from t_user where id<=3))
8.3 UpdateWrapper
查询结果SQL语句为:UPDATE t_user SET age=?,email=? WHERE is_deleted=0 AND (name LIKE ? AND (age < ? OR email IS NULL)) ,不会更新时间,解决办法是在update方法中添加user对象。
/** * 查询名字中包含n,且(年龄小于18或email为空的用户),并将这些用户的年龄设置为18,邮箱设置为 user@atguigu.com */ @Test public void test7() { UpdateWrapper<User> updateWrapper = new UpdateWrapper<>(); updateWrapper .set("age",18) .set("email","user@atguigu.com") .like("name", "n") .and(i -> i.lt("age", 18).or().isNull("email"));//lambda表达式的逻辑优先计算 boolean update = userService.update(updateWrapper); System.out.println(update); }
condition
/** * 查询名字中包含n,年龄大于10且小于20的用户,查询条件来源于用户输入,是可选的 */ @Test public void test8() { String name = "names"; Integer ageBegin = 10; Integer ageEnd = 20; QueryWrapper<User> queryWrapper = new QueryWrapper<>(); if (StringUtils.isNotBlank(name)) { queryWrapper.like("name", name); } if (ageBegin != null) { queryWrapper.gt("age", ageBegin); } if (ageEnd != null) { queryWrapper.lt("age", ageEnd); } List<User> list = userService.list(queryWrapper); list.forEach(System.out::println); }
8.4 LambdaQueryWrapper
基本用法,防止字段写错而出现问题
/** * 查询名字中包含n,年龄大于10且小于20的用户,查询条件来源于用户输入,是可选的 */ @Test public void test9() { String name = "names"; Integer ageBegin = 10; Integer ageEnd = 20; LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.like(StringUtils.isNotBlank(name), User::getName, name); queryWrapper.gt(ageBegin != null, User::getAge, ageBegin); queryWrapper.lt(ageEnd != null, User::getAge, ageEnd); List<User> list = userService.list(queryWrapper); list.forEach(System.out::println); }