- Mybatis+Mybatis-Plus
- Spring+Mybatis+Mybatis-Plus
- Spring Boot+Mybatis+Mybatis-Plus
通过继承BaseMapper就可以获取到各种各样的单表操作。
方法定义
/** * 插入一条记录 * * @param entity 实体对象 */ int insert(T entity);
测试用例
@RunWith(SpringRunner.class) @SpringBootTest public class TestUserMapper { @Autowired private UserMapper userMapper; @Test public void testInsert() { User user = new User(); user.setEmail("[email protected]"); user.setAge(301); user.setName("曹操"); user.setUserName("caocao"); user.setPassword("123456"); int result = this.userMapper.insert(user); // result数据库受影响的行数 System.out.println("result => " + result); // 获取自增长后的id值, 自增长后的id值会回填到user对象中 System.out.println("id => " + user.getId()); } }
测试结果
[main] [cn.com.javakf.mapper.UserMapper.insert]-[DEBUG] ==> Preparing: INSERT INTO tb_user ( id, user_name, password, name, age, email ) VALUES ( ?, ?, ?, ?, ?, ? ) [main] [cn.com.javakf.mapper.UserMapper.insert]-[DEBUG] ==> Parameters: 1281525642559074305(Long), caocao(String), 123456(String), 曹操(String), 301(Integer), caocao@163.com(String) [main] [cn.com.javakf.mapper.UserMapper.insert]-[DEBUG] <== Updates: 1 [main] [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1b4ae4e0] result => 1 id => 1281525642559074305
可以看到,数据已经写入到了数据库,但是,id的值不正确,我们期望的是数据库自增长,实际是Mybatis-Plus生成了id的值
写入到了数据库。
设置id的生成策略
Mybatis-Plus支持的id策略
package com.baomidou.mybatisplus.annotation; import lombok.Getter; /** * 生成ID类型枚举类 * * @author hubin * @since 2015-11-10 */ @Getter public enum IdType { /** * 数据库ID自增 */ AUTO(0), /** * 该类型为未设置主键类型 */ NONE(1), /** * 用户输入ID *
该类型可以通过自己注册自动填充插件进行填充
*/ INPUT(2), /* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */ /** * 全局唯一ID (idWorker) */ ID_WORKER(3), /** * 全局唯一ID (UUID) */ UUID(4), /** * 字符串全局唯一ID (idWorker 的字符串表示) */ ID_WORKER_STR(5); private final int key; IdType(int key) { this.key = key; } }修改User对象
@Data @NoArgsConstructor @AllArgsConstructor @TableName("tb_user") public class User { @TableId(type = IdType.AUTO) // 指定id类型为自增长 private Long id; private String userName; private String password; private String name; private Integer age; private String email; }
在Mybatis-Plus中通过@TableField注解可以指定字段的一些属性,常常解决的问题有2个
对象中的属性名和字段名不一致的问题(非驼峰)
对象中的属性字段在表中不存在的问题
使用
@Data @NoArgsConstructor @AllArgsConstructor @TableName("tb_user") public class User { @TableId(type = IdType.AUTO) // 指定id类型为自增长 private Long id; private String userName; private String password; private String name; private Integer age; @TableField(value = "email") // 指定数据表中字段名 private String mail; @TableField(exist = false) private String address; // 在数据库表中是不存在的 }
其他用法,如大字段不加入查询字段
@Data @NoArgsConstructor @AllArgsConstructor @TableName("tb_user") public class User { @TableId(type = IdType.AUTO) // 指定id类型为自增长 private Long id; private String userName; @TableField(select = false) // 查询时不返回该字段的值 private String password; private String name; private Integer age; @TableField(value = "email") // 指定数据表中字段名 private String mail; @TableField(exist = false) private String address; // 在数据库表中是不存在的 }
测试结果
[main] [cn.com.javakf.mapper.UserMapper.selectList]-[DEBUG] ==> Preparing: SELECT id,user_name,name,age,email AS mail FROM tb_user [main] [cn.com.javakf.mapper.UserMapper.selectList]-[DEBUG] ==> Parameters: [main] [cn.com.javakf.mapper.UserMapper.selectList]-[DEBUG] <== Total: 6 [main] [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3ab6678b] User(id=1, userName=zhangsan, password=null, name=张三, age=18, mail=zhangsan@163.com, address=null) User(id=2, userName=lisi, password=null, name=李四, age=20, mail=lisi@163.com, address=null) User(id=3, userName=wangwu, password=null, name=王五, age=28, mail=wangwu@163.com, address=null) User(id=4, userName=zhaoliu, password=null, name=赵六, age=21, mail=zhaoliu@163.com, address=null) User(id=5, userName=sunqi, password=null, name=孙七, age=24, mail=sunqi@163.com, address=null) User(id=6, userName=caocao, password=null, name=曹操, age=301, mail=caocao@163.com, address=null)
在Mybatis-Plus中,更新操作有2种,一种是根据id更新,另一种是根据条件更新。
方法定义
/** * 根据 ID 修改 * * @param entity 实体对象 */ int updateById(@Param(Constants.ENTITY) T entity);
测试用例
@Test public void testUpdateById() { User user = new User(); user.setId(1L); // 条件,根据id更新 user.setAge(19); // 更新的字段 user.setPassword("666666"); int result = this.userMapper.updateById(user);// 根据id更新,更新不为null的字段 System.out.println("result => " + result); }
测试结果
[main] [cn.com.javakf.mapper.UserMapper.updateById]-[DEBUG] ==> Preparing: UPDATE tb_user SET password=?, age=? WHERE id=? [main] [cn.com.javakf.mapper.UserMapper.updateById]-[DEBUG] ==> Parameters: 666666(String), 19(Integer), 1(Long) [main] [cn.com.javakf.mapper.UserMapper.updateById]-[DEBUG] <== Updates: 1 [main] [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3964d79] result => 1
方法定义
/** * 根据 whereEntity 条件,更新记录 * * @param entity 实体对象 (set 条件值,可以为 null) * @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句) */ int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);
测试用例
@Test public void testUpdate() { User user = new User(); user.setAge(20); // 更新的字段 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); }
或者,通过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); }
测试结果
[main] [cn.com.javakf.mapper.UserMapper.update]-[DEBUG] ==> Preparing: UPDATE tb_user SET password=?, age=? WHERE user_name = ? [main] [cn.com.javakf.mapper.UserMapper.update]-[DEBUG] ==> Parameters: 8888888(String), 20(Integer), zhangsan(String) [main] [cn.com.javakf.mapper.UserMapper.update]-[DEBUG] <== Updates: 1 [main] [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@46a953cf] result => 1
[main] [cn.com.javakf.mapper.UserMapper.update]-[DEBUG] ==> Preparing: UPDATE tb_user SET age=?,password=? WHERE user_name = ? [main] [cn.com.javakf.mapper.UserMapper.update]-[DEBUG] ==> Parameters: 21(Integer), 999999(String), zhangsan(String) [main] [cn.com.javakf.mapper.UserMapper.update]-[DEBUG] <== Updates: 1 [main] [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@46a953cf] result => 1
均可达到更新的效果
方法定义
/** * 根据 ID 删除 * * @param id 主键ID */ int deleteById(Serializable id);
测试用例
@Test public void testDeleteById() { // 根据id删除数据 int result = this.userMapper.deleteById(6L); System.out.println("result => " + result); }
测试结果
[main] [cn.com.javakf.mapper.UserMapper.deleteById]-[DEBUG] ==> Preparing: DELETE FROM tb_user WHERE id=? [main] [cn.com.javakf.mapper.UserMapper.deleteById]-[DEBUG] ==> Parameters: 6(Long) [main] [cn.com.javakf.mapper.UserMapper.deleteById]-[DEBUG] <== Updates: 1 [main] [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1abfe081] result => 1
方法定义
/** * 根据 columnMap 条件,删除记录 * * @param columnMap 表字段 map 对象 */ int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
测试用例
@Test public void testDeleteByMap() { Map<String, Object> map = new HashMap<>(); map.put("user_name", "zhangsan"); map.put("password", "999999"); // 根据map删除数据,多条件之间是and关系 int result = this.userMapper.deleteByMap(map); System.out.println("result => " + result); }
测试结果
[main] [cn.com.javakf.mapper.UserMapper.deleteByMap]-[DEBUG] ==> Preparing: DELETE FROM tb_user WHERE password = ? AND user_name = ? [main] [cn.com.javakf.mapper.UserMapper.deleteByMap]-[DEBUG] ==> Parameters: 999999(String), zhangsan(String) [main] [cn.com.javakf.mapper.UserMapper.deleteByMap]-[DEBUG] <== Updates: 1 [main] [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@27b2faa6] result => 1
方法定义
/** * 根据 entity 条件,删除记录 * * @param wrapper 实体对象封装操作类(可以为 null) */ int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
测试用例
@Test public void testDelete() { // 用法一: // QueryWrapper
wrapper = new QueryWrapper<>(); // wrapper.eq("user_name", "caocao").eq("password", "123456"); // 用法二: User user = new User(); user.setPassword("123456"); user.setUserName("caocao"); QueryWrapper<User> wrapper = new QueryWrapper<>(user); // 根据包装条件做删除 int result = this.userMapper.delete(wrapper); System.out.println("result => " + result); }测试结果
[main] [cn.com.javakf.mapper.UserMapper.delete]-[DEBUG] ==> Preparing: DELETE FROM tb_user WHERE user_name=? AND password=? [main] [cn.com.javakf.mapper.UserMapper.delete]-[DEBUG] ==> Parameters: caocao(String), 123456(String) [main] [cn.com.javakf.mapper.UserMapper.delete]-[DEBUG] <== Updates: 0 [main] [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4492eede] result => 0
方法定义
/** * 删除(根据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(10L, 11L)); System.out.println("result => " + result); }
测试结果
[main] [cn.com.javakf.mapper.UserMapper.deleteBatchIds]-[DEBUG] ==> Preparing: DELETE FROM tb_user WHERE id IN ( ? , ? ) [main] [cn.com.javakf.mapper.UserMapper.deleteBatchIds]-[DEBUG] ==> Parameters: 10(Long), 11(Long) [main] [cn.com.javakf.mapper.UserMapper.deleteBatchIds]-[DEBUG] <== Updates: 0 [main] [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7397c6] result => 0
Mybatis-Plus提供了多种查询操作,包括根据id查询、批量查询、查询单条数据、查询列表、分页查询等操作。
方法定义
/** * 根据 ID 查询 * * @param id 主键ID */ T selectById(Serializable id);
测试用例
@Test public void testSelectById() { // 根据id查询数据 User user = this.userMapper.selectById(2L); System.out.println(user); }
测试结果
[main] [cn.com.javakf.mapper.UserMapper.selectById]-[DEBUG] ==> Preparing: SELECT id,user_name,name,age,email AS mail FROM tb_user WHERE id=? [main] [cn.com.javakf.mapper.UserMapper.selectById]-[DEBUG] ==> Parameters: 2(Long) [main] [cn.com.javakf.mapper.UserMapper.selectById]-[DEBUG] <== Total: 1 [main] [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@c2e3264] User(id=2, userName=lisi, password=null, name=李四, age=20, mail=lisi@163.com, address=null)
方法定义
/** * 查询(根据ID 批量查询) * * @param idList 主键ID列表(不能为 null 以及 empty) */ List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
测试用例
@Test public void testSelectBatchIds() { // 根据id批量查询数据 List<User> users = this.userMapper.selectBatchIds(Arrays.asList(2L, 3L, 4L, 100L)); for (User user : users) { System.out.println(user); } }
测试结果
[main] [cn.com.javakf.mapper.UserMapper.selectBatchIds]-[DEBUG] ==> Preparing: SELECT id,user_name,name,age,email AS mail FROM tb_user WHERE id IN ( ? , ? , ? , ? ) [main] [cn.com.javakf.mapper.UserMapper.selectBatchIds]-[DEBUG] ==> Parameters: 2(Long), 3(Long), 4(Long), 100(Long) [main] [cn.com.javakf.mapper.UserMapper.selectBatchIds]-[DEBUG] <== Total: 3 [main] [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1d540566] User(id=2, userName=lisi, password=null, name=李四, age=20, mail=lisi@163.com, address=null) User(id=3, userName=wangwu, password=null, name=王五, age=28, mail=wangwu@163.com, address=null) User(id=4, userName=zhaoliu, password=null, name=赵六, age=21, mail=zhaoliu@163.com, address=null)
方法定义
/** * 根据 entity 条件,查询一条记录 * * @param queryWrapper 实体对象封装操作类(可以为 null) */ T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
测试用例
@Test public void testSelectOne() { QueryWrapper<User> wrapper = new QueryWrapper<>(); // 查询条件 wrapper.eq("name", "李四"); // wrapper.eq("password", "123456"); // 查询的数据超过一条时,会抛出异常 User user = this.userMapper.selectOne(wrapper); System.out.println(user); }
测试结果
[main] [cn.com.javakf.mapper.UserMapper.selectOne]-[DEBUG] ==> Preparing: SELECT id,user_name,name,age,email AS mail FROM tb_user WHERE name = ? [main] [cn.com.javakf.mapper.UserMapper.selectOne]-[DEBUG] ==> Parameters: 李四(String) [main] [cn.com.javakf.mapper.UserMapper.selectOne]-[DEBUG] <== Total: 1 [main] [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@cf67838] User(id=2, userName=lisi, password=null, name=李四, age=20, mail=lisi@163.com, address=null)
方法定义
/** * 根据 Wrapper 条件,查询总记录数 * * @param queryWrapper 实体对象封装操作类(可以为 null) */ Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
测试用例
@Test public void testSelectCount() { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.gt("age", 20); // 条件:年龄大于20岁的用户 // 根据条件查询数据条数 Integer count = this.userMapper.selectCount(wrapper); System.out.println("count => " + count); }
测试结果
[main] [cn.com.javakf.mapper.UserMapper.selectCount]-[DEBUG] ==> Preparing: SELECT COUNT( 1 ) FROM tb_user WHERE age > ? [main] [cn.com.javakf.mapper.UserMapper.selectCount]-[DEBUG] ==> Parameters: 20(Integer) [main] [cn.com.javakf.mapper.UserMapper.selectCount]-[DEBUG] <== Total: 1 [main] [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7942a854] count => 3
方法定义
/** * 根据 entity 条件,查询全部记录 * * @param queryWrapper 实体对象封装操作类(可以为 null) */ List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
测试用例
@Test public void testSelectList() { QueryWrapper<User> wrapper = new QueryWrapper<>(); // 设置查询条件 wrapper.like("email", "163"); List<User> users = this.userMapper.selectList(wrapper); for (User user : users) { System.out.println(user); } }
测试结果
[main] [cn.com.javakf.mapper.UserMapper.selectList]-[DEBUG] ==> Preparing: SELECT id,user_name,name,age,email AS mail FROM tb_user WHERE email LIKE ? [main] [cn.com.javakf.mapper.UserMapper.selectList]-[DEBUG] ==> Parameters: %163%(String) [main] [cn.com.javakf.mapper.UserMapper.selectList]-[DEBUG] <== Total: 4 [main] [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@341672e] User(id=2, userName=lisi, password=null, name=李四, age=20, mail=lisi@163.com, address=null) User(id=3, userName=wangwu, password=null, name=王五, age=28, mail=wangwu@163.com, address=null) User(id=4, userName=zhaoliu, password=null, name=赵六, age=21, mail=zhaoliu@163.com, address=null) User(id=5, userName=sunqi, password=null, name=孙七, age=24, mail=sunqi@163.com, address=null)
方法定义
/** * 根据 entity 条件,查询全部记录(并翻页) * * @param page 分页查询条件(可以为 RowBounds.DEFAULT) * @param queryWrapper 实体对象封装操作类(可以为 null) */ IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
配置分页插件
@Configuration @MapperScan("cn.com.javakf.mapper") // 设置mapper接口的扫描包 public class MybatisPlusConfig { @Bean // 配置分页插件 public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); } }
测试用例
// 测试分页查询 @Test public void testSelectPage() { Page<User> page = new Page<>(1, 1); // 查询第一页,查询1条数据 QueryWrapper<User> wrapper = new QueryWrapper<>(); // 设置查询条件 wrapper.like("email", "163"); 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); } }
测试结果
[main] [cn.com.javakf.mapper.UserMapper.selectPage]-[DEBUG] ==> Preparing: SELECT COUNT(1) FROM tb_user WHERE email LIKE ? [main] [cn.com.javakf.mapper.UserMapper.selectPage]-[DEBUG] ==> Parameters: %163%(String) [main] [cn.com.javakf.mapper.UserMapper.selectPage]-[DEBUG] ==> Preparing: SELECT id,user_name,name,age,email AS mail FROM tb_user WHERE email LIKE ? LIMIT ?,? [main] [cn.com.javakf.mapper.UserMapper.selectPage]-[DEBUG] ==> Parameters: %163%(String), 0(Long), 1(Long) [main] [cn.com.javakf.mapper.UserMapper.selectPage]-[DEBUG] <== Total: 1 [main] [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@59e43e8c] 数据总条数: 4 数据总页数: 4 当前页数: 1 User(id=2, userName=lisi, password=null, name=李四, age=20, mail=lisi@163.com, address=null)
Mybatis-Plus在启动后会将BaseMapper中的一系列的方法注册到meppedStatements中,那么究竟是如何注入的呢?流程又是怎么样的?
在Mybatis-Plus中,ISqlInjector负责SQL的注入工作,它是一个接口,AbstractSqlInjector是它的实现类,实现关系如下:
在AbstractSqlInjector中,主要是由inspectInject()方法进行注入的,如下:@Override public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) { Class<?> modelClass = extractModelClass(mapperClass); if (modelClass != null) { String className = mapperClass.toString(); Set<String> mapperRegistryCache = GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration()); if (!mapperRegistryCache.contains(className)) { List<AbstractMethod> methodList = this.getMethodList(); if (CollectionUtils.isNotEmpty(methodList)) { TableInfo tableInfo = TableInfoHelper.initTableInfo(builderAssistant, modelClass); // 循环注入自定义方法 methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo)); } else { logger.debug(mapperClass.toString() + ", No effective injection method was found."); } mapperRegistryCache.add(className); } } }
在实现方法中, methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass,tableInfo)); 是关键,循环遍历方法,进行注入。
最终调用抽象方法injectMappedStatement进行真正的注入:/** * 注入自定义 MappedStatement * * @param mapperClass mapper 接口 * @param modelClass mapper 泛型 * @param tableInfo 数据库表反射信息 * @return MappedStatement */ public abstract MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo);
public class SelectById extends AbstractMethod { @Override public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) { SqlMethod sqlMethod = SqlMethod.LOGIC_SELECT_BY_ID; SqlSource sqlSource = new RawSqlSource(configuration, String.format(sqlMethod.getSql(), sqlSelectColumns(tableInfo, false), tableInfo.getTableName(), tableInfo.getKeyColumn(), tableInfo.getKeyProperty(), tableInfo.getLogicDeleteSql(true, false)), Object.class); return this.addSelectMappedStatement(mapperClass, sqlMethod.getMethod(), sqlSource, modelClass, tableInfo); } }
可以看到,生成了SqlSource对象,再将SQL通过addSelectMappedStatement方法添加到meppedStatements中。