赞
踩
Mybatis-Plus(简称MP)是一个Mybatis的增强工具,在Mybatis的基础上只做增强不做改变,为简化开发、提高效率而生。
Mybatis-Plus已经封装好了大量增删改查的方法,程序员只需要继承BaseMapper就可以使用这些方法了,无需自己再开发。
向数据库保存一个User对象
create table tb_user ( id bigint(20) primary key auto_increment, username varchar(30) unique not null, name varchar(30) not null, password varchar(32) not null, age int(3) not null , tel varchar(32) not null, create_time datetime, update_time datetime ); insert into tb_user values(1,'zhangsan1','tom','123456',12,'12345678911',now(),now()); insert into tb_user values(2,'zhangsan2','jack','123456',8,'12345678912',now(),now()); insert into tb_user values(3,'zhangsan3','jerry','123456',15,'12345678910',now(),now()); insert into tb_user values(4,'zhangsan4','tom','123456',9,'12345678910',now(),now()); insert into tb_user values(5,'zhangsan5','snake','123456',28,'12345678910',now(),now()); insert into tb_user values(6,'zhangsan6','张益达','123456',22,'12345678910',now(),now()); insert into tb_user values(7,'zhangsan7','张大炮','123456',16,'12345678910',now(),now());
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.3</version> </parent> <dependencies> <!--mysql驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--连接池--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.3</version> </dependency> <!--mybatis plus依赖--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.1</version> </dependency> <!--lombok简化对象书写--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!--hutool工具包--> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.20</version> </dependency> <!--整合测试--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <!--web环境依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Long id;
private String username;
private String name;
private String password;
private Integer age;
private String tel;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
自定义的Mapper接口需要实现BaseMapper<实体类>,然后就可以继承到BaseMapper中定义的方法了
@Mapper
public interface UserMapper extends BaseMapper<User> {
//声明方法+注解实现sql
}
在resources中添加配置文件application.yml,然后在里面加入下面配置
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mp
username: root
password: 123456
# druid 阿里 HkariCp(springBoot)
type: com.alibaba.druid.pool.DruidDataSource
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 日志打印
map-underscore-to-camel-case: true # 驼峰映射
@SpringBootApplication
public class MpApplication {
public static void main(String[] args) {
SpringApplication.run(MpApplication.class,args);
}
}
@SpringBootTest public class UserMapperTest { @Autowired private UserMapper userMapper; @Test public void testInsert() { //1. 封装user对象 User user = User.builder() .username("lisi1") .name("tom") .password("123456") .age(18) .tel("13700137001") .build(); //2. 调用mapper方法 userMapper.insert(user); } }
标注在实体类上,用于声明实体类对应的表,如果表名和类名一致可以省略
//标注在实体类上,用于声明实体类对应的表,如果表名和类名一致可以省略
@TableName("tb_user")
public class User {
....
}
如果大部分表都是以固定前缀开头,可以全局配置表前缀,但优先级比注解配置低
mybatis-plus:
global-config:
db-config:
table-prefix: tb_ #表前缀
标注在主键字段上,用于声明主键生成策略
//主键生成策略
//AUTO : 数据库的自增
//INPUT: 让用户自己输入主键
//ASSIGN_ID: 雪花算法生成的一个纯数字
//ASSIGN_UUID: UUID生成一个不重复字符串
//ASSIGN_NONE: INPUT+ASSIGN_ID
@TableId(type = IdType.AUTO)
private Long id;
值 | 描述 |
---|---|
AUTO | 数据库主键自增 |
INPUT | 手动设置主键值 |
ASSIGN_ID | 由雪花算法生成的纯数字 |
ASSIGN_UUID | UUID生成的字符串 |
NONE | 默认值 相当于INPUT+ASSIGN_ID |
如果大部分表主键都是自增,可以进行全局设置,但优先级比注解配置低
mybatis-plus:
global-config:
db-config:
id-type: auto #主键策略
table-prefix: tbl_ #表前缀
BaseMapper:通用 CRUD 接口,内部声明了大量的单表操作方法,泛型
T
为任意实体对象
@Mapper
public interface ProductMapper extends BaseMapper<Product> {
}
MybatisPlus提供了单表的增删改查方法,常用的如下
// 插入一条记录 int insert(T entity); // 主键查询 T selectById(Serializable id); // 主键批量查询 List<T> selectBatchIds(Collection idList); // 根据ID修改不为空的字段 int updateById(T entity); // 根据ID删除 int deleteById(Serializable id); // 根据ID集合批量删除 int deleteBatchIds(Collection idList);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
@SpringBootTest public class UserMapperTest { @Autowired private UserMapper userMapper; //插入一条记录 //int insert(T entity); @Test public void testInsert() { //1. 封装user对象 User user = User.builder() .username("lisi1") .name("tom") .password("123456") .age(18) .tel("13700137001") .build(); //2. 调用mapper方法 userMapper.insert(user); } //主键查询 //T selectById(Serializable id); @Test public void testSelectById() { User user = userMapper.selectById(1L); System.out.println(user); } //主键批量查询 //List<T> selectBatchIds(Collection idList); @Test public void testSelectBatchIds() { //1. 构建id集合 List<Long> idList = ListUtil.of(1L, 2L, 3L); //2. 执行查询 List<User> userList = userMapper.selectBatchIds(idList); System.out.println(userList); } //根据ID修改不为空的字段 //int updateById(T entity); @Test public void testUpdateById() { //1. 封装user对象 User user = User.builder() .username("lisi2") .name("tom2") .tel("13700137002") .id(2L)//不要忘记设置id .build(); userMapper.updateById(user); } //根据ID删除 //int deleteById(Serializable id); @Test public void testDeleteById() { userMapper.deleteById(7L); } //根据ID集合批量删除 //int deleteBatchIds(Collection idList); @Test public void testDeleteBatchIds() { List<Long> idList = ListUtil.of(1L, 2L, 3L); userMapper.deleteBatchIds(idList); } }
MybatisPlus提供了Wrapper对象来封装各种条件,比如条件、分页、排序、分组、过滤等等
// 条件查询,当查询结果最多为一条记录时使用 手机号 身份证号 用户名 唯一约束 T selectOne(Wrapper<T> queryWrapper); // 条件查询,当查询结果可能为多条记录时使用 List<T> selectList(Wrapper<T> queryWrapper);
- 1
- 2
- 3
- 4
- 5
MybatisPlus支持使用多种格式组装条件,我们推荐使用Lambda格式
@SpringBootTest public class UserMapperTest2 { @Autowired private UserMapper userMapper; //根据name=李四和age>18查询(支持动态sql) @Test public void testSelectList1() { //0. 模仿前端传入参数 String name = null; Integer age = 18; //1. 构建查询条件 QueryWrapper<User> wrapper = new QueryWrapper<>(); if (StrUtil.isNotEmpty(name)) { wrapper.eq("name", name); //where name = '李四' } if (age != null) { wrapper.gt("age", age);//and age > 18 } //2. 查询 List<User> userList = userMapper.selectList(wrapper); userList.forEach(System.out::println); } //Lambda 根据name=李四和age>18查询(支持动态sql) @Test public void testSelectList2() { //0. 模仿前端传入参数 String name = "李四"; Integer age = 18; //1. 构建查询条件 LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); //参数1位置是一个boolean值,只有这个值为true,当前条件才会生效 wrapper.eq(StrUtil.isNotEmpty(name), User::getName, name);//where name = '李四' wrapper.gt(age != null, User::getAge, age);//and age > 18 //2. 查询 List<User> userList = userMapper.selectList(wrapper); userList.forEach(System.out::println); } //Lambda链式 根据name=李四和age>18查询(支持动态sql) @Test public void testSelectList3() { //0. 模仿前端传入参数 String name = "李四"; Integer age = 18; //1. 构建查询条件 LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); //参数1位置是一个boolean值,只有这个值为true,当前条件才会生效 wrapper.eq(StrUtil.isNotEmpty(name), User::getName, name)//where name = '李四' .gt(age != null, User::getAge, age);//and age > 18 //2. 查询 List<User> userList = userMapper.selectList(wrapper); userList.forEach(System.out::println); } }
查询方法 | 说明 | 例子 |
---|---|---|
eq、ne、gt、ge、lt、le、isNull、isNotNull | 比较运算 | eq(“name”, “老王”)---> name = ‘老王’ |
like、notLike、likeLeft、likeRight | 模糊查询 | likeRight(“name”, “王”)---> name like ‘王%’ |
in、notIn、between、notBetween | 范围运算 | in(“age”,{1,2,3})---> age in (1,2,3) |
or、and | 拼接 | eq(“id”,1).or().eq(“name”,“老王”)---> id = 1 or name = ‘老王’ |
//select * from tb_user where //id >= 1 //and username = 'baima' //and name like '%四' //and age between 10 and 30 //or tel in ('1370013001','1370013002') @Test public void testSelectList4() { //0. 模仿前端传入参数 String name = "李四"; Integer age = 18; //1. 构建查询条件 LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); wrapper.ge(User::getId,1) .eq(User::getUsername,"lisi") .likeLeft(User::getName,'四') .between(User::getAge,10,30) .or() .in(User::getTel,List.of("1370013001","1370013002")); //2. 查询 List<User> userList = userMapper.selectList(wrapper); userList.forEach(System.out::println); }
除了设置查询条件外,MP还支持:投影、排序、分组、过滤等功能
查询方法 | 说明 | 例子 |
---|---|---|
select | 投影 | select(“name”,“password”)---> select name,password from 表 |
orderByAsc、orderByDesc | 排序 | orderByDesc(“id”, “name”)---> order by id DESC,name DESC |
groupBy | 分组 | groupBy(“id”, “name”)---> group by id,name |
having | 过滤 | having(“sum(age) > 10”)---> having sum(age) > 10 |
@SpringBootTest public class UserMapperTest2 { @Autowired private UserMapper userMapper; //投影和排序 //select name,age from tb_user where id > 1 order by age desc @Test public void testSelectList1() { //1. 构建查询条件 LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); wrapper.gt(User::getId,1);//id > 1 wrapper.select(User::getName,User::getAge);//select name,age wrapper.orderByDesc(User::getAge);//order by age desc wrapper.orderByAsc(User::getId);//order by id asc //2. 查询 List<User> userList = userMapper.selectList(wrapper); userList.forEach(System.out::println); } //分组和过滤 //select age,count(*) from tb_user group by age having count(*) >= 2 @Test public void testSelectList2() { //1. 构建查询条件(LambdaQueryWrapper不支持分组和过滤) QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.select("age","count(*)");//select age,count(*) wrapper.groupBy("age");//group by age wrapper.having("count(*) >= 2");//having count(*) >= 2 //2. 查询 List<Map<String, Object>> list = userMapper.selectMaps(wrapper); list.forEach(System.out::println); } //分组和过滤 //select age,count(*) from tb_user group by age having count(*) >= 2 @Test public void testSelectList3() { List<Map<String, Object>> list = userMapper.count1(); list.forEach(System.out::println); } @Test public void testSelectList4() { List<Re> list = userMapper.count2(); list.forEach(System.out::println); } }
UserMapper
//自定义的Mapper 要求继承BaseMapper<实体类类型>
@Mapper
public interface UserMapper extends BaseMapper<User> {
//自定义
@Select("select age,count(*) from tb_user group by age having count(*) >= 2")
List<Map<String, Object>> count1();
@Select("select age,count(*) as num from tb_user group by age having num >= 2")
List<Re> count2();
}
Re
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Re {
private String age;
private Integer num;
}
MybatisPlus内置了专门用于分页的插件,使用起来非常简单,它是基于拦截器原理实现分页的
① 配置拦截器
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
//1 创建MybatisPlusInterceptor拦截器对象
MybatisPlusInterceptor mpInterceptor=new MybatisPlusInterceptor();
//2 添加分页拦截器
mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return mpInterceptor;
}
}
② 分页代码实现
//分页查询 //select * from tb_user where id > 1 limit 5,7 @Test public void testPage() { //1. 设置分页条件 当前页面 每页条数 Page<User> page = new Page<>(2, 3); //2. 设置业务条件 LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(User::getAge, 18); //3. 调用分页方法 page = userMapper.selectPage(page, wrapper); //4. 获取分页结果 System.out.println("总条数:" + page.getTotal()); System.out.println("总页数:" + page.getPages()); System.out.println("当前页数据集合:" + page.getRecords()); }
// 参数1: 封装要修改的字段 参数2: 封装更新条件 int update(T entity,Wrapper<T> updateWrapper);
- 1
- 2
@SpringBootTest public class UserMapperTest4 { @Autowired private UserMapper userMapper; //条件更新 //update tb_user set age = 10,password = '123123' where name = 'tom' @Test public void testUpdate() { //1. 设置更新条件 LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>(); wrapper.eq(User::getName, "tom"); //2. 设置更新字段 User user = User.builder() .age(10) .password("123123") .build(); //3. 执行更新 userMapper.update(user, wrapper); } }
// 条件删除 int delete(Wrapper<T> wrapper);
- 1
- 2
//条件删除
//delete from tb_user where name = 'tom';
@Test
public void testDelete() {
//1. 设置删除条件
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getName, "tom");
//2. 执行删除
userMapper.delete(wrapper);
}
逻辑删除指的是:当执行删除操作时,并不是将对应记录真正从数据库删掉,而是使用一个标识列,将要删除的数据标识为删除状态
MP使用@TableLogic就可以轻松实现这个功能
①:在user表中添加逻辑删除标记字段,并设置默认值为0
②:实体类中添加对应字段,设定为当前字段为逻辑删除标记字段
//逻辑删除字段:value用于指定未删除状态的值, delval用于指定删除状态的值
@TableLogic(value = "0", delval = "1")
private Integer deleted;
③ 删除其中一个用户观察效果
@TableLogic 只是单个表设置逻辑删除字段,如果多张表都需要配置逻辑删除,则可以做全局配置
mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted # 全局逻辑删除的实体字段名
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
对于数据表中的一些公共字段,我们可以使用mybatisplus的自动填充功能来统一设置值
@TableField注解的fill属性可以完成这个功能 [1. 什么时候填充 2. 填充什么值]
① 在实体类的字段上添加注解
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
值 | 描述 |
---|---|
INSERT | 插入时填充字段 |
UPDATE | 更新时填充字段 |
INSERT_UPDATE | 插入和更新时填充字段 |
② 在配置类中设置要填充的值
@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()); } }
③ 测试
//自动填充
@Test
public void testFill(){
//1. 封装user对象
User user = User.builder()
.username("lisi10")
.name("tom10")
.password("123456")
.age(18)
.tel("13700137001")
.build();
//2. 调用mapper方法
userMapper.insert(user);
}
多余属性指的是:实体类中存在,但是在数据表没有对应字段的属性
此时需要使用@TableField(exist = false)标识此属性
@Builder @Data @NoArgsConstructor @AllArgsConstructor //标注在实体类上,用于声明实体类对应的表,如果表名和类名一致可以省略 //@TableName("tb_user") public class User { //type: 声明主键生成策略 @TableId(type = IdType.AUTO) private Long id; private String username; private String name; private String password; @TableField(exist = false)//表示当前属性仅在实体类中存在,在数据表中没有对应的字段 private String password2; private Integer age; private String tel; @TableField(fill = FieldFill.INSERT) private LocalDateTime createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private LocalDateTime updateTime; //逻辑删除字段:value用于指定未删除状态的值, delval用于指定删除状态的值 //@TableLogic(value = "1", delval = "0") private Integer deleted; }
后端返回一个Long的id时候,前端接收到的数据精度有损失
pom.xml
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.13.0</version>
</dependency>
WebMvcConfig
@Configuration public class WebMvcConfig implements WebMvcConfigurer { //扩展mvc框架的消息转换器 public void extendMessageConverters(List<HttpMessageConverter<?>> converters) { //创建消息转换器对象 MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter(); //设置对象转换器,底层使用Jackson将Java对象转为json messageConverter.setObjectMapper(new JacksonObjectMapper()); //将上面的消息转换器对象追加到mvc框架的转换器集合中 converters.add(0, messageConverter); } /** * 对象映射器:基于jackson将Java对象转为json,或者将json转为Java对象 * 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象] * 从Java对象生成JSON的过程称为 [序列化Java对象到JSON] */ public class JacksonObjectMapper extends ObjectMapper { public JacksonObjectMapper() { super(); //对应JDK8+ 时间类型处理需要添加的模块 this.registerModule(new JavaTimeModule()); //收到未知属性时不报异常 this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false); //反序列化时,属性不存在的兼容处理 this.getDeserializationConfig().withoutFeatures(FAIL_ON_UNKNOWN_PROPERTIES); //自定义转换规则 SimpleModule simpleModule = new SimpleModule() .addSerializer(BigInteger.class, ToStringSerializer.instance)//将BigInteger转换为String .addSerializer(Long.class, ToStringSerializer.instance);//将Long转换成String this.registerModule(simpleModule); } } }
为了简化service代码编写,mybatisPlus也提供了的基础接口和实现类
我们只需要让我们自己的service去继承它提供的service,就可以使用里面的方法
进一步封装 CRUD 采用 get 查询单行
remove 删除
list 查询集合
page 分页
前缀命名方式区分 Mapper
层避免混淆
分类 | 方法 | 描述 |
---|---|---|
新增 | boolean save(T entity) | 新增,entity 实体对象 |
boolean saveOrUpdate(T entity) | id存在则更新记录,否插入一条记录 | |
boolean saveBatch(Collection entityList) | 插入(批量),默认一次可以保存1000条数据 | |
修改 | boolean updateById(T entity) | 根据 ID 修改 |
boolean update(T entity,Wrapper updateWrapper) | 根据 条件 修改 | |
查询 | T getById(Serializable id) | 根据 ID 查询 |
List listByIds(Collection idList) | 查询(根据ID 批量查询) | |
List list() | 查询所有 | |
List list(Wrapper queryWrapper) | 条件查询 | |
删除 | boolean removeById(Serializable id) | 根据 ID 删除 |
boolean removeByIds(Collection idList) | 删除(根据ID 批量删除) | |
boolean remove(Wrapper queryWrapper) | 根据条件删除 |
使用Service 接口使用
//自定义service接口
public interface UserService extends IService<User> {
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
@RestController public class UserController { @Autowired private UserService userService; @GetMapping("/user") public Result findList(){ List<User> userList = userService.list(); return Result.success(userList); } @PostMapping("/user") public Result save(@RequestBody User user){ userService.saveOrUpdate(user); return Result.success(); } @DeleteMapping("/user/{id}") public Result deleteById(@PathVariable("id") Long id){ userService.removeById(id); return Result.success(); } @GetMapping("/user/{id}") public Result findById(@PathVariable("id") Long id){ User user = userService.getById(id); return Result.success(user); } @PutMapping("/user") public Result update(@RequestBody User user){ userService.saveOrUpdate(user); return Result.success(); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。