MyBatis-Plus——学习笔记
- 一、快速入门
- 1.0 准备工作
- 1.1 添加依赖&配置文件
- 1.3 UserMapper接口
- 1.4 测试类&测试效果
- 二、常用注解
- 2.1 注解
- 2.2 拓展(全局配置)
- 三、CRUD 接口
- 3.1 Service CRUD 接口
- 3.2 Mapper CRUD 接口
- 四、拓展功能
- 4.1 自动填充(FieldFill)
- 4.2 逻辑删除(@TableLogic)
- 五、插件
- 5.1 分页插件
- 5.1.1 添加配置
- 5.1.2 测试
- 5.1.3 自定义带条件的分页查询
- 5.1.4 总结: 分页查询到底怎么写?
- 5.2 代码生成器
参考:MyBatis-Plus
一、快速入门
1.0 准备工作
参考 MyBatis-Plus中的建表语句!
1.1 添加依赖&配置文件
springboot整合时:
<!-- springboot的单元测试依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- mybatis-plus的起步依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3.4</version>
</dependency>
<!-- mysql依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
配置文件:application.yml
#MyBatis-Plus配置
mybatis-plus:
global-config:
db-config:
#全局主键生成策略
id-type: assign_id #该值为默认值(雪花算法生成主键id值)
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #日志输出
【注】
- 别忘记在 Spring Boot 启动类中添加
@MapperScan 注解
,扫描 Mapper 文件夹;- 在User实体类上添加注解:
@TableName("t_user")
映射数据库中的 t_user 表;
1.3 UserMapper接口
UserMapper接口要实现BaseMapper接口;
public interface UserMapper extends BaseMapper<User> {
//Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能
}
1.4 测试类&测试效果
@SpringBootTest
class MybatisPlusDemoApplicationTests {
//注入的Mapper接口自定义实现了BaseMapper接口
@Autowired
private UserMapper userMapper;
@Test
void baseMapperTest1() {
int i = userMapper.insert(new User("zhangsan", 22, "123@qq.com"));
System.out.println(i);
}
}
当数据库中主键没有设置自增,并且在添加记录的时候没有手动设置主键id时,mybatis-plus默认会使用雪花算法
给记录生成主键id;如下
二、常用注解
2.1 注解
参考:MyBatis-Plus注解
2.2 拓展(全局配置)
全局配置: 要想影响所有实体的配置,可以设置全局主键配置;
#MyBatis-Plus 配置
mybatis-plus:
global-config:
db-config:
#全局主键生成策略
id-type: assign_id #该值为默认值(雪花算法生成主键id值)
三、CRUD 接口
3.1 Service CRUD 接口
参考:Service CRUD 接口
1)创建UserService接口
public interface UserService extends IService<User> {
// IService:顶级 Service,内部调用BaseMapper中的方法实现CRUD等操作
}
2)创建UserService的实现类
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
// ServiceImpl:IService 实现类( 泛型:M 是 mapper 对象,T 是实体 )
}
3)执行单元测试
@SpringBootTest
class MybatisPlusDemoApplicationTests {
//注入IService的实现类的Bean
@Autowired
private UserService userService;
@Test
void iServiceTest1(){
User user = userService.getById(2L);
System.out.println(user);
}
}
控制台运行效果:
- 如果我们在自己的XXXMapper中扩展了方法,这时候自己的XxxService中也要扩展一个方法来调用Mapper中的方法,在调用时,直接用baseMapper对象来调用就可以。(ServiceImpl实现类中注入了BaseMapper的实现类即UserMapper)
//ServiceImpl部分源码如下
public class ServiceImpl<M extends BaseMapper<T>, T> implements IService<T> {
//省略部分代码...
@Autowired
protected M baseMapper;
//省略部分代码...
}
3.2 Mapper CRUD 接口
参考:Mapper CRUD 接口
四、拓展功能
4.1 自动填充(FieldFill)
1)数据库表中添加两个属性;
2)实体类中添加对应属性值;
@TableName("t_user")
public class User {
@TableId
private Long id;
private String name;
private Integer age;
private String email;
//插入时填充字段(属性值和字段值不对应时要加value属性进行映射;看最下面的提示!)
@TableField(value = "create_time",fill = FieldFill.INSERT)
private LocalDateTime createTime;
//插入和更新时填充字段
@TableField(value = "update_time",fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
//省略构造方法和get、set方法
}
3)实现元对象处理器接口 -> 创建handler包,创建MyMetaObjectHandler类;(别忘记@Component注解)
/**
* @Description 元对象字段填充控制器抽象类的实现类,实现公共字段自动写入
* @Author cb
* @Date 2021-12-23 21:31
**/
@Component //别忘记把该Handler注入到容器中!!!
public class MyMetaObjectHandler implements MetaObjectHandler {
//log4j日志
Logger log = LoggerFactory.getLogger(MyMetaObjectHandler.class);
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill ...");
//根据标注了FieldFill的属性在 插入 的时候填充值
this.strictInsertFill(metaObject,"createTime", LocalDateTime.class,LocalDateTime.now());
this.strictInsertFill(metaObject,"updateTime",LocalDateTime.class,LocalDateTime.now());
log.info("end insert fill ...");
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill ...");
//根据标注了FieldFill的属性在 修改 的时候填充值
this.strictUpdateFill(metaObject,"updateTime",LocalDateTime.class,LocalDateTime.now());
log.info("end update fill ...");
}
}
4)新增测试&修改测试;
//新增测试
@Test
void baseMapperTest1() {
int i = userMapper.insert(new User("张良", 26, "14bx6@qq.com"));
System.out.println(i);
}
//修改测试
@Test
void baseMapperTest2() {
User user = new User("张良良",23,"cc@qq.com");
user.setId(1474013526022393858L);
int i = userMapper.updateById(user);
System.out.println(i);
}
注意:MyBatis-Plus会自动将数据库中的下划线命名风格转化为实体类中的驼峰命名风格;所以如果命名规范的话@TableField注解的value属性可以不用写。
4.2 逻辑删除(@TableLogic)
使用场景:可以进行数据恢复;
物理删除
:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除的数据;逻辑删除
:假删除,将数据表中代表是否被删除字段的状态修改为“被删除状态”,之后在数据库中仍旧能看到此条数据记录;
步骤:
1)数据库中创建逻辑删除状态列;
2)实体类中添加逻辑删除属性;
方式一:添加@TableLogic注解
;(从3.3.0版本以后:方式一和方式二两个二选一)
//如果在配置文件中进行全局配置逻辑删除时,此注解可以不用写;
//逻辑已删除值(默认为 1),逻辑未删除值(默认为 0)
@TableLogic
@TableField(value = "is_deleted")
private Integer deleted;
方式二:全局配置;
mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted # 全局逻辑删除的实体字段名
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
3)删除测试:删除功能被转变为更新功能;
-- 实际执行的SQL
UPDATE t_user SET is_deleted=1 WHERE id=? AND is_deleted=0
4)查询测试:被逻辑删除的数据默认不会被查询;
//查询测试
@Test
void selectAll(){
//QueryWrapper : 封装查询条件的抽象类
List<User> userList = userService.list(new QueryWrapper<User>().select("id","name","age"));
for (User user : userList) {
System.out.println(user);
}
}
-- 实际执行的SQL
SELECT id,name,age FROM t_user WHERE is_deleted=0
五、插件
5.1 分页插件
5.1.1 添加配置
/**
* @Description MyBatis-Plus的配置类
* @Author cb
* @Date 2021-12-23 23:16
**/
@SpringBootConfiguration
@MapperScan("com.ccbx.mapper") //可以将主类中的注解移到此处
public class MyBatisPlusConfig {
//MyBatisPlus拦截器
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//添加一个分页的内部拦截器
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
5.1.2 测试
封装一个类型为Page的分页参数对象,然后在Service类中调用BaseMapepr接口中的selectPage方法,或者直接调用IService接口的page方法都可以。
@Test
void testPage1(){
//当前页和每页条数
long current = 2,size = 5;
Page<User> page = new Page<>(current,size);
//包装查询条件
QueryWrapper<User> wrapper = new QueryWrapper<>();
//查询三个字段,并且年龄大于22
wrapper.select("id","name","age").gt("age",22);
//此处查询返回的也是Page对象,跟上面的Page一模一样,只不过是完善了上面Page中的属性的值
userService.page(page, wrapper);
System.out.println("总记录数:"+page.getTotal());
System.out.println("当前页的数据集合:");
List<User> userList = page.getRecords();
for (User user : userList) {
System.out.println(user);
}
}
日志中打印出来的SQL语句:
-- page.getTotal()方法调用的SQL语句
SELECT COUNT(*) AS total FROM t_user WHERE is_deleted = 0 AND (age > ?)
-- page.getRecords()方法调用的SQL语句
SELECT id,name,age FROM t_user WHERE is_deleted=0 AND (age > ?) LIMIT ?,?
5.1.3 自定义带条件的分页查询
方法1(简单查询): 不写SQL语句,用QueryWrapper来封装查询条件;如:
//包装查询条件
QueryWrapper<User> wrapper = new QueryWrapper<>();
//查询三个字段,并且年龄大于22
wrapper.select("id","name","age").gt("age",22);
方法2(复杂查询):把SQL写到SQL映射文件中,在SQL中拼接查询条件;
步骤:
1)添加全局配置
mybatis-plus:
# 加载类路径下的MyBatis 映射文件
mapper-locations: classpath:mappers/*Mapper.xml
2)UserMapper接口
public interface UserMapper extends BaseMapper<User> {
//自定义分页查询(这里封装查询条件的时候可以使用 实体类,VO类,Map集合等)
Page<User> queryByPage(Page<User> page, @Param("conditionMap") Map<String,Object> cmap);
}
3)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.ccbx.mapper.UserMapper">
<!--自定义的条件查询动态SQL-->
<select id="queryByPage" resultType="com.ccbx.pojo.User">
select id,name,age from t_user
<where>
<if test="null != conditionMap.name and '' != conditionMap.name">
name like #{conditionMap.name}
</if>
<if test="null != conditionMap.age">
and age > #{conditionMap.age}
</if>
</where>
</select>
</mapper>
4)UserService接口
public interface UserService extends IService<User> {
//自定义的分页条件查询方法
public Page<User> myQueryByPage();
}
5)UserServiceImpl实现类
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
//自定义的分页条件查询方法
public Page<User> myQueryByPage(){
long current = 1, size = 5;
Page<User> page = new Page<>(current, size);
Map<String,Object> map = new HashMap<>();
map.put("name","%J%");
map.put("age",18);
baseMapper.queryByPage(page,map);
return page;
}
}
6)测试方法:
@Test
void myPageTest(){
Page<User> page = userService.myQueryByPage();
List<User> userList = page.getRecords();
for (User user : userList) {
System.out.println(user);
}
}
5.1.4 总结: 分页查询到底怎么写?
-
没有带查询条件,直接在Controller中调用Service中的page( Page对象, null )
-
带查询条件,但是条件封装不复杂,可以在Service类中调用
BaseMapepr接口
中的selectPage方法,或者直接调用IService接口
的page方法都可以。 -
带复杂查询条件, 就在Mapper接口中新增方法,方法里一定带Page对象参数,和要封装的查询条件参数的map集合(实体类,Vo类都行);例如下面代码; 然后在Mapper.xml中来定义对应sql;
Page<User> queryByPage(Page<User> page, @Param("conditionMap") Map<String,Object> cmap);
5.2 代码生成器
参考:代码生成器(新)