Mybatis-plus
概况
Mybatis-plus 是在mybatis的基础上进行开发,简化Mybatis的一些操作,提高开发效率,并支持任意mybatis支持的数据库。
优点(相对于mybatis)
-
简化了CRUD 操作 sql 语句,通过反射分析类名,扫描字段来拼接 sql 语句,不用我们写,就可以进行简单增删改查。
-
内置代码生成器,分页插件,性能分析插件等
实现原理
继承BaseMapper并在后面的泛型中添加实体类,让BaseMapper根据你的实体类,通过反射获取实体类名,默认把实体类名当作表名,而把内部的属性默认当成列名,根据你调用的方法,判断对数据库的操作是什么,来进行数据库操作。
如:你的EmployeeMapper接口继承了BaseMapper,并在泛型中写把 Employee 实体类,通过反射获取 类名 employee 当表名,字段当列名 ,调用的selectList 方法判断你是进行查询操作,就可以拼接sql语句进行查询。
select 列名 from 表名
快速开发
添加依赖
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.0</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.17</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.22</version> </dependency>
继承BaseMapper
public interface EmployeeMapper extends BaseMapper<Employee> {}
实体类
@Data public class Employee { private Long id; private String username; private String name; private String password; private String email; private Long age; private boolean admin; private Long deptId; }
配置文件
#mysql spring.datasource.url=jdbc:mysql://localhost:3306/car?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC spring.datasource.username=rootproperties spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver # 配置slq打印日志 logging.level.cn.wolfcode.mp.mapper=debug
还有app配置类
@SpringBootApplication @MapperScan(basePackages = "cn.wolfcode.mapper") public class app { public static void main(String[] args) { SpringApplication.run(app.class,args); } }
测试 (测试类要添加依赖 spring-boot-starter-test)
@SpringBootTest public class TestMybatisPlus { @Autowired private EmployeeMapper employeeMapper; @Test public void test(){ List<Employee> list = employeeMapper.selectList(null); for (Employee employee : list) { System.out.println(employee); } } }
注意:
如果是使用 insert 方法,id默认是用内部的雪花算法,计算出唯一 id ,而不是数据库自动增长 id ,要自动增长 id 需要使@TableId 自己设置。
相关注解
概况
Mybatis-plus 是根据实体来拼接 sql 语句,来达到无 sql 操作,为了避免表名和实体类名不一样、字段名和数据库列名不一样,而通过一些相关注解来解决相关问题。
注解与相关作用
-
@TableName:指定当前实体类映射哪张表数据库表,默认是跟实体类名一样,贴在实体类上。
-
@TableField:指定当前属性映射数据库表哪一列,默认跟属性名一样,value是指定数据库名,exist指定是否当查询列名,如果是false,不查询该列。
-
@TableId:标记当前属性映射表主键, IdType 指定当前 id 是自动增长还是使用默认的雪花算法 id 等其他值。exist 表示是否为是数据库字段。
使用BaseMapper
日志使用
mybatis使用的日志格式是
logging.level.cn.wolfcode.mp.mapper=debug
而mybatis-plus使用的日志格式是
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
这两者是有区别的,一个使用mybatis内置得日志,在springboot中输出在信息初始化列中,不方便看。mybatis-plus是直接输出在控制台中,方便看。
插入数据Id异常
数据插入时,默认是使用雪花算法,计算出唯一字符串,当 id 插入数据库。
解决方法
使用@TableId解决,就是把默认的雪花算法关闭,type 指定主键生成的方式。
使用 insert(Object entity) 方法
@Test public void insert(){ Employee employee = new Employee(); employee.setUsername("f"); employee.setName("f"); employee.setPassword("f"); employeeMapper.insert(employee); }
实体类中有不是不属于 列 的属性报错
使用exist 表示是否为是数据库字段,默认是true,如果指定为false,则不会在查询的时候添加到 sql 语句中。
修改数据错误
参数实体属性值为null,不参与 sql 拼接,如果参数实体属性类型是8大基本类型,有默认值,mybatis-plus认为有属性值,就参与sql拼接,会引起参数丢失问题。
解决方案
-
把基本类型改成包装类型。
-
先查询,再修改,后更新。
-
使用update(null,wrapper)方法操作,部分字段更新方法。
updateById(Object entity)
//sql语句 UPDATE employee SET username=?, name=?, password=?, admin=? WHERE id=? //因为admin是基本 boolean 类型有值。 @Test public void updateById(){ Employee employee = new Employee(); employee.setId(1408412759464095748L); employee.setUsername("cc"); employee.setName("cc"); employee.setPassword("c"); employeeMapper.updateById(employee); }
update(null,wrapper)的使用
@Test public void update(){ /* //普通的条件构造器 UpdateWrapper<Employee> updateWrapper = new UpdateWrapper(); //拼接条件语句 如: where name = ? updateWrapper.eq("id",1408412759464095748L); //修改列 updateWrapper.set("name","sdfsd"); //sql 语句: UPDATE employee SET name=? WHERE (id = ?) */ //支持lambda的 条件构造器 LambdaUpdateWrapper<Employee> lambdaUpdateWrapper = new LambdaUpdateWrapper(); //使用 Lambda 表达式 注入sql语句 lambdaUpdateWrapper.eq(Employee::getId,"1408412759464095748L"); lambdaUpdateWrapper.set(Employee::getName,"fdf"); // UPDATE employee SET name=? WHERE (id = ?) employeeMapper.update(null,lambdaUpdateWrapper); }
updateById 和 update 怎么选择?
在sql 的 where 条件为 id 时 和 全部字段更新时,使用 updateById。
在sql 的 where 条件不一定为 id 时,和部分字段更新时,使用 update。
delete方法
-
deleteById(Long id) :
//删除 id 为 1408412759464095748L 数据 //DELETE FROM employee WHERE id=? employeeMapper.deleteById("1408412759464095748L");
-
deleteBatchId(List list) sql 语句
//删除 id 为 list 中的值 //DELETE FROM employee WHERE id IN ( ? , ? ) employeeMapper.deleteBatchIds(Arrays.asList(1408412759464095747L,35L));
-
delete(Wrapper wrapper) sql 语句
//删除符合 wrapper 中的条件的数据 //DELETE FROM employee WHERE (id = ? OR username = ?) QueryWrapper<Employee> queryWrapper = new QueryWrapper(); queryWrapper.eq("id",33L).or().eq("username","a"); employeeMapper.delete(queryWrapper);
-
deleteByMap(Map map) sql 语句
//删除条件为 map 中的 数据 // DELETE FROM employee WHERE name = ? HashMap<String, Object> hashMap = new HashMap<>(); hashMap.put("name","l"); employeeMapper.deleteByMap(hashMap);
注意:只进行 id 条件的删除请用 deleteById 或 deleteBatchId。
select方法
-
selectById(Long id) 根据 id 查询
// SELECT id,username,name,password,email,age,admin,dept_id FROM employee WHERE id=? employeeMapper.selectById(9L);
-
selectBatchIds(List list) 查询 list 的 id 。
/*SELECT id,username,name,password,email,age,admin,dept_id FROM employee WHERE id IN ( ? , ? )*/ employeeMapper.selectBatchIds(Arrays.asList(10L,11L));
-
selectByMap(Map map) 查询条件为 map 中的数据,全部默认为 and 查询,有条件为 or 不建议使用
/* SELECT id,username,name,password,email,age,admin,dept_id FROM employee WHERE name = ? AND username = ? */ HashMap<String, Object> hashMap = new HashMap<>(); hashMap.put("username","菠萝吹雪"); hashMap.put("name","王五"); employeeMapper.selectByMap(hashMap);
-
selectCount(Wrapper wrapper) 查询条件password 为 ?并 id 存在的数量,返回值为 Integer 类型。
//SELECT COUNT(id) FROM employee WHERE (password = ?) QueryWrapper queryWrapper = new QueryWrapper(); queryWrapper.eq("password","c4ca4238a0b923820dcc509a6f75849b"); queryWrapper.select("id"); employeeMapper.selectCount(queryWrapper);
-
selectList(Wrapper wrapper) 查询wrapper 中条件的所有数据,如果要自己设置查询列,使用wrapper.select(String column)
/*SELECT id,username,name,password,email,age,admin,dept_id FROM employee WHERE (admin = ?)*/ QueryWrapper queryWrapper = new QueryWrapper(); queryWrapper.eq("admin",false); employeeMapper.selectList(queryWrapper);
-
selectMaps(Wrapper wrapper) 这查询的返回值是List<泛型>,泛型一般使用 Map ,可以装一些和实体无关的属性。
// SELECT id,username FROM employee QueryWrapper queryWrapper = new QueryWrapper(); queryWrapper.select("id","username"); List<Map<String,Object>> list = employeeMapper.selectMaps(queryWrapper);
-
selectPage(Wrapper wrapper) 使用分页查询首先去配置类中,配置启动分页插件bean
启动分页插件
@Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL); paginationInnerInterceptor.setOverflow(true); //合理化 interceptor.addInnerInterceptor(paginationInnerInterceptor); return interceptor; }
使用分页查询
QueryWrapper queryWrapper = new QueryWrapper(); IPage<Employee> page = new Page<>(1,5); queryWrapper.select("id","username","name"); employeeMapper.selectPage(page,queryWrapper);
selectMaps和selectList的使用区别
selectMaps返回的是一个 List< Map > 集合,而selectList返回的是List< 对象 >集合,如果查询的数据能封装成 domain 使用 selectList ,不能复制成 domain 使用 selectMaps 。一般我们分组查询数量时,就使用 selectMaps,好处就是查询的数据不在实体类,并且我们不用添加额外的属性和创建一个新的类去封装数据。
条件构造器
条件构造器是 mybatis-plus 特有的,是用增强 mybatis 用的,可以帮我们编写 sql 语句。
继承体系图
我们使用的所有条件构造器都间接或直接继承了 AbstractWrapper 类,而继承 AbstractLambdaWrapper 这个类,就可以使用 Lambda 表达式来指定条件。
继承 AbstractWrapper 这个类有两种条件类型,一种是修改型子类,独立的实现方法有 set 和setSql,另一种是查询型子类,独立的实现方法有 select。
set 和 setSql的区别
set 是使用预编译来拼接 sql 语句,而 setSql 是直接拼接 sql 语句,使用 set 效率比 setSql 效率高,所以建议尽量使用 set。
注意: 使用查询 Wrapper 的 select(String column,...) 时,要直接把想显示的列全都安照顺序写好,注意,第二次调用 select 时会替换掉第一次的 select ;
建议
使用Lambda条件构造器来操作。
UpdateWrapper 和 LambdaUpdateWrapper 的使用
//普通的条件构造器 UpdateWrapper<Employee> updateWrapper = new UpdateWrapper(); //拼接条件语句 如: where name = ? updateWrapper.eq("id",1408412759464095748L); //修改列 update set bane='sefad' from employee updateWrapper.set("name","sdfsd"); employeeMapper.update(null,lambdaUpdateWrapper); //------------------------------------------------------ //支持lambda的 条件构造器 LambdaUpdateWrapper<Employee> lambdaUpdateWrapper = new LambdaUpdateWrapper(); //使用 Lambda 表达式 注入sql语句 lambdaUpdateWrapper.eq(Employee::getId,"1408412759464095748L"); lambdaUpdateWrapper.set(Employee::getName,"fdf"); // UPDATE employee SET name=? WHERE (id = ?) employeeMapper.update(null,lambdaUpdateWrapper);
QueryWrapper 和 LambdaQueryWrapper的使用
//普通的条件构造器 QueryWrapper<Employee> queryWrapper = new QueryWrapper(); //指定要显示出来的列 queryWrapper.select("id","username"); List<Map<String,Object>> list = employeeMapper.selectMaps(qeryWrapper); //------------------------------------ //支持lambda的 条件构造器 LambdaQueryWrapper<Employee> lambdaQueryWrapper = new LambdaQueryWrapper(); //指定根据排序列 lambdaQueryWrapper.orderByDesc(Employee::getId); //指定要显示出来的列 lambdaQueryWrapper.select(Employee::getId,Employee::getName,Employee::getPassword); List<Employee> employees = employeeMapper.selectList(lambdaQueryWrapper);
工具类 wrappers ,可以用来开始创建一个各种条件构造器。
高级查询
-
select:指定返回的列
-
排序
-
orderByAsc:指定某列进行顺序排序
-
orderByDesc :指定某列进行倒序排序
-
orderBy:根据条件指定谋列进行排序
使用
LambdaQueryWrapper<Employee> lambdaQueryWrapper = new LambdaQueryWrapper(); lambdaQueryWrapper.select(Employee::getId,Employee::getName,Employee::getPassword); lambdaQueryWrapper.orderByAsc(Employee::getId); List<Employee> employees = employeeMapper.selectList(lambdaQueryWrapper); employees.forEach(System.err::println); //清空lambdaQueryWrapper lambdaQueryWrapper.clear(); lambdaQueryWrapper.orderByDesc(Employee::getId); lambdaQueryWrapper.select(Employee::getId,Employee::getName,Employee::getPassword); List<Employee> employees2 = employeeMapper.selectList(lambdaQueryWrapper); employees2.forEach(System.err::println); //清空lambdaQueryWrapper lambdaQueryWrapper.clear(); // 按调用来进行先后排序 lambdaQueryWrapper.orderByAsc(Employee::getAge); lambdaQueryWrapper.orderByDesc(Employee::getId); lambdaQueryWrapper.select(Employee::getId,Employee::getName,Employee::getPassword); List<Employee> employees3 = employeeMapper.selectList(lambdaQueryWrapper); employees3.forEach(System.err::println); //清空lambdaQueryWrapper lambdaQueryWrapper.clear(); // condition (条件):允许排序, isAse :是否进行正排序 排序的列,可以多个 lambdaQueryWrapper.orderBy(true,false,Employee::getAge,Employee::getId); lambdaQueryWrapper.select(Employee::getId,Employee::getName,Employee::getPassword); List<Employee> employees4 = employeeMapper.selectList(lambdaQueryWrapper); employees4.forEach(System.err::println); // 排序的列为空的数据排在最上面,可以调用多个排序方法
-
-
分组查询
-
groupBy:根据谋列进行分组
-
having:显示符合该条件的分组
使用
LambdaQueryWrapper<Employee> lambdaQueryWrapper = new LambdaQueryWrapper(); lambdaQueryWrapper.select(Employee::getId,Employee::getName,Employee::getPassword); lambdaQueryWrapper.groupBy(Employee::getAge); List<Map<String, Object>> employees = employeeMapper.selectMaps(lambdaQueryWrapper); employees.forEach(System.err::println); QueryWrapper queryWrapper = new QueryWrapper(); queryWrapper.groupBy("age"); queryWrapper.select("age","count(id) count"); queryWrapper.having("count > 2"); List<Map<String, Object>> employees1 = employeeMapper.selectMaps(queryWrapper); employees1.forEach(System.err::println);
-
-
条件查询
-
比较运算符
-
allEq: 把 list 中的全部条件进行 and 比较,查询出符合要求的数据
-
eq:查询等于该条件的数据,后面可以使用多个 eq ,使用 and 拼接。
-
ne:查询不等于该条件的数据。
-
gt:大于该条件的数据
-
ge:大于或等于该条件的数据
-
lt:小于该条件的数据
-
le:小于或等于该条件的数据
-
between:取在 betwenn 中间的数据
-
notBetWeen:取不在 betwenn 中间的数据
-
isNull:取等于null的数据、
-
in:取等于 in 中所有的数据
-
notIn:取不等于 in 中所有的数据
-
inSql:取等于 in 中所有的数据,字符串拼接进去的
-
notinSql:取不等等于 in 中所有的数据,字符串拼接进去的
使用
QueryWrapper<Employee> queryWrapper = new QueryWrapper<Employee>(); queryWrapper.select("id","username","name"); // Map<String,Object> map = new HashMap<>(); // map.put("age",29L); // map.put("password","c4ca4238a0b923820dcc509a6f75849b"); // queryWrapper.allEq(map); // List<Map<String, Object>> employees = employeeMapper.selectMaps(queryWrapper); //获取不等于34的数据 // queryWrapper.ne("age",34L); // List<Map<String, Object>> employees = employeeMapper.selectMaps(queryWrapper); // queryWrapper.inSql("age","34,19,29"); // List<Map<String, Object>> employees = employeeMapper.selectMaps(queryWrapper);
-
-
模糊查询
-
like:查询某列中能匹配到该条件的数据
-
notList:查询某列中能不匹配到该条件的数据
-
likeLeft:查询某列中能在第一个字母开始,匹配到该条件的数据
-
likeRight:查询某列中能在最后一个字母开始,匹配到该条件的数据
使用
QueryWrapper<Employee> queryWrapper = new QueryWrapper<Employee>(); queryWrapper.select("id","username","name"); queryWrapper.like("username","de"); employeeMapper.selectMaps(queryWrapper); // queryWrapper.clear(); queryWrapper.notLike("username","de"); employeeMapper.selectMaps(queryWrapper); // queryWrapper.clear(); queryWrapper.likeLeft("username","de"); employeeMapper.selectMaps(queryWrapper); // queryWrapper.clear(); queryWrapper.likeRight("username","de"); employeeMapper.selectMaps(queryWrapper);
-
-
逻辑运算符
-
or:指定条件为 Or
-
and:指定条件 And
使用
QueryWrapper<Employee> queryWrapper = new QueryWrapper<Employee>(); queryWrapper.select("id","username","name"); //不管事and 还是 or 条件当成参数传进去后 sql 一定在括号中 queryWrapper.or(wq->wq.eq("username","defei").or().eq("id",21L)).eq("age",34L); employeeMapper.selectList(queryWrapper); //注意:and一定要传参数 queryWrapper.eq("id",21L).and(wq->wq.eq("age",34L)); employeeMapper.selectList(queryWrapper);
-
-
-
自定义Sql
-
和mybatis的使用方式一模一样
-
注解方式
-
在Mapper接口中使用,直接在方法上贴一个@Select 注解,并且在注解中写sql语句
-
在使用多表查询的时候,可以使用@Result注解定哪个列映射到哪个属性中。
-
-
Service接口
Service接口的一些常用方法,mybatis-plus 官方已经已经帮我们写好了,我们只要在接口中继承mybatis-plus 官方帮我们写 IService<泛型>接口,就可以指定一些常用的方法,到时候我们的实体类实现这个来后,我们就可以自己实现这些方法,也可以在继承mybatis-plus 官方帮我们写 ServiceImpl<泛型1,泛型2>,只要我们把 接口Mapper 放在泛型1,实体类放在泛型2,IService接口中的所有方法都帮我们实现好了,到时候我们直接调用就行了。
例子
public interface IEmployeeService extends IService<Employee> { } @Transactional @Service //这里是通过把 EmployeeMapper Employee 传给ServiceImpl 让它帮我们实现 IService 的方法 public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper,Employee> implements IEmployeeService { }
注意:ServiceImpl 内部的实现和我们平时写的一样,如果我们自己也要实现一些方法可以在接口中定义,并在实现类中实现,就可以调用 BaseMapper() 方法,对比一下从 IoC 容器中获取的 Mapper 是一样的。