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) Wrapper<T> updateWrapper);
测试用例
/*
测试根据条件进行修改
*/
@Test
public void testUpate(){
// 1. 更新的字段
User user = new User();
user.setAge(35);
// 2.更新的条件
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name","子慕");
int i = userMapper.update(user,queryWrapper);
System.out.println(i);
}
// 通过UpdateWrapper进⾏更新
@Test
public void testUpate2(){
UpdateWrapper<User> 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) Map<String, Object> columnMap);
测试用例
/*
根据columnMap进行删除
*/
@Test
public void testDeleteByMap(){
HashMap<String, Object> map = 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) Wrapper<T> wrapper);
测试用例
/*
调用delete进行删除
*/
@Test
public void testDelete(){
User user = new User();
user.setName("子慕2");
user.setAge(18);
QueryWrapper<User> queryWrapper = 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)
*/
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
测试用例
/*
根据ID进行批量查询
*/
@Test
public void testSelectBatchIds(){
List<User> users = 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) Wrapper<T> queryWrapper);
测试用例
/*
测试selectOne
*/
@Test
public void testSelectOne(){
QueryWrapper<User> queryWrapper = 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) Wrapper<T> queryWrapper);
测试用例
/*
根据wrapper条件,查询总记录数
*/
@Test
public void testSelectCount(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.gt("age",18); // 查询年龄大于18的
// 根据条件查询一条记录,如果查询结果超过一条,会报错
Integer count = userMapper.selectCount(queryWrapper);
List<User> users = userMapper.selectList(queryWrapper);
System.out.println(count);
for (User user : users) {
System.out.println(user);
}
}
4.5、selectList
方法定义
/**
* 根据 entity 条件,查询全部记录
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
测试用例
@Test
public void testSelectList() {
QueryWrapper<User> wrapper = new QueryWrapper<User>();
wrapper.gt("age", 23); //年龄⼤于23岁
//根据条件查询数据
List<User> 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)
*/
IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
配置分页插件:
@Configuration
@MapperScan("com.lagou.mp.mapper") //设置mapper接⼝的扫描包
public class MybatisPlusConfig {
/**
* 分⻚插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
测试用例
/*
分页查询
*/
@Test
public void testSelectPage(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.gt("age",18); // 查询年龄大于18的
// 第一个参数:当前页 第二个参数:每页显示条数
Page<User> page = new Page<>(2, 2);
IPage<User> 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();
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);
查看该⽅法的实现:
以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中。