目录
mybatis-plus入门篇 | 尚融宝02-mybatisplus复习 |
一、通用Mapper
MP中的基本CRUD在内置的BaseMapper中都已得到了实现。
创建MapperTests测试类:
package com.atguigu.mybatisplus;
@SpringBootTest
public class MapperTests {
@Resource
private UserMapper userMapper;
}
1、Create
@Test
public void testInsert(){
User user = new User();
user.setName("Helen");
user.setAge(18);
//不设置email属性,则生成的动态sql中不包括email字段
int result = userMapper.insert(user);
System.out.println("影响的行数:" + result); //影响的行数
System.out.println("id:" + user.getId()); //id自动回填
}
2、Retrieve
@Test
public void testSelect(){
//按id查询
User user = userMapper.selectById(1);
System.out.println(user);
//按id列表查询
List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
users.forEach(System.out::println);
//按条件查询
Map<String, Object> map = new HashMap<>();
map.put("name", "Helen"); //注意此处是表中的列名,不是类中的属性名
map.put("age", 18);
List<User> users1 = userMapper.selectByMap(map);
users1.forEach(System.out::println);
}
3、Update
@Test
public void testUpdate(){
User user = new User();
user.setId(1L);
user.setAge(28);
//注意:update时生成的sql自动是动态sql
int result = userMapper.updateById(user);
System.out.println("影响的行数:" + result);
}
4、Delete
@Test
public void testDelete(){
int result = userMapper.deleteById(5);
System.out.println("影响的行数:" + result);
}
二、通用Service
MP中有一个接口 IService和其实现类 ServiceImpl,封装了常见的业务层逻辑
1、创建Service接口
创建 service 包,创建 UserService,继承 IService
package com.atguigu.mybatisplus.service;
public interface UserService extends IService<User> {
}
2、创建Service实现类
创建 impl 包,创建 UserServiceImpl,继承 ServiceImpl,实现 UserService
package com.atguigu.mybatisplus.service.impl;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
3、创建测试类
创建ServiceTests
package com.atguigu.mybatisplus;
@SpringBootTest
public class ServiceTests {
@Resource
private UserService userService;
}
4、测试记录数
@Test
public void testCount(){
int count = userService.count();
System.out.println("总记录数:" + count);
}
5、测试批量插入
@Test
public void testSaveBatch(){
// SQL长度有限制,海量数据插入单条SQL无法实行,
// 因此MP将批量插入放在了通用Service中实现,而不是通用Mapper
ArrayList<User> users = new ArrayList<>();
for (int i = 0; i < 5; i++) {
User user = new User();
user.setName("Helen" + i);
user.setAge(10 + i);
users.add(user);
}
userService.saveBatch(users);
}
三、自定义Mapper
当通用Mapper无法满足我们的需求时,我们可以自定义基于Mapper接口的xml文件,并在xml文件中配置SQL语句
1、接口方法定义
在UserMapper接口中定义如下方法
List<User> selectAllByName(String name);
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.atguigu.mybatisplus.mapper.UserMapper">
<sql id="Base_Column_List">
id, name, age, email
</sql>
<select id="selectAllByName" resultType="com.atguigu.mybatisplus.entity.User">
select
<include refid="Base_Column_List"/>
from user
where
name = #{name}
</select>
</mapper>
注意:MP中mapper目录是持久层映射文件的默认目录,如果是其他目录,需要配置mapper-locations,例如:
mybatis-plus.mapper-locations=classpath:xml/*.xml
3、测试条件查询
在MapperTests中创建如下测试用例
@Test
public void testSelectAllByName(){
List<User> users = userMapper.selectAllByName("Helen");
users.forEach(System.out::println);
}
四、自定义Service
1、添加接口方法
UserService中添加接口方法
List<User> listAllByName(String name);
2、实现接口方法
@Override
public List<User> listAllByName(String name) {
// baseMapper对象指向当前业务的mapper对象
return baseMapper.selectAllByName("Helen");
}
4、测试
ServiceTests中添加测试方法
@Test
public void testListAllByName(){
List<User> users = userService.listAllByName("Helen");
users.forEach(System.out::println);
}
五、常用注解
1、@TableName
① value属性
实体类的名字是User,数据库表名是t_user
@TableName(value = "t_user")
public class User {
2、@TableId
① 指定主键列
- 测试:将数据库表中的id列改为 uid,将实体类中的id属性改成 uid,执行数据插入,则报告如下错误
- 原因:因为MP默认认为id是主键列,其他名字的属性MP无法默认自动填充
- 解决方案:为主键列添加 @TableId 注解
② value属性
实体类的属性名是 id,数据库的列名是 uid,此时使用 value 属性将属性名映射到列名
@TableId(value = "uid")
private String id;
③ type类型
type属性用来定义主键策略
- IdType.ASSIGN_ID:使用基于雪花算法的策略生成数据id
@TableId(value = "uid", type = IdType.ASSIGN_ID)
private Long id;
注意:当对象的id被明确赋值时,不会使用雪花算法
- IdType.AUTO:使用数据库的自增策略
@TableId(value = "uid", type = IdType.AUTO)
private Long id;
注意:该类型请确保数据库设置了 ID自增 否则无效
- 全局配置:要想影响所有实体的配置,可以设置全局主键配置
#全局设置主键生成策略
mybatis-plus.global-config.db-config.id-type=auto
关于雪花算法,不了解的同学可以看看我的这篇文章:UUID的弊端以及雪花算法
3、@TableField
① value属性
功能同TableId的value属性
注意:MP会自动将数据库中的下划线命名风格转化为实体类中的驼峰命名风格
例如,数据库中的列 create_time 和 update_time 自动对应实体类中的 createTime 和 updateTime
private LocalDateTime createTime;
private LocalDateTime updateTime;
扩展知识:为什么建议使用你 LocalDateTime ,而不是 Date?为什么建议使用你 LocalDateTime ,而不是 Date? - 知乎
- java.util.Date的大多数方法已经过时
- java.util.Date的输出可读性差
- java.util.Date对应的格式化类SimpleDateFormat是线程不安全的类。阿里巴巴开发手册中禁用static修饰SimpleDateFormat。
- LocalDateTime 对应的格式化类DateTimeFormatter是线程安全的
② 自动填充
需求描述:
项目中经常会遇到一些数据,每次都使用相同的方式填充,例如记录的创建时间,更新时间等。我们可以使用MyBatis Plus的自动填充功能,完成这些字段的赋值工作。
例如,阿里巴巴的开发手册中建议每个数据库表必须要有create_time 和 update_time字段,我们可以使用自动填充功能维护这两个字段
- step1:添加fill属性
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
- step2:实现元对象处理器接口 -> 创建handler包,创建MyMetaObjectHandler类
注意:不要忘记添加 @Component 注解
package com.atguigu.mybatisplus.handler;
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill ....");
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill ....");
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
}
③ 测试
- 测试新增
- 测试修改
④ 优化
- 避免自动填充时开销过大,填充前先判断当前对象中是否有相关属性
@Override
public void insertFill(MetaObject metaObject) {
//其他代码
//判断是否具备author属性
boolean hasAuthor = metaObject.hasSetter("author");
if(hasAuthor){
log.info("start insert fill author....");
this.strictInsertFill(metaObject, "author", String.class, "Helen");
}
}
- 用户明确定义了属性值,则无需自动填充,否则使用自动填充
@TableField(fill = FieldFill.INSERT)
private Integer age;
@Override
public void insertFill(MetaObject metaObject) {
//其他代码
//判断age是否赋值
Object age = this.getFieldValByName("age", metaObject);
if(age == null){
log.info("start insert fill age....");
this.strictInsertFill(metaObject, "age", String.class, "18");
}
}
4、@TableLogic
① 逻辑删除
- 物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除的数据
- 逻辑删除:假删除,将对应数据中代表是否被删除字段的状态修改为“被删除状态”,之后在数据库中仍旧能看到此条数据记录
使用场景:可以进行数据恢复
② 实现逻辑删除
- step1:数据库中创建逻辑删除状态列
- step2:实体类中添加逻辑删除属性
@TableLogic
@TableField(value = "is_deleted")
private Integer deleted;
③ 测试
- 测试删除:删除功能被转变为更新功能
-- 实际执行的SQL
update user set is_deleted=1 where id = 1 and is_deleted=0
- 测试查询:被逻辑删除的数据默认不会被查询
-- 实际执行的SQL
select id,name,is_deleted from user where is_deleted=0
@TableLogic默认1为删除0为未删除,可以通过application.xml配置改变默认标识
mybatis-plus: global-config: db-config: logic-delete-field: -1 # 设置-1为删除 logic-not-delete-value: 1 # 设置1为未删除