MyBatis-plus简单来说就是一款 Mybatis 增强工具,用于简化开发,提高效率。下文使用缩写 mp来简化表示 MyBatis-plus,致力于 MyBatis 的基础上只做增强不做改变,为简化开发,提高效率而生。mybatis-plus只支持单表操作,联表查询不能使用。
1、mybatis-plus特点:
- 依赖少:仅仅依赖 Mybatis 以及 Mybatis-Spring 。
- 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作 。
- 预防Sql注入:内置 Sql 注入剥离器,有效预防Sql注入攻击 。
- 通用CRUD操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求 。
- 多种主键策略:支持多达4种主键策略(内含分布式唯一ID生成器),可自由配置,完美解决主键问题 。
- 支持热加载:Mapper 对应的 XML 支持热加载,对于简单的 CRUD 操作,甚至可以无 XML 启动
- 支持ActiveRecord:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可实现基本 CRUD 操作
- 支持代码生成:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码(生成自定义文件,避免开发重复代码),支持模板引擎、有超多自定义配置等。
- 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )。
- 支持关键词自动转义:支持数据库关键词(order、key…)自动转义,还可自定义关键词 。
- 内置分页插件:基于 Mybatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通List查询。
- 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库.
- 内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能有效解决慢查询 。
- 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,预防误操作。
- 默认将实体类的类名查找数据库中的表,使用@TableName(value="table1")注解指定表名,@TableId指定表主键,若字段与表中字段名保持一致可不加注解。
2、使用mybatis-plus
(1)创建数据库和表
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for student
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`age` int(11) NULL DEFAULT NULL,
`address` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES (2, '李四', 21, '郑州');
INSERT INTO `student` VALUES (3, '王五', 19, '深圳');
INSERT INTO `student` VALUES (4, '小黑', 24, '商丘');
INSERT INTO `student` VALUES (5, '小白', 17, '太原');
INSERT INTO `student` VALUES (7, 'dumpling', 23, '郑州');
SET FOREIGN_KEY_CHECKS = 1;
(2)引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--引入mybatis-plus依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
编写配置文件中的数据源配置和加入mybatis-plus的日志文件
# 配置mysql数据源
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.1.29:3306/stu?serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=990412
# mybatis-plus的日志文件stdout
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
(3)创建实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
//如果表名和实体类名不一致,使用该注解
@TableName("student")
public class Student {
//标记该属性为主键,value标记列名和属性名对应
@TableId(value = "id")
private Long id;
//如果属性名和列名不一致使用该注解
@TableField(value = "name")
private String name;
private Integer age;
private String address;
public Student(String name, Integer age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
}
(4)编写dao层
//BaseMapper接口中提供了单表的所有操作。crud.
@Mapper
public interface StudentMapper extends BaseMapper<Student> {
}
(5)测试
@SpringBootTest
class SpringbootMybatisPlusApplicationTests {
@Autowired
StudentMapper studentMapper;
@Test
void testSelectById(){
Student student = studentMapper.selectById(2);
System.out.println(student);
}
}
3、mybatis-plus添加
mybatis-plus框架为主键提供了相应的生成策略:
AUTO(0):递增策略,如果使用该策略必须要求数据表的列也是递增。
NONE(1)和INPUT(2):没有策略,必须人为的输入id值。
ASSIGN_ID(3),:随机生成一个Long类型的值。该值一定是唯一。而且每次生成都不会相同。使用雪花算法,适合分布式主键。要求数据库中该列的类型为bigint,实体类中的数据类型为long类型.
@SpringBootTest
class SpringbootMybatisPlusApplicationTests {
@Autowired
StudentMapper studentMapper;
@Test
void testInsert(){
Student student = new Student("dumplings",21,"太宁宫");
int insert = studentMapper.insert(student);
System.out.println(insert);
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
//如果表名和实体类名不一致,使用该注解
@TableName("student")
public class Student {
//标记该属性为主键,value标记列名和属性名对应
/*AUTO(0),递增策略,如果使用该策略必须要求数据表的列也是递增。
NONE(1),
INPUT(2),没有策略,必须人为的输入id值
ASSIGN_ID(3), 随机生成一个Long类型的值。该值一定是唯一。而且每次生成都不会相同。算法:雪花算法。 适合分布式主键。
要求数据库中该列的类型为bigint,实体类中的数据类型为long类型
ASSIGN_UUID(4); 随机产生一个String类型的值。该值也是唯一的。*/
@TableId(value = "id",type = IdType.ASSIGN_ID)
private Long id;
//如果属性名和列名不一致使用该注解
@TableField(value = "name")
private String name;
private Integer age;
private String address;
public Student(String name, Integer age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
}
运行结果:
4、mybatis-plus删除
在实际开发中,我们所谓的删除可能是逻辑删除,所谓的逻辑删除就是修改功能,修改该表中的逻辑字段,这个逻辑删除功能只对自动注入的sql起效:
插入:不限制,
查找:追加where条件过滤已删除数据,且使用wrapper.entity生成的where条件会忽略这个字段;
更新:追加where条件防止更新到已删除数据,且使用wrapper.entity生成的where条件会忽略这个字段;
删除:转变为更新
(1)增加一个逻辑字段: isdeleted 0表示未删除 1表示删除.
(2)实体类上的字段添加 @TableLogic注解,标记这个字段为逻辑字段
@SpringBootTest
class SpringbootMybatisPlusApplicationTests {
@Autowired
StudentMapper studentMapper;
/**
* 实际开发中: 我们的删除可能是逻辑删除。所谓的逻辑删除就是修改功能。把某个列修改以删除的状 态值。
* 只对自动注入的 sql 起效:
* 插入: 不作限制---
* 查找: 追加 where 条件过滤掉已删除数据,且使用 wrapper.entity 生成的 where 条件会忽略该字段
* 更新: 追加 where 条件防止更新到已删除数据,且使用 wrapper.entity 生成的 where 条件会忽略该字段
* 删除: 转变为 更新
* (1)增加一个逻辑字段: isdeleted 0表示未删除 1表示删除.
* (2)实体类上的字段添加 @TableLogic.
*/
@Test
void testDelete(){
int i = studentMapper.deleteById(1551549433282404353L);
System.out.println(i);
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
//如果表名和实体类名不一致,使用该注解
@TableName("student")
public class Student {
//标记该属性为主键,value标记列名和属性名对应
/*AUTO(0),递增策略,如果使用该策略必须要求数据表的列也是递增。
NONE(1),
INPUT(2),没有策略,必须人为的输入id值
ASSIGN_ID(3), 随机生成一个Long类型的值。该值一定是唯一。而且每次生成都不会相同。算法:雪花算法。 适合分布式主键。
要求数据库中该列的类型为bigint,实体类中的数据类型为long类型
ASSIGN_UUID(4); 随机产生一个String类型的值。该值也是唯一的。*/
@TableId(value = "id",type = IdType.ASSIGN_ID)
private Long id;
//如果属性名和列名不一致使用该注解
@TableField(value = "name")
private String name;
private Integer age;
private String address;
//标记该字段为逻辑字段,删除时修改该字段
@TableLogic
private Boolean isdeleted;
public Student(String name, Integer age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
}
运行结果:
5、mybatis-plus修改
自动填充功能:
在阿里规则中我们的每一张表必须具备的三个字段 id,create_time,update_time.
这两个字段要不要自己添加。
(1)在需要自动填充属性上@TableField(fill=)
(2)创建mp自动填充类
@SpringBootTest
class SpringbootMybatisPlusApplicationTests {
@Autowired
StudentMapper studentMapper;
@Test
void testUpdate(){
Student student = new Student();
student.setId(1551549433282404353L);
student.setName("skin");
int update = studentMapper.updateById(student);
System.out.println(update);
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
//如果表名和实体类名不一致,使用该注解
@TableName("student")
public class Student {
@TableId(value = "id",type = IdType.ASSIGN_ID)
private Long id;
//如果属性名和列名不一致使用该注解
@TableField(value = "name")
private String name;
private Integer age;
private String address;
//标记该字段为逻辑字段,删除时修改该字段
@TableLogic
@TableField(fill = FieldFill.INSERT)
private Boolean isdeleted;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.UPDATE)
private LocalDateTime updateTime;
public Student(String name, Integer age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
}
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill ....");
this.strictInsertFill(metaObject, "createTime", () -> LocalDateTime.now(), LocalDateTime.class); // 起始版本 3.3.3(推荐)
//添加记录时,自动填充逻辑字段的值为0
this.strictInsertFill(metaObject,"isDeleted",()->0,Integer.class);
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill ....");
this.strictUpdateFill(metaObject, "updateTime", () -> LocalDateTime.now(), LocalDateTime.class);
}
}
执行结果:
6、 mybatis-plus查询
(1)根据主键查询
@SpringBootTest
class SpringbootMybatisPlusApplicationTests {
@Autowired
StudentMapper studentMapper;
@Test
void testSelectById(){
Student student = studentMapper.selectById(2L);
System.out.println(student);
}
}
执行结果:
(2)根据各种条件查询
Wrapper:封装了关于查询的各种条件方法。有三个子类最常用:
QueryWrapper查询条件
UpdateWrapper修改条件
LambdaQueryWrapper查询使用lambda表达式条件
@Test
void testSelectByCondition(){
QueryWrapper<Student> wrapper = new QueryWrapper<>();
wrapper.ge("age",18);
studentMapper.selectList(wrapper);
}
执行结果:
(3)根据条件查询一条记录
@Test
void testSelectOne(){
//可以用于登录功能,比较用户名和密码两个列
QueryWrapper<Student> wrapper = new QueryWrapper<>();
wrapper.eq("name","dumpling");
wrapper.eq("age",23);
studentMapper.selectOne(wrapper);
}
执行结果:
(4)分页查询
mybatis-plus中内置了分页插件,不需要我们在手动添加依赖,不过我们需要添加分页插件的配置文件
@Configuration
public class MybatisPlusConfig {
/**
* 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
return interceptor;
}
}
测试分页插件的使用
@Test
void testPage(){
Page<Student> page = new Page<>(1,2);
// 两个参数,一个Page(分页对象),一个QueryWrapper(查询条件)
// selectPage()方法把查询的结果自动封装到Page对象中
studentMapper.selectPage(page,null);
System.out.println("总页数:"+page.getPages());
System.out.println("总条数:"+page.getTotal());
System.out.println("当前页记录:"+page.getRecords());
}
运行结果:
(5)联表查询使用mp的分页
mybatis-plus是对单表操作的增强,不支持联表查询,我们需要在接口中自己定义联表查询的方法
实体类:
@Data
@AllArgsConstructor
@NoArgsConstructor
//如果表名和实体类名不一致,使用该注解
@TableName("student")
public class Student {
//标记该属性为主键,value标记列名和属性名对应
/*AUTO(0),递增策略,如果使用该策略必须要求数据表的列也是递增。
NONE(1),
INPUT(2),没有策略,必须人为的输入id值
ASSIGN_ID(3), 随机生成一个Long类型的值。该值一定是唯一。而且每次生成都不会相同。算法:雪花算法。 适合分布式主键。
要求数据库中该列的类型为bigint,实体类中的数据类型为long类型
ASSIGN_UUID(4); 随机产生一个String类型的值。该值也是唯一的。*/
@TableId(value = "id",type = IdType.ASSIGN_ID)
private Long id;
//如果属性名和列名不一致使用该注解
@TableField(value = "name")
private String name;
private Integer age;
private String address;
//标记该字段为逻辑字段,删除时修改该字段
@TableLogic
@TableField(fill = FieldFill.INSERT)
private Boolean isdeleted;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.UPDATE)
private LocalDateTime updateTime;
//标记该列数据库中没有
@TableField(exist = false)
private Clazz clazz;
public Student(String name, Integer age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
}
dao层:
@Mapper
public interface StudentMapper extends BaseMapper<Student> {
IPage<Student> selectByCross(IPage<Student> page,@Param("ew") Wrapper<Student> queryWrapper);
}
映射文件:
<?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.gjx.dao.StudentMapper">
<resultMap id="resultMap" type="com.gjx.entity.Student" autoMapping="true">
<id property="id" column="id"/>
<association property="clazz" javaType="com.gjx.entity.Clazz" autoMapping="true">
<id property="clazzid" column="clazzid"/>
</association>
</resultMap>
<select id="selectByCross" resultMap="resultMap">
select * from student s join clazz c on s.clazzid=c.clazzid where s.isdeleted=0 and c.isdeleted=0
<if test="ew!=null">
and ${ew.sqlSegment}
</if>
</select>
</mapper>
测试:
@Test
void testCross(){
Page<Student> page = new Page<>(1,2);
IPage<Student> page1 = studentMapper.selectByCross(page,null);
System.out.println(page1.getRecords());
}
运行结果:
7、mybatis-plus的代码生成器
(1)引入依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>
(2)编写代码生成器
public class CodeGenerator {
public static void main(String[] args) {
FastAutoGenerator.create("jdbc:mysql://192.168.1.29:3306/stu?serverTimezone=Asia/Shanghai", "root", "root")
.globalConfig(builder -> {
builder.author("gjx") // 设置作者
.enableSwagger() // 开启 swagger 模式
.fileOverride() // 覆盖已生成文件
.outputDir("./src/main/java"); // 指定输出目录
})
.packageConfig(builder -> {
builder.parent("com") // 设置父包名
.moduleName("gjx") // 设置父包模块名
.pathInfo(Collections.singletonMap(OutputFile.xml, "./src/main/resources/mapper/")); // 设置mapperXml生成路径
})
.strategyConfig(builder -> {
builder.addInclude("student") // 设置需要生成的表名
.addInclude("clazz");
//.addTablePrefix("t_", "c_"); // 设置过滤表前缀
})
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
.execute();
}
}
然后直接运行就可以生成自己所需的文件了。