Mybatis-Plus:通用CRUD操作

Posted 丿涛哥哥

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mybatis-Plus:通用CRUD操作相关的知识,希望对你有一定的参考价值。

Mybatis-Plus通⽤CRUD

通过继承BaseMapper来获取到各种各样的单表操作,下面通过案例来演示这些操作。

1、插⼊操作

⽅法定义

 /**
 * 插⼊⼀条记录
 *
 * @param entity 实体对象.
 */
 int insert(T entity);

测试⽤例

@RunWith(SpringRunner.class)
@SpringBootTest
class MybatisPlusSpringbootApplicationTests 

    @Autowired
    private UserMapper userMapper;

    /*
        测试添加
     */
    @Test
    public void testInsert()
        User user = new User();
        user.setName("小七");
        user.setAge("18");
        user.setEmail("xiaoqi@163.com");

        //返回值是数据库中影响的行数
        int result = userMapper.insert(user);
        System.out.println(result);
        System.out.println("id值为:" + user.getId());

    

测试结果:

1
id值为:1548196585471504386

可以看到,数据已经写⼊到了数据库,但是id的值是不正确的,或者说不是我们想要的,我们期望的是数据库⾃增⻓,实际是MP⽣成了id的值写⼊到了数据库。

如何设置id的⽣成策略呢?

MP⽀持的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
	* <p>该类型可以通过⾃⼰注册⾃动填充插件进⾏填充</p>
	*/
	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
public class User 
    @TableId(type = IdType.AUTO) //指定Id的生成策略为自增长
    private Long id;
    private String name;
    private String age;
    private String email;

再次执行(数据库表支持自动增长),插入成功:

@TableField

在MP中通过@TableField注解可以指定字段的⼀些属性,常常解决的问题有2个:

  1. 对象中的属性名和字段名不⼀致的问题(⾮驼峰)
  2. 对象中的属性字段在表中不存在的问题

使⽤:

其他⽤法,如数据库表中的某一个字段不加⼊查询字段

演示效果:

User(id=1, name=null, age=18, mail=test1@baomidou.com, address=null)
User(id=2, name=null, age=20, mail=test2@baomidou.com, address=null)
User(id=3, name=null, age=28, mail=test3@baomidou.com, address=null)
User(id=4, name=null, age=21, mail=test4@baomidou.com, address=null)
User(id=5, name=null, age=24, mail=test5@baomidou.com, address=null)

2、更新操作

在MP中,更新操作有2种,⼀种是根据id更新,另⼀种是根据条件更新。

2.1、根据id更新

⽅法定义:

/**
 * 根据 ID 修改
 *
 * @param entity 实体对象
 */
 int updateById(@Param(Constants.ENTITY) T entity);

编写测试方法:

/*
    测试根据ID进行修改
 */
@Test
public void testUpdateById()
    User user = new User();
    user.setId(6L);
    user.setAge("30");

    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 testUpdate()
    //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 testUpdate2()
    UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
    updateWrapper.eq("id",6).set("age",40);

    int i = userMapper.update(null,updateWrapper);
    System.out.println(i);

测试结果:

[main] [com.tao.mapper.UserMapper.update]-[DEBUG] ==> Preparing: UPDATE _user SET age=? WHERE id = ?
[main] [com.tao.mapper.UserMapper.update]-[DEBUG] ==> Parameters: 40(String),6(Integer)
[main] [com.tao.mapper.UserMapper.update]-[DEBUG] <== Updates: 1

两种方式均可达到更新的效果。


3、删除操作

3.1、deleteById

⽅法定义:

/**
* 根据 ID 删除
*
* @param id 主键ID
*/
int deleteById(Serializable id);

测试⽤例:

/*
    根据Id进行删除
 */
@Test
public void testDeleteById()
    int i = userMapper.deleteById(6L);
    System.out.println(i);

结果:

[main] [com.tao.mmapper.UserMapper.deleteById]-[DEBUG] ==> Preparing: DELETE FROM user WHERE id=?
[main] [com.tao.mapper.UserMapper.deleteById]-[DEBUG] ==> Parameters: 6(Long)
[main] [com.tao.mapper.UserMapper.deleteById]-[DEBUG] <== Updates: 1

数据被删除。


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);

结果:

[main] [com.tao.mapper.UserMapper.deleteByMap]-[DEBUG] ==> Preparing: DELETE FROM user WHERE name = ? AND age = ?
[main] [com.tao.mapper.UserMapper.deleteByMap]-[DEBUG] ==> Parameters: 小七(String), 18(Integer)
[main] [com.tao.mapper.UserMapper.deleteByMap]-[DEBUG] <== Updates: 0

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("小七1");
    user.setAge("18");
    //将实体对象进行包装,包装为操作对象
    QueryWrapper<User> queryWrapper = new QueryWrapper<>(user);
    //queryWrapper.eq("name","小七1").eq("age",18);

    int i = userMapper.delete(queryWrapper);
    System.out.println(i);

结果:

[main] [com.tao.mapper.UserMapper.delete]-[DEBUG] ==> Preparing: DELETE FROM user WHERE name=? AND age=?
[main] [com.tao.mapper.UserMapper.delete]-[DEBUG] ==> Parameters: 小七1(String),18(Integer)
[main] [com.tao.mapper.UserMapper.delete]-[DEBUG] <== Updates: 0

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);

结果:

[main] [com.tao.mapper.UserMapper.deleteBatchIds]-[DEBUG] ==> Preparing: DELETE FROM user WHERE id IN ( ? , ? )
[main] [com.tao.mapper.UserMapper.deleteBatchIds]-[DEBUG] ==> Parameters: 10(Long), 20(Long)
[main] [com.tao.mapper.UserMapper.deleteBatchIds]-[DEBUG] <== Updates: 2

4、查询操作

MP提供了多种查询操作,包括根据id查询、批量查询、查询单条数据、查询列表、分⻚查询等操作。

4.1、selectById

⽅法定义:

/**
 * 根据 ID 查询
 *
 * @param id 主键ID
 */
 T selectById(Serializable id);

测试⽤例:

/*
    根据Id进行查询
 */
@Test
public void testSelectById()
    User user = userMapper.selectById(12L);
    System.out.println(user);

结果:

User(id=12, name=小七, age=20, mail=test, address=null)

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(6L, 12L));
    for (User user : users) 
        System.out.println(user);
    

结果:

User(id=6, name=小七1, age=18, mail=xiaoqi@163.com, address=null)
User(id=12, name=小七, age=20, mail=test, address=null)

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);

结果:

User(id=12, name=小七, age=20, mail=test, address=null)

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);
    System.out.println(count);

结果:

5

4.5、selectList

⽅法定义:

/**
* 根据 entity 条件,查询全部记录
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

测试⽤例:

/*
    根据wrapper条件,查询数据
 */
@Test
public void testSelectList()
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.gt("age",18); //查询年龄大于18的

    //根据条件查询数据
    List<User> users = userMapper.selectList(queryWrapper);
    for (User user : users) 
        System.out.println(user);
    

结果:

User(id=2, name=Jack, age=20, mail=test2@baomidou.com, address=null)
User(id=3, name=Tom, age=28, mail=test3@baomidou.com, address=null)
User(id=4, name=Sandy, age=21, mail=test4@baomidou.com, address=null)
User(id=5, name=Billie, age=24, mail=test5@baomidou.com, address=null)
User(id=12, name=小七, age=20, mail=test, address=null)

4.6、selectPage

⽅法定义:

/**
* 根据 entity 条件,查询全部记录(并翻⻚)
*
* @param page 分⻚查询条件(可以为 RowBounds.DEFAULT)
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

配置分⻚插件:

@Configuration
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<>(1, 2);

    IPage<User> userIPage = userMapper.selectPage(page, queryWrapper);
    System.out.println("总条数:" + userIPage.getTotal());
    System.out.println("总页数:" + userIPage.getPages());
    System.out.println("分页数据:" + userIPage.getRecords());

结果:

总条数:5
总页数:3
分页数据:[User(id=2, name=Jack, age=20, mail=test2@baomidou.com, address=null), User(id=3, name=Tom, age=28, mail=test3@baomidou.com, address=null)]

5、SQL注⼊的原理

在MP中,ISqlInjector负责SQL的注⼊⼯作,它是⼀个接⼝,AbstractSqlInjector是它的实现类,实现关系如下:

在AbstractSqlInjector中,主要是由inspectInject()⽅法进⾏注⼊的,如下:

@Overried
public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) 
        Class<?> modelClass = this.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
 */
 publi

以上是关于Mybatis-Plus:通用CRUD操作的主要内容,如果未能解决你的问题,请参考以下文章

Mybatis-Plus:通用CRUD操作

mybatis-plus通用CRUD

光速入门MyBatis-Plus

MyBatis-Plus(狂神)

「Spring Boot架构」集成Mybatis-Plus的实例详解

Mybatis-Plus通用Mapper CRUD之insert(附带视频教程)