1、Mybatis-Plus
1.1 简介
MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
我们的愿景是成为 MyBatis 最好的搭档,就像 魂斗罗 中的 1P、2P,基友搭配,效率翻倍。
1.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 操作智能分析阻断,也可自定义拦截规则,预防误操作
1.3 框架结构
框架结构 |
---|
2、Mybatis-Plus 入门
2.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.2 导入mybatis-plus依赖
<!-- mybatis-plus依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>3.4.2</version>
</dependency>
2.3 Spring整合MP
<!-- 配置Mybatis-plus的SqlSessionFactoryBean -->
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- 加载mybatis配置 -->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<!-- 配置Mapper扫描 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.qf.mapper"/>
</bean>
2.4 编写实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Integer id;
private String name;
private Integer age;
private String email;
}
2.5 编写Mapper接口
//BaseMapper封装了基本的CRUD
public interface UserMapper extends BaseMapper<User> {
}
2.5 测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class MPTest {
@Autowired
private UserMapper userMapper;
@Test
public void test01(){
List<User> userList = userMapper.selectList(null);
userList.forEach(System.out::println);
}
}
3、Mybatis-Plus实现CRUD
- 通用 CRUD 封装BaseMapper (opens new window)接口,为
Mybatis-Plus
启动时自动解析实体表关系映射转换为Mybatis
内部对象注入容器- 泛型
T
为任意实体对象- 参数
Serializable
为任意类型主键Mybatis-Plus
不推荐使用复合主键约定每一张表都有自己的唯一id
主键- 对象
Wrapper
为 条件构造器
3.1 Insert增加
插入一条记录
//测试增加
@Test
public void test(){
User user = new User(null,"jack",30,"jack@qq.com");
userMapper.insert(user);
}
3.2 ID生成策略
通过@TableId(type = IdType.AUTO) 注解进行指定
主键类型 | 参数说明 |
---|---|
AUTO(0) | 数据库ID自增(数据库使用自增) |
NONE(1) | 该类型为未设置主键类型(将跟随全局) |
INPUT(2) | 用户手动输入ID |
ID_WORKER(3) | 全局唯一ID (idWorker ) |
UUID(4) | 全局唯一ID (UUID) |
ID_WORKER_STR(5) | 符串全局唯一ID (idWorker 的字符串表示) |
3.3 Delete删除
根据 ID 删除
删除(根据ID 批量删除)
//测试删除
@Test
public void test03(){
userMapper.deleteById(6);
}
//测试批量删除
@Test
public void test04(){
Integer[] ids = {1,2,3,4};
userMapper.deleteBatchIds(Arrays.asList(ids));
}
3.4 逻辑删除
在开发中有些数据不能直接从数据库中进行删除。用户删除数据有时候管理员还需要查看!!!
那么在实际的开发中可以实现
逻辑删除
实现:
- 1、在数据库中添加deleted字段(0表示未删除、1表示删除)
- 2、在实体上添加
@TableLogic
注解
@TableLogic
private Integer deleted;
//测试逻辑删除
@Test
public void test11(){
userMapper.deleteById(1);
}
# 执行sql语句为:
UPDATE user SET deleted=1 WHERE id=? AND deleted=0
3.5 Update修改
根据 ID 修改(参数为实体类对象)
//测试修改
@Test
public void test05(){
User user = new User(5,"rose",30,"rose@qq.com");
userMapper.updateById(user);
}
3.6 Select查询
根据 ID 查询
查询(根据ID 批量查询)
根据 entity 条件,查询全部记录
根据 Wrapper 条件,查询总记录数
//测试根据id查询
@Test
public void test06(){
User user = userMapper.selectById(1);
System.out.println(user);
}
//查询(根据ID 批量查询)
@Test
public void test07(){
List<User> userList = userMapper.selectBatchIds(Arrays.asList(1, 2, 3, 4));
userList.forEach(System.out::println);
}
//根据 entity 条件,查询全部记录
@Test
public void test08(){
List<User> userList = userMapper.selectList(null);
userList.forEach(System.out::println);
}
//根据 Wrapper 条件,查询总记录数
@Test
public void test09(){
Integer count = userMapper.selectCount(null);
System.out.println(count);
}
3.7 分页查询
- 使用数据库limit实现
- 使用PageHelper分页插件
- Mybatis-Plus内置了分页插件
根据 entity 条件,查询全部记录(并分页)
配置分页插件拦截器
<!-- 设置分页插件 -->
<plugins>
<plugin interceptor="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor"> </plugin>
</plugins>
//测试分页查询
@Test
public void test10(){
Page<User> page = new Page<>(1,3);
userMapper.selectPage(page, null);
//userPage中包含了分页的相关信息
System.out.println(page.getTotal());
List<User> userList = page.getRecords();
userList.forEach(System.out::println);
}
4、条件构造器
说明:
- 以下出现的第一个入参
boolean condition
表示该条件是否加入最后生成的sql中,例如:query.like(StringUtils.isNotBlank(name), Entity::getName, name) .eq(age!=null && age >= 0, Entity::getAge, age)- 以下代码块内的多个方法均为从上往下补全个别
boolean
类型的入参,默认为true
- 以下出现的泛型
Param
均为Wrapper
的子类实例(均具有AbstractWrapper
的所有方法)- 以下方法在入参中出现的
R
为泛型,在普通wrapper中是String
,在LambdaWrapper中是函数(例:Entity::getId
,Entity
为实体类,getId
为字段id
的getMethod)- 以下方法入参中的
R column
均表示数据库字段,当R
具体类型为String
时则为数据库字段名(字段名是数据库关键字的自己用转义符包裹!)!而不是实体类数据字段名!!!,另当R
具体类型为SFunction
时项目runtime不支持eclipse自家的编译器!!!- 以下举例均为使用普通wrapper,入参为
Map
和List
的均以json
形式表现!- 使用中如果入参的
Map
或者List
为空,则不会加入最后生成的sql中!!!
警告:
不支持以及不赞成在 RPC 调用中把 Wrapper 进行传输
- wrapper 很重
- 传输 wrapper 可以类比为你的 controller 用 map 接收值(开发一时爽,维护火葬场)
- 正确的 RPC 调用姿势是写一个 DTO 进行传输,被调用方再根据 DTO 执行相应的操作
- 我们拒绝接受任何关于 RPC 传输 Wrapper 报错相关的 issue 甚至 pr
@Test
public void test12(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//年龄大于12,id为1、2、3
queryWrapper
.ge("age",20)
.in("id",1,2,3);
userMapper.selectList(queryWrapper);
}
@Test
public void test13(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//邮箱不为空,姓名为jack
queryWrapper
.isNotNull("email")
.eq("name","jack");
userMapper.selectList(queryWrapper);
}
@Test
public void test15(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//按照年龄降序
queryWrapper
.orderByDesc("age");
userMapper.selectList(queryWrapper);
}
@Test
public void test16(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//姓名中包含s
queryWrapper
.like("name","s");
userMapper.selectList(queryWrapper);
}
@Test
public void test17(){
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
//姓名中包含s
updateWrapper
.like("name","s");
User user = new User(null,"rose",null,"rose@qq.com");
//修改以上条件构造器中包含的数据
userMapper.update(user,updateWrapper);
}
5、代码生成器
AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。
5.1 引入依赖
<!-- 代码生成器依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
<!-- 模板引擎 依赖 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
<!-- 日志依赖 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
5.2 生成器代码
public class CodeGenerator {
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.setAuthor("威哥");
//设置是否在资源管理器打开
gc.setOpen(false);
//设置生成代码是否覆盖
gc.setFileOverride(true);
//设置去除生成代码接口中的I
gc.setServiceName("%sService");
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql:///java2101");
dsc.setDriverName("com.mysql.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
//设置模块名称
//pc.setModuleName("shopping");
pc.setParent("com.qf");
pc.setEntity("pojo");
pc.setMapper("mapper");
pc.setService("service");
pc.setController("controller");
mpg.setPackageInfo(pc);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
//表名 下划线转驼峰
strategy.setNaming(NamingStrategy.underline_to_camel);
//字段名 下划线转驼峰
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
//实体类是否使用lombok
strategy.setEntityLombokModel(true);
//Controller是否使用RESTful风格(RestController)
strategy.setRestControllerStyle(true);
//设置逻辑删除字段
strategy.setLogicDeleteFieldName("deleted");
//设置生成的表名
strategy.setInclude("user");
mpg.setStrategy(strategy);
mpg.execute();
}
}