说明:Mybaits-Plus是Mybatis框架的升级,该框架提供了一系列API,用于操作数据,可以免受手搓SQL语句的痛苦。
一、使用
第一步:添加依赖
使用前,需先添加对应的依赖,建议使用最新的版本
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
application.yml配置文件
# 1.数据源的配置
spring:
# 数据库配置
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/db_mybatis_plus
username: root
password: 123456
# 2.mybatis-plus配置
mybatis-plus:
configuration:
# 配置日志的实现类 stdOutImpl 输出到控制台
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.hzy</groupId>
<artifactId>essay_mybatis_plus</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<!--springboot-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.12</version>
<relativePath/>
</parent>
<dependencies>
<!--web依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<!--数据库连接池-->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!--测试类-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--hutool-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.6</version>
</dependency>
</dependencies>
</project>
第二步:环境搭建
创建一张User表,注意ID设置为bigint类型
创建一个User实体类
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
private Long id;
private String userName;
private String password;
private String name;
private Integer age;
private String email;
private LocalDateTime createTime;
private LocalDateTime updateTime;
private Integer del;
}
第三步:关联表与MyBatis-Plus
实体类中增加@TableName()注解,括号内填该实体类在数据中对应的表名称;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TableName("tb_user")
public class User implements Serializable {
private Long id;
private String userName;
private String password;
private String name;
private Integer age;
private String email;
private LocalDateTime createTime;
private LocalDateTime updateTime;
private Integer del;
}
Mapper层继承BaseMapper类,尖括号内填该Mapper对应的实体类对象;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.hzy.pojo.User;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
第四步:测试
在测试类中,装配Mapper,使用属性名.的方式,查看是否有MyBatis-plus提供的API,有的话说明已经关联成功,此时就可以使用API对数据库进行增删改查操作
二、API
(1)添加数据
添加一条对象数据
@Test
public void insertTest(){
User user = new User();
user.setUserName("zhangsan");
user.setPassword("123456");
user.setName("张三");
user.setAge(18);
user.setEmail("zhangsan@gmail.com");
userMapper.insert(user);
}
需要注意:
1)这里的ID是MyBatis-Plus自动生成的,后面可以修改生成ID的策略;
2)不需要在配置中设置对象属性名(userName)和数据表字段名(user_name)允许驼峰命名,MyBatis-Plus会自动适配;
3)添加后的数据,会自动填充ID的,不需要考虑主键返回的问题;
(2)删除数据
@Test
public void deleteTest(){
// 1.根据ID删除
int i = userMapper.deleteById(1677652427726557186L);
System.out.println(i);
// 2.根据ID批量删除
ArrayList<Long> ids = new ArrayList<>();
ids.add(1L);
ids.add(2L);
ids.add(3L);
userMapper.deleteBatchIds(ids);
// 3.根据条件(Map)删除
HashMap<String, Object> hashMap = new HashMap<>();
hashMap.put("user_name","lisi");
userMapper.deleteByMap(hashMap);
// 4.根据条件(QueryWrapper)删除
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.gt("age",20);
int j = userMapper.delete(wrapper);
System.out.println("j = " + j);
}
需要注意,条件删除使用Map,只能做等值判断,不能做比较判断,建议使用QueryWrapper
(3)修改数据
例如,将下面这条记录的name修改为小明;
@Test
public void updateTest() {
// 1.根据ID修改
User user1 = new User();
user1.setId(1677657896868278273L);
user1.setName("小明");
userMapper.updateById(user1);
// 2.根据条件(QueryWrapper)修改
User user2 = new User();
user2.setName("小明");
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("id", 1677657896868278273L);
userMapper.update(user2, wrapper);
// 3.根据条件(UpdateWrapper)修改
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("user_name", "zhangsan");
updateWrapper.set("name", "小明");
userMapper.update(null, updateWrapper);
// 4.使用lambda+方法引用简写代码
userMapper.update(null, Wrappers.lambdaUpdate(User.class)
.eq(User::getUserName,"zhangsan").set(User::getName,"小明"));
}
其中eq是等值判断,其他的比较方法如下:
需要注意的是:
(1)使用ID修改记录,传入的对象需要设置ID;
(2)使用UpdateWrapper做条件修改,相较于QueryWrapper,前者可以在条件判断的同时设置修改后的值;
(4)查询数据
Mybatis-Plus中有丰富的查询API,如下,其中根据条件查询单条记录(2)和多条记录(5)较为常用;
@Test
public void selectTest() {
// 1.根据ID查询:查询ID等于2的记录
User user1 = userMapper.selectById(2L);
System.out.println(user1);
// 2.根据条件查询:查询user_name等于zhangsan的记录(常用)
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getUserName,"zhangsan");
User user2 = userMapper.selectOne(wrapper);
System.out.println(user2);
// 3.根据ID集合进行批量查询:查询id in (1,2,3)的记录
List<Long> ids = new ArrayList<>();
ids.add(1L);
ids.add(2L);
ids.add(3L);
List<User> users = userMapper.selectBatchIds(ids);
System.out.println(users);
// 4.根据条件查询多条记录,但只返回首个字段,即主键的值:查询年龄为20的记录,返回这些集合的主键值
List<Object> objects = userMapper.selectObjs(Wrappers.lambdaQuery(User.class).ge(User::getAge,20));
System.out.println(objects);
// 5.根据条件查询多条记录:查询年龄为20的所有记录(常用)
List<User> userList = userMapper.selectList(Wrappers.lambdaQuery(User.class).ge(User::getAge,20));
System.out.println(userList);
// 6.根据条件查询总记录条数:条件为空,为查询所有记录条数
Long count = userMapper.selectCount(null);
// 7.模糊查询:统计user_name中以zh开头的记录
Long likeCount = userMapper.selectCount(Wrappers.lambdaQuery(User.class).likeRight(User::getUserName, "zh"));
System.out.println(likeCount);
// 8.使用Map作为条件进行查询:查询user_name等于zhangsan的记录
HashMap<String, Object> hashMap = new HashMap<>();
hashMap.put("user_name", "zhangsan");
List<User> selectByMap = userMapper.selectByMap(hashMap);
System.out.println(selectByMap);
// 9.把查询结果封装到Map中
List<Map<String, Object>> maps = userMapper.selectMaps(Wrappers.emptyWrapper());
for (Map<String, Object> map : maps) {
System.out.println(map);
}
// 10.多条件查询1:查询年龄大于20岁,并且 user_name以zh开头的
List<User> selectList = userMapper.selectList(Wrappers.lambdaQuery(User.class).gt(User::getAge, 20).likeRight(User::getUserName, "zh"));
for (User user : selectList) {
System.out.println(user);
}
// 11.多条件查询2:查询年龄小于25岁 或者 user_name包含zh的
List<User> list = userMapper.selectList(Wrappers.lambdaQuery(User.class).lt(User::getAge, 25).like(User::getUserName, "zh"));
for (User user : list) {
System.out.println(user);
}
// 12.查询指定字段:查询ID等于10的记录的id、user_name字段
User user = userMapper.selectOne(Wrappers.lambdaQuery(User.class).select(User::getId, User::getUserName).eq(User::getId, 10L));
System.out.println(user);
// 13.排序查询:查询年龄大于25岁的,并且按照年龄进行倒序
List<User> users1 = userMapper.selectList(Wrappers.lambdaQuery(User.class).gt(User::getAge, 25).orderByDesc(User::getAge));
for (User user3 : users1) {
System.out.println(user3);
}
}
三、MyBatis-Plus的其他功能
Mybatis-Plus除了提供一套API外,还提供了一些附加功能;
(1)主键填充
可在实体类中,设置ID自动生成ID的策略,AUTO为数据库自动递增策略
@TableId(type = IdType.AUTO)
private Long id;
关于IdType的备选项,官网说明如下:
(2)数据填充
Mybatis-Plus提供了对添加数据、更新数据进行填充的功能,如用户字段create_time、update_time的值,可由Mybatis-Plus自动填充;
第一步:在对应字段上添加注解
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
第二步:创建配置类
可参考官网代码(https://baomidou.com/pages/4c6bcf/),创建一个配置类,该类的功能是对查询、更新方法的增强,即填充指定的字段值;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
/**
* MyMetaObjectHandler
*
* 是Mybatis-Plus提供的一个接口
*/
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
/**
* 插入操作 自动填充createTime、updateTime的值
* @param metaObject 元对象 就是使用CURD时传入的对象
*/
@Override
public void insertFill(MetaObject metaObject) {
// 注意这里的数据类型要与JavaBean中对应属性的数据类型一致
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
/**
* 更新操作 自动修改updateTime的值
* @param metaObject
*/
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
}
需要注意以下两点:
(1)自动填充未做非空校验:即添加的对象已设置了create_time、update_time的值,应该以传入的值为准,不再填充;
(2)自动填充的数据类型应该与JavaBean对象对应字段的数据类型一致,如create_time属性,JavaBean中类型是LocalDateTime,则自动填充的数据库类型也应该是LocalDateTime,而不能是LocalDate;
(3)逻辑删除
在实际业务中,如果用户注销账户,对于用户来说,是删除了账户有关的所有记录,如订单、好友列表等等,但对于后台管理端来说,该用户的数据已经产生了,已经在各种统计报表中存在。如果用户注销账户,也同步删除数据库中的相关数据,会造成报表数据有偏差。
所以,当用户注销账户时,对于数据库来说,会将该用户相关的表数据设置为“已删除”,而不会真正删除。具体实现为,在表中设置一个“标识判断”,表示该条记录是否为业务层面上的“已删除数据”或者“正常数据”,这种方式称为逻辑删除。
如上面tb_user表中的最后一个字段(del),可在对应的JavaBean中,在del属性上设置逻辑删除注解;表示“正常数据”值为0,“已删除数据”值为1。设置后,后续所有对数据库的操作,会自动考虑,如delete方法只会更新该标识字段,select方法会自动追加判断该数据是否为“已删除数据”。
@TableLogic(value = "0",delval = "1")
private Integer del;
需要注意以下两点:
(1)在数据库表中,该字段不能为空,只能有设置的0、1这两个值,所以建议该字段创建时设置默认值(当然默认值应该为“正常数据”);
(2)该功能的实现,实际上就是在SQL语句后面追加了对数据标识字段的判断,如“and del = 0”。所以当查询结果为null时,应考虑查询的该条记录标识字段是否设置为了“已删除”;
(4)属性排除
当我们设置了一个JavaBean,该JavaBean中存在一个属性,该属性不能和数据库表字段一一对应时,我们使用对应的API去查询数据库时,由于该属性值不能在数据库表字段中找到,查询会报错。
比如,在上面的User类中增加一个orders属性,数据类型为List,表示该用户的订单;
private List<Order> orders;
查询会报错,因为该属性在数据库表中并没有与之对应的字段
此时,我们可以用MyBatis-Plus中的属性排除,在对应属性上设置@TableField(exist = false)注解,排除掉该属性,让查询记录时不查询该字段;
@TableField(exist = false)
private List<Order> orders;
(5)Service增强
当一个请求非常简单,没有复杂的业务逻辑时,可以使用MyBatis-Plus提供的Service增强技术,可不再Service层中编写对应的方法,直接在Controller层中,使用Service对象,调用到Mapper层中的方法。具体操作如下:
第一步:Service接口继承IService
在Service接口中,继承MyBatis-Plus的IService类,泛型填对应的JavaBean;
import com.baomidou.mybatisplus.extension.service.IService;
import com.hzy.pojo.User;
public interface UserService extends IService<User> {
}
第二步:Service实现类继承ServiceImpl
在Service实现类中,继承MyBatis-Plus的ServiceImpl类,并设置泛型,泛型内分别写对应Mapper和JavaBean;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hzy.mapper.UserMapper;
import com.hzy.pojo.User;
import com.hzy.service.UserService;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
第三步:Controller层中使用
此时,Service层中不需要写方法,就可以使用对数据库操作的API
import com.hzy.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/test")
public String test() {
return userService.getById(2L).toString();
}
}
(6)分页查询
使用MyBatis-Plus的分页功能,先需要创建配置类;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Mybatis-Plus配置类
*/
@Configuration
public class MybatisConfig {
/**
* 手动装配Bean对象
* @return
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
// 创建MybatisPlusInterceptor
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 设置数据库类型,不同数据库的限制记录语句有所不同
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
使用selectPage()方法实现分页功能
@Test
public void pageSelect() {
// 从第2页开始,每页显示2条,条件为null,需要注意Page.of是新版本才有的功能,3.4.0版本没有
IPage<User> userIPage = userMapper.selectPage(Page.of(2, 2), null);
// 总页数
userIPage.getPages();
// 总记录数
userIPage.getTotal();
// 当前页码
userIPage.getCurrent();
// 每页显示的条数
userIPage.getSize();
// 当前页的数据
userIPage.getRecords();
for (User record : userIPage.getRecords()) {
System.out.println("record = " + record);
}
}
可以看到分页查询功能正常(只查询出第2页的记录)、逻辑删除功能正常(del字段为1的记录未被查询出来);
(7)代码生成器
代码生成指Mybatis-Plus可以根据数据表,自动生成对应的实体类、三层框架、mapper.xml文件代码;官网有提供对应的功能,但在Github上也有大神提供,我这里是用了Github用户(davidfantasy)的代码(https://github.com/davidfantasy/mybatis-plus-generator-ui),比较好用。
第一步:添加依赖
添加该代码的依赖
<!--github上复制的代码生成器 davidfantasy -->
<dependency>
<groupId>com.github.davidfantasy</groupId>
<artifactId>mybatis-plus-generator-ui</artifactId>
<version>2.0.5</version>
</dependency>
第二步:修改代码
修改下载下来的代码,适配自己的环境
第三步:运行使用
该类中是main()方法,可以直接运行使用,端口号是8068
(选择仅有的一张表,点代码生成)
(选择Entity,实体类)
(生成完成后会自动打开目录,点开目录找到生成后的实体类)
(可以看到查看生成后的实体类内容)
总结
MyBatis-Plus是一款非常强大的框架,提供了一系列对数据库操作的API和技术,让开发人员免于编写大量繁琐的SQL语句,关注于业务逻辑的开发。