通过继承BaseMapper就可以获取到各种各样的单表操作,接下来我们将详细讲解这些操作。
BaseMapper.java源码
/*
* Copyright (c) 2011-2020, baomidou (jobob@qq.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.baomidou.mybatisplus.core.mapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import org.apache.ibatis.annotations.Param;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能
* <p>这个 Mapper 支持 id 泛型</p>
*
* @author hubin
* @since 2016-01-23
*/
public interface BaseMapper<T> extends Mapper<T> {
/**
* 插入一条记录
*
* @param entity 实体对象
*/
int insert(T entity);
/**
* 根据 ID 删除
*
* @param id 主键ID
*/
int deleteById(Serializable id);
/**
* 根据 columnMap 条件,删除记录
*
* @param columnMap 表字段 map 对象
*/
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
/**
* 根据 entity 条件,删除记录
*
* @param wrapper 实体对象封装操作类(可以为 null)
*/
int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
/**
* 删除(根据ID 批量删除)
*
* @param idList 主键ID列表(不能为 null 以及 empty)
*/
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
/**
* 根据 ID 修改
*
* @param entity 实体对象
*/
int updateById(@Param(Constants.ENTITY) T entity);
/**
* 根据 whereEntity 条件,更新记录
*
* @param entity 实体对象 (set 条件值,可以为 null)
* @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
*/
int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);
/**
* 根据 ID 查询
*
* @param id 主键ID
*/
T selectById(Serializable id);
/**
* 查询(根据ID 批量查询)
*
* @param idList 主键ID列表(不能为 null 以及 empty)
*/
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
/**
* 查询(根据 columnMap 条件)
*
* @param columnMap 表字段 map 对象
*/
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
/**
* 根据 entity 条件,查询一条记录
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 Wrapper 条件,查询总记录数
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 entity 条件,查询全部记录
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 Wrapper 条件,查询全部记录
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 Wrapper 条件,查询全部记录
* <p>注意: 只返回第一个字段的值</p>
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 entity 条件,查询全部记录(并翻页)
*
* @param page 分页查询条件(可以为 RowBounds.DEFAULT)
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
<E extends IPage<T>> E selectPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 Wrapper 条件,查询全部记录(并翻页)
*
* @param page 分页查询条件
* @param queryWrapper 实体对象封装操作类
*/
<E extends IPage<Map<String, Object>>> E selectMapsPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
}
1 插入操作
1.1 方法定义
/**
* 插入一条记录
*
* @param entity 实体对象
*/
int insert(T entity);
1.2 编写测试用例
TestUserMapper.java
package com.tian.springbootmybatis_plus;
import mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import pojo.User;
@SpringBootTest
@RunWith(SpringRunner.class)
public class TestUserMapper {
@Autowired
private UserMapper userMapper;
@Test
public void testInsert() {
User user = new User();
user.setAge(301);
user.setUserName("caocao1");
user.setName("曹操1");
user.setPassword("123456");
int result = this.userMapper.insert(user); //result数据库受影响的行数
System.out.println("result => " + result);
//获取自增长后的id值, 在Mybatis-Plus中自增长后的id值会回填到user对象中
System.out.println("id => " + user.getId());
}
}
运行结果:
可以看见,数据已经写入到了数据库,但是,id
的值不正确,我们期望的是数据库自增长,实际是MP
生成了id
的值写入到了数据库。
1.3 设置Mybatis-Plus Id的生成策略
MP
支持的id
策略:
IdType.java
/*
* Copyright (c) 2011-2020, baomidou (jobob@qq.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.baomidou.mybatisplus.annotation;
import lombok.Getter;
/**
* 生成ID类型枚举类
*
* @author hubin
* @since 2015-11-10
*/
@Getter
public enum IdType {
/**
* 数据库ID自增
*/
AUTO(0),
/**
* 该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
*/
NONE(1),
/**
* 用户输入ID
* <p>该类型可以通过自己注册自动填充插件进行填充</p>
*/
INPUT(2),
/* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */
/**
* 分配ID (主键类型为number或string),
* 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(雪花算法)
*
* @since 3.3.0
*/
ASSIGN_ID(3),
/**
* 分配UUID (主键类型为 string)
* 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(UUID.replace("-",""))
*/
ASSIGN_UUID(4),
/* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */
/*** 全局唯一ID (idWorker) */
@Deprecated
ID_WORKER(3),
/*** 字符串全局唯一ID (idWorker 的字符串表示) */
@Deprecated
ID_WORKER_STR(3),
/*** 全局唯一ID (UUID) */
@Deprecated
UUID(4);
private final int key;
IdType(int key) {
this.key = key;
}
}
修改User对象,指定id类型为自增长
先把原来那条数据删除,然后我们把ID自增长设置为6,紧着上面那条数据:
然后重新运行程序:
1.4 @TableField
在MyBtais-Plus
中通过@TableField
注解可以指定字段的一些属性,常常解决的问题有2个:
- 对象中的属性名和字段名不一致的问题(非驼峰,如果是驼峰命名法,
Mybatis-Plus
会自动帮你做转换,eg:userName
可以自动映射user_name
) - 对象中的属性字段在表中不存在的问题。
- 某些字段不加入查询(比如
password
)。
效果:
2. 更新操作
在Mybatis-Plus
中,更新操作有2种,一种是根据id更新,另一种是根据条件更新。
2.1 根据id更新
方法定义:
/**
* 根据 ID 修改
*
* @param entity 实体对象
*/
int updateById(@Param(Constants.ENTITY) T entity);
测试:
@SpringBootTest
@RunWith(SpringRunner.class)
public class TestUserMapper {
@Autowired
private UserMapper userMapper;
@Test
public void testUpdateById() {
User user = new User();
user.setId(6L); // 主键
user.setAge(21); // 更新的字段
// 根据id更新, 更新不为null的字段
this.userMapper.updateById(user);
}
}
运行结果:
2.2 根据条件更新
说明,大多数时候,QueryWrapper
和 UpdateWrapper
是可以相互替换的。
方法定义:
/**
* 根据 whereEntity 条件,更新记录
*
* @param entity 实体对象 (set 条件值,可以为 null)
* @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
*/
int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);
Wrapper
可以理解为查询条件。
2.2.1 QueryWrapper 用法
@Test
public void testUpdate() {
User user = new User();
user.setAge(2022); //更新的字段
user.setPassword("8888888");
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("user_name", "zhangsan"); //匹配user_name = zhangsan 的用户数据
//根据条件做更新
int result = this.userMapper.update(user, wrapper);
System.out.println("result => " + result);
}
运行结果:
2.2.2 UpdateWrapper 用法
UpdateWrapper
更加倾向于链式调用。
@Test
public void testUpdate2() {
UpdateWrapper<User> wrapper = new UpdateWrapper<>();
wrapper.set("age", 21).set("password", "999999") //更新的字段
.eq("user_name", "zhangsan"); //更新的条件
//根据条件做更新
int result = this.userMapper.update(null, wrapper);
System.out.println("result => " + result);
}
运行结果:
3. 删除操作
3.1 deleteById
方法定义:
/**
* 根据 ID 删除
*
* @param id 主键ID
*/
int deleteById(Serializable id);
具体示例:
@Test
public void testDeleteById() {
// 删除id为1的数据
int result = this.userMapper.deleteById(1L);
System.out.println("result => " + result);
}
运行结果:
3.2 deleteByMap
方法定义:
/**
* 根据 columnMap 条件,删除记录
*
* @param columnMap 表字段 map 对象
*/
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
具体示例:
@Test
public void testDeleteByMap() {
Map<String, Object> map = new HashMap<>();
// 删除的条件为 user_name为lisi, password为123456
map.put("user_name", "lisi");
map.put("password", "123456");
// 根据map删除数据,多条件之间是and关系
int result = this.userMapper.deleteByMap(map);
System.out.println("result => " + result);
}
运行结果:
3.3 delete
方法定义:
/**
* 根据 entity 条件,删除记录
*
* @param wrapper 实体对象封装操作类(可以为 null)
*/
int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
测试:
删除username
为caocao1
,password
为123456
的用户。
@Test
public void testDelete() {
//用法一: 注意, 这里QueryWrapper可以用updateWrapper替换
// QueryWrapper<User> wrapper = new QueryWrapper<>();
// wrapper.eq("user_name", "caocao1")
// .eq("password", "123456");
//用法二:
User user = new User();
user.setPassword("123456");
user.setUserName("caocao1");
QueryWrapper<User> wrapper = new QueryWrapper<>(user);
// 根据包装条件做删除
int result = this.userMapper.delete(wrapper);
System.out.println("result => " + result);
}
运行结果:
3.4 deleteBatchIds
方法定义:
/**
* 删除(根据ID 批量删除)
*
* @param idList 主键ID列表(不能为 null 以及 empty)
*/
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
测试:
@Test
public void testDeleteBatchIds() {
// 根据id批量删除数据
int result = this.userMapper.deleteBatchIds(Arrays.asList(3L, 4L));
System.out.println("result => " + result);
}
运行结果:
4. 查询操作(注意匹配的字段是数据库的字段,不是实体类的字段)
Mybatis-Plus
提供了多种查询操作,包括根据id查询、批量查询、查询单条数据、查询列表、分页查询等操作。
注意匹配的字段是数据库的字段,不是实体类的字段。假如数据库里面的字段是user_name,那么查询的时候应该eq(“user_name”,xxx),而不是q(“userName”,xxx)。
4.1 selectById
方法定义:
/**
* 根据 ID 查询
*
* @param id 主键ID
*/
T selectById(Serializable id);
测试:
@Test
public void testSelectById() {
// 查询id为5的用户
User user = this.userMapper.selectById(5L);
System.out.println(user);
}
4.2 selectBatchIds
方法定义:
/**
* 查询(根据ID 批量查询)
*
* @param idList 主键ID列表(不能为 null 以及 empty)
*/
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
示例:
我们先给数据库多插入几条数据,前面的数据快被删完了,太惨了。
-- 插入测试数据
INSERT INTO `tb_user` ( `id`, `user_name`, `password`, `name`, `age`, `email` )
VALUES
( '1', 'zhangsan', '123456', '张三', '18', 'test1@itcast.cn' );
INSERT INTO `tb_user` ( `id`, `user_name`, `password`, `name`, `age`, `email` )
VALUES
( '2', 'lisi', '123456', '李四', '20', 'test2@itcast.cn' );
INSERT INTO `tb_user` ( `id`, `user_name`, `password`, `name`, `age`, `email` )
VALUES
( '3', 'wangwu', '123456', '王五', '28', 'test3@itcast.cn' );
INSERT INTO `tb_user` ( `id`, `user_name`, `password`, `name`, `age`, `email` )
VALUES
( '4', 'zhaoliu', '123456', '赵六', '21', 'test4@itcast.cn' );
下面编写测试代码:
@Test
public void testSelectBatchIds() {
// 根据id批量查询数据 id为1,2,3,4
List<User> users = this.userMapper.selectBatchIds(Arrays.asList(1L, 2L, 3L, 4L));
for (User user : users) {
System.out.println(user);
}
}
运行结果:
4.3 selectOne
方法定义:
/**
* 根据 entity 条件,查询一条记录
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
示例:
@Test
public void testSelectOne() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
//查询条件
wrapper.eq("password", "123456");
// 查询的数据超过一条时, 会抛出异常
User user = this.userMapper.selectOne(wrapper);
System.out.println(user);
}
运行结果:
我们改一下条件,只定位到一条数据。
@Test
public void testSelectOne() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
//查询条件
wrapper.eq("password", "123456").eq("id", 1);
// 查询的数据超过一条时, 会抛出异常
User user = this.userMapper.selectOne(wrapper);
System.out.println(user);
}
4.4 selectCount
方法定义:
/**
* 根据 Wrapper 条件,查询总记录数
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
示例:查询年龄大于20岁的用户数量
@Test
public void testSelectCount() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
// gt是做大于的操作
wrapper.gt("age", 20); // 条件:年龄大于20岁的用户
// 根据条件查询数据条数
Integer count = this.userMapper.selectCount(wrapper);
System.out.println("count is : " + count);
}
运行结果:
4.5 selectList
方法定义:
/**
* 根据 entity 条件,查询全部记录
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
具体案例1:查询email字段包含itcast的用户
@Test
public void testSelectList() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
//设置查询条件 这里设置的条件是email字段包含itcast这个值
wrapper.like("email", "itcast");
List<User> users = this.userMapper.selectList(wrapper);
for (User user : users) {
System.out.println(user);
}
}
运行结果:
具体案例2:查询年龄大于23岁的用户
@Test
public void testSelectList() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
//设置查询条件 这里设置的条件是年龄大于23岁
wrapper.gt("age", 23);
List<User> users = this.userMapper.selectList(wrapper);
for (User user : users) {
System.out.println(user);
}
}
运行结果:
4.6 selectPage
方法定义:
/**
* 根据 entity 条件,查询全部记录(并翻页)
*
* @param page 分页查询条件(可以为 RowBounds.DEFAULT)
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
<E extends IPage<T>> E selectPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
配置分页插件:
MybatisPlusConfig.java
package com.tian.springbootmybatisplus;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@MapperScan("com.tian.mapper") //因为这个是配置类 所以我们把启动类的包扫描放在了这里
public class MybatisPlusConfig {
@Bean //配置分页插件
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
编写测试用例:查询第一页, 查询3条数据(email包含itcast的数据)
// 测试分页查询
@Test
public void testSelectPage() {
Page<User> page = new Page<>(1, 3); //查询第一页, 查询3条数据
QueryWrapper<User> wrapper = new QueryWrapper<>();
//设置查询条件
wrapper.like("email", "itcast");
IPage<User> iPage = this.userMapper.selectPage(page, wrapper);
System.out.println("数据总条数: " + iPage.getTotal());
System.out.println("数据总页数: " + iPage.getPages());
System.out.println("当前页数: " + iPage.getCurrent());
// 把拿到的数据打印出来
List<User> records = iPage.getRecords();
for (User record : records) {
System.out.println(record);
}
}
运行结果: