MybatisPlus 通用CRUD操作
1、插入操作
1.1、方法定义
/** * 插⼊⼀条记录 * * @param entity 实体对象. */ int insert(T entity);
1.2、测试用例
/* 测试添加 */ @Test public void testInsert(){ User user = new User(); user.setName("应颠22"); user.setAge(20); user.setMail("zimu@lagou.com"); // 返回值是影响的行数 int result = userMapper.insert(user); System.out.println(result); System.out.println("id值为" + user.getId()); // 这里我们没有设置id,但是user中却能获取值,是因为MybatisPlus自动帮我们生成的 }
2、更新操作
在MP中,更新操作有2种,⼀种是根据id更新,另⼀种是根据条件更新。
2.1、根据id更新
方法定义
/** * 根据 ID 修改 * * @param entity 实体对象 */ int updateById(@Param(Constants.ENTITY) T entity);
测试:
/* 测试根据ID进行修改 */ @Test public void testUpateById(){ User user = new User(); user.setId(6L); user.setAge(30);//更新的字段 //根据id更新,更新不为null的字段 int i = userMapper.updateById(user); System.out.println(i); }
2.2、根据条件更新
方法定义
/** * 根据 whereEntity 条件,更新记录 * * @param entity 实体对象 (set 条件值,可以为 null) * @param updateWrapper 实体对象封装操作类(可以为 null,⾥⾯的 entity ⽤于⽣成where 语句) */ int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) WrapperupdateWrapper);
测试用例
/* 测试根据条件进行修改 */ @Test public void testUpate(){ // 1. 更新的字段 User user = new User(); user.setAge(35); // 2.更新的条件 QueryWrapperqueryWrapper = new QueryWrapper<>(); queryWrapper.eq("name","子慕"); int i = userMapper.update(user,queryWrapper); System.out.println(i); } // 通过UpdateWrapper进⾏更新 @Test public void testUpate2(){ UpdateWrapper updateWrapper = new UpdateWrapper<>(); updateWrapper.eq("id",6).set("age",40); int i = userMapper.update(null,updateWrapper); System.out.println(i); }
3、删除操作
3.1、deleteById
方法定义
/** * 根据 ID 删除 * * @param id 主键ID */ int deleteById(Serializable id);
测试用例
/* 根据ID进行删除 */ @Test public void testDeleteById(){ int i = userMapper.deleteById(2L); System.out.println(i); }
3.2、deleteByMap
方法定义:
/** * 根据 columnMap 条件,删除记录 * * @param columnMap 表字段 map 对象 */ int deleteByMap(@Param(Constants.COLUMN_MAP) MapcolumnMap);
测试用例
/* 根据columnMap进行删除 */ @Test public void testDeleteByMap(){ HashMapmap = new HashMap<>(); map.put("name","子慕"); map.put("age",18); // 将columnMap中的元素设置为删除的条件,多个条件是and的关系 int i = userMapper.deleteByMap(map); System.out.println(i); }
3.3、delete
方法定义
/** * 根据 entity 条件,删除记录 * * @param wrapper 实体对象封装操作类(可以为 null) */ int delete(@Param(Constants.WRAPPER) Wrapperwrapper);
测试用例
/* 调用delete进行删除 */ @Test public void testDelete(){ User user = new User(); user.setName("子慕2"); user.setAge(18); QueryWrapperqueryWrapper = new QueryWrapper<>(user); // queryWrapper.eq("name","子慕1").eq("age",18); int i = quserMapper.delete(queryWrapper); System.out.println(i); }
3.4、deleteBatchIds
方法定义
/** * 删除(根据ID 批量删除) * * @param idList 主键ID列表(不能为 null 以及 empty) */ int deleteBatchIds(@Param(Constants.COLLECTION) Collection extends Serializable> idList);
测试用例
/* 调用deleteBatchIds进行批量删除 */ @Test public void testDeleteBatchIds(){ int i = userMapper.deleteBatchIds(Arrays.asList(10l,11l)); System.out.println(i); }
4、查询操作
MP提供了多种查询操作,包括根据id查询、批量查询、查询单条数据、查询列表、分⻚查询等操作。
4.1、selectById
方法定义:
/** * 根据 ID 查询 * * @param id 主键ID */ T selectById(Serializable id);
测试用例
/* 根据ID进行查询 */ @Test public void testSelectById(){ User user = userMapper.selectById(2L); System.out.println(user); }
4.2、selectBatchIds
方法定义
/** * 查询(根据ID 批量查询) * * @param idList 主键ID列表(不能为 null 以及 empty) */ ListselectBatchIds(@Param(Constants.COLLECTION) Collection extends Serializable> idList);
测试用例
/* 根据ID进行批量查询 */ @Test public void testSelectBatchIds(){ Listusers = userMapper.selectBatchIds(Arrays.asList(12L, 13l)); for (User user : users) { System.out.println(user); } }
4.3、selectOne
方法定义
/** * 根据 entity 条件,查询⼀条记录 * * @param queryWrapper 实体对象封装操作类(可以为 null) */ T selectOne(@Param(Constants.WRAPPER) WrapperqueryWrapper);
测试用例
/* 测试selectOne */ @Test public void testSelectOne(){ QueryWrapperqueryWrapper = new QueryWrapper<>(); queryWrapper.eq("name","子慕"); // 根据条件查询一条记录,如果查询结果超过一条,会报错 User user = userMapper.selectOne(queryWrapper); System.out.println(user); }
4.4、selectCount
方法定义
/** * 根据 Wrapper 条件,查询总记录数 * * @param queryWrapper 实体对象封装操作类(可以为 null) */ Integer selectCount(@Param(Constants.WRAPPER) WrapperqueryWrapper);
测试用例
/* 根据wrapper条件,查询总记录数 */ @Test public void testSelectCount(){ QueryWrapperqueryWrapper = new QueryWrapper<>(); queryWrapper.gt("age",18); // 查询年龄大于18的 // 根据条件查询一条记录,如果查询结果超过一条,会报错 Integer count = userMapper.selectCount(queryWrapper); List users = userMapper.selectList(queryWrapper); System.out.println(count); for (User user : users) { System.out.println(user); } }
4.5、selectList
方法定义
/** * 根据 entity 条件,查询全部记录 * * @param queryWrapper 实体对象封装操作类(可以为 null) */ ListselectList(@Param(Constants.WRAPPER) Wrapper queryWrapper);
测试用例
@Test public void testSelectList() { QueryWrapperwrapper = new QueryWrapper (); wrapper.gt("age", 23); //年龄⼤于23岁 //根据条件查询数据 List users = this.userMapper.selectList(wrapper); for (User user : users) { System.out.println("user = " + user); } }
4.6、selectPage
方法定义
/** * 根据 entity 条件,查询全部记录(并翻⻚) * * @param page 分⻚查询条件(可以为 RowBounds.DEFAULT) * @param queryWrapper 实体对象封装操作类(可以为 null) */ IPageselectPage(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper);
配置分页插件:
@Configuration @MapperScan("com.lagou.mp.mapper") //设置mapper接⼝的扫描包 public class MybatisPlusConfig { /** * 分⻚插件 */ @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); } }
测试用例
/* 分页查询 */ @Test public void testSelectPage(){ QueryWrapperqueryWrapper = new QueryWrapper<>(); queryWrapper.gt("age",18); // 查询年龄大于18的 // 第一个参数:当前页 第二个参数:每页显示条数 Page page = new Page<>(2, 2); IPage userIPage = userMapper.selectPage(page, queryWrapper); System.out.println("总条数" + userIPage.getTotal()); System.out.println("总页数" + userIPage.getPages()); System.out.println("分页数据" + userIPage.getRecords()); }
5、SQL注入的原理
前⾯我们已经知道,MP在启动后会将BaseMapper中的⼀系列的⽅法注册到meppedStatements中,那么究竟是如何注⼊的呢?流程⼜是怎么样的?下⾯我们将⼀起来分析下。
在MP中,ISqlInjector负责SQL的注⼊⼯作,它是⼀个接⼝,AbstractSqlInjector是它的实现类,实现关系如下:
在AbstractSqlInjector中,主要是由inspectInject()⽅法进⾏注⼊的,如下:
@Override public void inspectInject(MapperBuilderAssistant builderAssistant, Class> mapperClass) { Class> modelClass = extractModelClass(mapperClass); if (modelClass != null) { String className = mapperClass.toString(); SetmapperRegistryCache = GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration()); if (!mapperRegistryCache.contains(className)) { List 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);
查看该⽅法的实现:
以SelectById为例查看:
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中。