从分析MyBatis框架的开发效率开始
当使用MyBatis框架编写一个SQL需求时,需要以下几个步骤
1.创建Mapper接口,并提供一个抽象方法;
2.创建Mapper接口对应的配置文件,并提供对应的标签和SQL语句;
3.在Service对象中注入Mapper实例对象;
4.在Controller对象中注入Service对象;
5.Controller对象调用Service对象的方法,接着Service对象调用Mapper对象中的方法。
在以上几个步骤中,有几个步骤期望由框架进一步抽象化,带来更好的体验:
1.在单表查询时,可以由框架将常用的Mapper抽象方法和对应的SQL实现;
2.同时,进一步提供Service调用Mapper的常用方法;
3.其他的通用操作;
所以,MyBatis-Plus框架就由此而生。
MyBatis-Plus的特性
- 无侵入,低损耗;
- 内置通用Mapper,通用Service,通过少量配置即可实现单表大部分CRUD操作;
- 支持Lambda形式调用,主键自动生成,支持ActiveRecord模式;
- 支持自定义全局通用操作;
- 内置代码生成器,分页插件,性能分析器,全局拦截插件。
搭建SpringBoot工程启动第一个MyBatis-Plus程序
通过pom.xml引入MyBatis-Plus依赖;
创建UserMapper接口实现BaseMapper接口;
创建UserService接口实现IService接口;
创建UserServiceImpl类继承ServiceImpl类,并实现UserService接口;
在UserMapper接口中自定义的抽象方法,通过编写Mapper配置文件,提供对应的SQL即可;
通过Controller对象调用UserService对象的方法,UserService对象调用UserMapper对象中的方法完成操作。
// pom.xml
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
// application.xml
spring:
main:
// 去除SpringBoot的logo
banner-mode: off
datasource:
password: root
username: root
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/...
mybatis-plus:
global-config:
// 去除MyBatis-Plus的logo
banner: false
configuration:
// 配置log
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
// UserMapper.java
public class UserMapper extends BaseMapper<User> {
// 自定义抽象方法
List<User> selectUsersByBirthday(Date date);
}
// 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.mapper.UserMapper">
<select id="selectByBirthday" resultType="com.domain.User">
select * from user where birthday = #{value}
</select>
</mapper>
// UserService.java
public interface UserService extends IService<User> {
// 自定义抽象方法
List<User> getUsersByBirthday(Date date);
}
// UserServiceImpl.java
public class UserServiceImpl extends ServiceImpl<User> implements UserService {
@Autowired
UserMapper userMapper;
// 自定义抽象方法
User getUserByBirthday(Date date) {
return userMapper.selectUsersByBirthday(date);
}
}
// UserController.java
public class UserController {
@Autowired
UserService userService;
public void saveUser(User user) {
// 框架内置的单表CRUD方法
userService.save(user);
}
public List<User> getUsers(Date date) {
// 自定义方法
return userService.getUsersByBirthday(date);
}
}
MyBatis-plus中的映射规则
当表名和实体类名一致,表中的字段名和实体类属性名一致,即可自动映射;
在配置文件中开启驼峰命名自动映射后,可以支持下划线到驼峰的映射
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
通过使用注解@TableName
指定映射的数据库表名,按照指定的表名进行映射;
通过使用注解@TableField
指定映射的数据库表中的字段名,将注解中的名称和字段名完成映射;
配置属性@TableField(select=false)
完成隐藏字段,那么在拼接SQL语句时,就不会拼接这个字段;
配置属性@TableField(exit=false)
可以将表中不存在的字段去掉,那么这个字段就不会作为查询字段。
条件构造器
除了使用框架内置的单表CRUD方法,还通过使用条件构造器拼接方法,就能完成条件查询。
- 继承关系
Wrapper
是抽象类,条件类的顶层,提供获取和判断相关的方法;
AbstractWrapper
是抽象类,Wrapper
类的子类,提供所有条件相关的方法;
AbstractLambdaWrapper
是抽象类,AbstractWrapper
类的子类,将参数限定为方法引用类型;
LambdaWrapper
是AbstractLambdaWrapper
类的实现类,用于传递方法引用类型的字段信息;
QueryWrapper
是AbstractWrapper
类的实现类,用于传递String
类型的字段信息。
// QueryWrapper方式
User getUser() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name", "John");
return userMapper.selectOne(wrapper);
}
// LambdaWrapper方式
User getUser() {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getName, "John");
return userMapper.selectOne(lambdaQueryWrapper);
}
- API
等值查询:eq / allEq / ne
范围查询:gt / ge / lt / le / between / notBetween
模糊查询:like / notLike / likeLeft / likeRight
判空查询:isNull / isNotNull
包含查询:in / notIn / inSql / notInSql
(inSql / notInSql
存在SQL注入隐患)
分组查询:groupBy
聚合查询:having
排序查询:orderBy = orderByAsc / orderByDesc
逻辑查询:and
(非方法,正常拼接代表and)/ or / nested
(构建条件查询语句并and)
字段查询:select
func查询:func
(构建逻辑判断语句)
last查询:last
(将SQL拼接到语句最后,存在SQL注入隐患)
exists查询:exists
自定义条件查询:apply
(拼接在where后面)
主键策略
使用注解@TableId
指定主键生成策略
@TableId(type=IdType.AUTO)
跟随数据库表的主键递增策略
@TableId(type=IdType.INPUT)
指定手动插入id,否则无法添加数据
@TableId(type=IdType.ASSIGN-ID)
采用雪花算法策略(19位数字)
@TableId(type=IdType.NONE)
不指定策略时,设置为全局默认策略(默认是雪花算法)
@TableId(type=IdType.ASSIGN-UUID)
采用UUID策略(32位数字)
分页
使用分页插件需要配置分页插件
@Configuration
public class MybatisPlusconfig {
@Bean
public MybatisPlusInterceptor pageInterceptor() {
return new MybatisPlusInterceptor().addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL);
}
}
// 在自定义SQL语句中进行分页查询
// 将IPage对象作为参数传入
@Mapper
public interface UserMapper extends BaseMapper<User> {
IPage<User> selectByName(IPage<User> page, String name);
}
// controller
void selectPage() {
...
IPage<User> page = new Page<>(1,3);
userMapper.selectPage(page, lambdaQueryMapper);
userMapper.selectByName(page, lambdaQueryMapper);
...
}
ActiveRecord模式
将实体类对象继承Model
类,可以直接使用这个对象调用增删改查方法。底层仍需要Mapper
接口的支持,所以持久类接口并不能省略。
public class User extends Model<User> {
...
}
// 直接调用方法
user.insert();
user.deleteById();
user.updateById();
user.selectById();
SimpleQuery工具
使用SimpleQuery
类的API可以对查询结果使用Stream流进行封装。
拓展功能
- 逻辑删除
使用注解@TableLogic
指定数据表中的一个字段,将其设置为逻辑删除的状态字段。
// 执行删除操作后,记录不消失,状态由1变为0
@TableLogic(value="1", delval="0")
private Integer status;
// 还可以通过在配置文件进行全局配置
mybatis-plus:
global-config:
db-config:
logic-delete-field: status
logic-not-delete-value: 1
logic-delete-value: 0
- 通用枚举
对于适用枚举类作为数据类型的数据来说,无法将其插入到数据库中。这种情况下,使用注解@EnumValue
可以解决。
public enum GenderEnum {
MAN(0, "man"),
WOMAN(1, "woman");
@EnumValue
private Integer gender;
private String genderName;
GenderEnum(Integer gender, String genderName) {
this.gender = gender;
this.gender = genderName;
}
}
- 字段类型处理器
通过使用字段类型处理器,将实体类中的Map集合类型和数据库中的varchar类型数据进行互相转换。
// pom.xml
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
// 实体类字段
@TableField(typeHandler=FastjsonTypeHandler.class)
privete Map<String, String> contact;
- 自动填充
通过使用自动填充处理器完成自动填充。
// 配置处理器
@Component
public class TimeMetaOjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
setFieldValByName("createTime", new Date(), metaObject);
setFieldValByName("updateTime", new Date(), metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
setFieldValByName("updateTime", new Date(), metaObject);
}
// 实体类
public class user {
...
@TableField(fill=FieldFill.INSERT)
private Date createTime;
@TableField(fill=FieldFill.INSERT_UPDATE)
private Date updateTime;
}
- 防全表更新与删除插件
与配置分页插件同样的方式。
@Configuration
public class MybatisPlusconfig {
@Bean
public MybatisPlusInterceptor pageInterceptor() {
return new MybatisPlusInterceptor().addInnerInterceptor(new BlockAttackInnerInterceptor();
}
}
- 乐观锁
使用注解@Version
实现乐观锁。
需要在数据库的表中由对应的字段。
执行SQL分析打印
通过p6spy组件实现执行SQL分析,并将结果输出。
// pom.xml
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>3.9.1</version>
</dependency>
// 配置文件
spring:
datasource:
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
url: jdbc:p6spy:mysql
...
// p6spy配置文件
...
- 多数据源操作
使用注解@DS
可以对不同的数据库进行操作。
// pom.xml
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.1.0</version>
</dependency>
// 配置文件
spring:
datasource:
dynamic:
primary: master
strict: false
datasource:
master:
username: root
password: root
url: jdbc:mysql://localhost:3306/...
driver-class-name: com.mysql.cj.jdbc.Driver
slave_1:
username: root
password: root
url: jdbc:mysql://localhost:3306/...
driver-class-name: com.mysql.cj.jdbc.Driver
// 不同的Service使用不同的数据库
@Service
@DS("master")
public class UserServiceImplMaster implements UserService {
...
}
@Service
@DS("slave_1")
public class UserServiceImplSlaver implements UserService {
...
}
- 代码生成
使用MybatisX工具使用逆向工程生产Java工程
通过数据库的表结构可以生产出:实体类,Mapper接口,Mapper映射文件,Service接口,Service实体类。
通过代码生成器可以生成更多的结构,更多的内容。可配置项更丰富。
// pom.xml
<!--代码生成器依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.3</version>
</dependency>
<!--freemarker模板依赖-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>
// 使用FastAutoGenerator工具类完成配置并生成。