MyBatis-Plus
1: mybatis-plus 常用注解
2: mybatis-plus 通用 mapper 接口方法
1>编写方式[必须掌握]
3: 条件构造器 QueryWrapper UpdateWrapper
4: 高级查询
里面各种条件查询必须会
1>投影
2>排序
3>分组
4>条件查询
5>自定义 sql
5: 通用 service接口
MyBatis-plus 是一个 MyBatis 的增强工具
mapper接口 继承 BaseMapper<当前实体类>
mybatis-plus mapper 编写规则:
1. 自定义一个mapper接口
2. 接口继承BaseMapper接口
3. BaseMapper接口明确指定要操作的实体对象泛型: Employee
问题1 : EmployeeMapper 接口并没有编写crud 方法, 为什么测试类中可以直接使用
EmployeeMapper 接口继承 BaseMapper 接口, 可以继承 BaseMapper接口中所有crud 方法
问题2 : 项目中并没有编写EmployeeMapper.xml 也没有crud sql 编写操作, 为啥就可以进行crud 操作呢?
项目没写, 但是 mybatis-plus 框架帮忙写了, 引申出一个问题: mybatis-plus怎么编写crud sql 语句的?
以查询所有数据为例子:
接口方法: employeeMapper.selectList(...)
执行sql: SELECT id,name,password,age,admin,dept_id FROM employee
思考: 如何查询上述查询 sql?
分析发现:
sql语句中表名跟domain中类名一致
sql语句中列名跟domain中字段名一致
最终原理:
mybatis-plus 启动之后会获取 BaseMapper 接口中指定的泛型类型,通过这个类型字节码对象 反射内省得到该实体对象的类名, 字段名,
进而拼接出 表名 与 列名, 那么 sql 语句就出来了
mybatis-plus注解
// 当前这个字段映射name对象
@TableField(value = "")
//mybatis-plus 会自动的将 dept_id 中的_转换为java中驼峰命名方式
private Long deptId;
// 不将当前字段作为表的列映射
@TabelField(exist = false)
当表名 与 实体类的名称不一致的时候
// 当前类名映射 t_employee 这张表
@TableName("t_employee")
如果 实体类中的 id 与 表中的 id不一致
@TableField(value = "id")
// 或
@TableId(type=IdType.AUTO) // 指定id操作策略 数据库自增
添加操作(insert)
mybatis-plus 做添加的时候, 如果 实体类属性值为 null, 不会将该属性作为 insert sql 中的列
修改操作(update)
updateById
// updateById
UPDATE employee SET name=?, age=?, admin=? where id=?
拼接sql规则 :
1> 传入参数(实体)对象如果属性值为null, 那么该属性不参与sql 列拼接
2> 如果参数(实体)对象属性为基本属性, 它们有默认值, mybatis-plus就认为
是属性值不为 null ,这些属性会参与 sql 列拼接
问 : 如果不想让只有默认值得基本属性参与sql列拼接怎么办?
方式1: 将基本属性字段设置为包装类型
方式2: 使用 BaseMapper中的 update(null,wrapper)
update( 实体类 / null ,wrapper)
// update(employee,wrapper);
这种写法是 update 方法中其中一种操作方式, update 数据从传入的 employee 实体中获取
employee.setAge(18);
employee.setName("dafei");
// 暂时理解where 条件 sql 片段: name=dafei
UpdateWrapper<Employee> wrapper = Updatenew Wrapper<>();
wrapper.eq("name","dafei");
employeeMapper(Employee,wrapper);
// update(null,wrapper);
这种用法 : 实体对象设置为 null ,更新数据与条件从wrapper 获取[部分字段更新方式]
UpdateWrapper<Employee> wrapper = new UpdateWrapper<>();
wrapper.eq("name","dafei");
wrapper.set("age",18);
employeeMapper.update(null,wrapper);
updateById 与 update 的使用范围
updateById
使用范围:
1> update 的实体对象必须有id值
2> 全字段(全量) 更新
update
适用范围:
1> update数据筛选条件不仅仅是 id
2> 部门字段更新
删除操作
deleteById
Long 继承 Number, Number 实现 Serializable 序列化接口
deleteBatchIds
批量删除
employeeMapper.deleteBatchIds(Arrays.asList(21L,22L));
sql语句:
delete form employee where id in(?,?)
deleteByMap
map = new HashMap<>();
map.put(key,value);
// key : 条件列 , value : 条件值
SQL语句:
delete from employee where name = ? and age =?
delete(wrapper)
// 与 updateWrapper 一样, 区别: 一个update相关(DML), 一个查询相关(DQL)
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
wrapper.eq("","");
查询操作
selectById
查单个
selectBatchIds
批量查询
batch方法在mysql 中 等于 in
selectByMap
以Map为条件
selectCount(wrapper)
查全部count 有多少条数据
不拼接任何条件
返回 Integer 类型
selectList(wrapper / null)
查所有数据
selectMaps(wrapper)
查所有,当返回的值没法用实体对象封装的时候
返回值 List<Map<String,Object>> mapList
底层将每条数据封装成HashMap
selectPage
//分页拦截器
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
paginationInnerInterceptor.setOverflow(true); //合理化
interceptor.addInnerInterceptor(paginationInnerInterceptor);
return interceptor;
}
//1: 分页拦截器
在 APP.class 启动类 中 配置一个 分页拦截器
//2: 分页逻辑
// mybatis-plus 分页对象 等价于 PageResult 或者 PageInfo
IPage<Employee> page = new Page<>(current : 当前页 ,size : 每页显示条数);
条件构造器
父类: AbstractWrapper
更新
使用 UpdateWrapper
@SpringBootTest
public class ControllerTest {
@Autowired
private EmployeeMapper employeeMapper;
/**
* 需求:将id=1的员工name改为scorpio 会造成数据丢失 因为 age 和
* admin 字段是基本类型 有默认值 0
* 当进行updateById 的时候 会默认添加进去
*/
@Test
public void testUpdate(){
Employee employee = new Employee();
employee.setId(1L);
employee.setName("scorpio");
employeeMapper.updateById(employee);
}
/**
* 需求:将id=9的员工name改为scorpio
* UPDATE employee SET name=? WHERE (id = ?)
*
* wrapper.eq() 表示条件
* wrapper.set() 表示修改内容
*/
@Test
public void testUpdate2(){
UpdateWrapper<Employee> wrapper = new UpdateWrapper<>();
wrapper.eq("id",9L);
wrapper.set("name","scorpio");
employeeMapper.update(null,wrapper);
}
/**
* 需求:将id=9的员工age改为20, 如果传入uname变量值不等于null
* 或者“”,修改为员工name为uname变量值
* UPDATE employee SET age=?,name=? WHERE (id = ?)
*/
@Test
public void testUpdate3(){
String uname = "xxx";
UpdateWrapper<Employee> wrapper = new UpdateWrapper<>();
wrapper.eq("id",9L);
wrapper.set("age",20);
/*if (StringUtils.hasLength(uname)){
wrapper.set("name",uname);
}*/
// 等价于上面的操作
wrapper.set(StringUtils.hasLength(uname),"name",uname);
employeeMapper.update(null,wrapper);
}
/**
* setSql
* 需求:将id=9的用户name改为scorpio
* UPDATE employee SET name='scorpio' WHERE (id = ?)
*/
@Test
public void testUpdate4(){
UpdateWrapper<Employee> wrapper = new UpdateWrapper<>();
wrapper.eq("id",9L);
wrapper.setSql("name='scorpio'");// 使用sql 片段 直接插入
employeeMapper.update(null,wrapper);
}
}
使用 LambdaUpdateWrapper
/**
* LambdaUpdateWrapper
* 需求:将id=9的用户name改为scorpio
*/
@Test
public void testUpdate5(){
LambdaUpdateWrapper<Employee> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(Employee::getId,9L);
wrapper.set(Employee::getName,"scorpio");
employeeMapper.update(null,wrapper);
}
查询操作 QueryWrapper 和 LambdaQueryWrapper
/**
* QueryWrapper
* 需求:查询name=scorpio, age=20的用户
* SELECT id,name,password,email,age,admin,dept_id
* FROM employee WHERE (name = ? AND age = ?)
*/
@Test
public void testQuery1(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
/*wrapper.eq("name","scorpio");
wrapper.eq("age",20);*/
// 等价于上面 代码
wrapper.eq("name", "scorpio").eq("age", 20);
System.out.println(employeeMapper.selectList(wrapper));
}
/**
* LambdaQueryWrapper
* 需求:查询name=scorpio, age=20的用户
* SELECT id,name,password,email,age,admin,dept_id
* FROM employee WHERE (name = ? AND age = ?)
*
* 开发中推荐使用
*/
@Test
public void testQuery2(){
LambdaQueryWrapper<Employee> wrapper = new LambdaQueryWrapper<>();
/*wrapper.eq(Employee::getName,"scorpio");
wrapper.eq(Employee::getAge,20);*/
// 等价于上面 代码
wrapper.eq(Employee::getName, "scorpio").eq(Employee::getAge, 20);
System.out.println(employeeMapper.selectList(wrapper));
}
各种 Wrapper 对象的创建
/**
* 各种 Wrapper 的创建
*
* public static <T> QueryWrapper<T> query() {
* return new QueryWrapper<>();
* }
*/
@Test
public void testWrappers(){
// Query
QueryWrapper<Employee> queryWrapper1 = new QueryWrapper<>();
QueryWrapper<Employee> queryWrapper2 = Wrappers.<Employee>query();
LambdaQueryWrapper<Employee> queryWrapper3 = new LambdaQueryWrapper<>();
LambdaQueryWrapper<Employee> queryWrapper4 = Wrappers.<Employee>lambdaQuery();
LambdaQueryWrapper<Employee> queryWrapper5 = queryWrapper2.lambda();
// Update
UpdateWrapper<Employee> updateWrapper1 = new UpdateWrapper<>();
UpdateWrapper<Employee> updateWrapper2 = Wrappers.<Employee>update();
LambdaUpdateWrapper<Employee> updateWrapper3 = new LambdaUpdateWrapper<>();
LambdaUpdateWrapper<Employee> updateWrapper4 = Wrappers.<Employee>lambdaUpdate();
LambdaUpdateWrapper<Employee> updateWrapper5 = updateWrapper2.lambda();
}
高级查询
列投影
wrapper.select("",""); // 列投影: 列表出指定的列, 其他列不查
wrapper.select(" , "); // sql 片段
排序
都是正序
wrapper.orderByAsc("age","id"); // ORDER By age ASC,id ASC
wrapper.orderByAsc("age"); // 正序
wrapper.orderByDesc("id"); // 倒序
// 参数1 : 满足排序条件, 参数2: 排序策略(true:正序,false:倒序)
wrapper.orderBy(true,true,"age");
wrapper.orderBy(true,false,"id");
分组查询
wrapper.select("dept_id","count(id) count");
wrapper.groupBy("dept_id");
// 如果要添加 having 条件
// wrapper.having("count>3");
wrapper.having("count > {0}",3);
// 因为返回的东西不可以直接封装成对象 , 所以 用 selectMaps
条件查询
比较运算符
1.eq("属性名","属性值") : 等于
// 一个eq,一个等 两个eq加起来等 and 操作
key value
sql (name = ? AND age = ?)
2.allEq(map) : 里面传的是个 map数组 全等于
sql (name = ? AND age = ?)
3.ne : 不等于
4.gt : 大于
5.ge : 大于等于
6. lt : 小于
7. le : 小于等于
8.between : 某一列大于某个值小于某个值 [n,m]
9.notBetween : 某一列不大于某个值也不小于某个值 [n,m]之外
10.isNull : 为空
wrapper.isNull("name"); // where name is null
11.isNotNull : 不为空
wrapper.isNotNull("name"); // where name is not null
12.in : 某列 = ? 或 某列 = ?
wrapper.in("id",1L,2L); // id in(1,2)
13.insql : 某列 = ? 或 某列 = ? sql片段
wrapper.insql("id","1,2"); // id in(1,2)
14.notInsql : 某列 != ? 或 某列 != ?
wrapper.notInsql("id","1,2"); // id not in(1,2)
模糊查询
like : %fei%
wrapper.like("name","fei"); // 自动拼接 %fei%
notLike :
wrapper.like("name","fei");
likeLeft : '%fei' 以fei结尾
以 % 判断左右
likeRight : 'fei%' 以fei开头
逻辑运算符
and : 和
// 方式1:
// mybatis-plus 默认 多条件 下 and连接
// 如果想要 or 操作 就需要明确调用or方法
// 在中间
wrapper.or();
// 方式2:
wrapper.lt("age",18).or().gt("age",30);
or : 或者
嵌套 and :
wrapper.like("name","fei")
.and(wr -> wr.lt("age",18).or().gt("age",30))
// wr 为 变量 随便写 代表第二个wrapper
/**
* 需求:查询name含有hao字样的,和 年龄在18到30之间的用户
* WHERE (name LIKE ? AND (age BETWEEN ? AND ?))
*/
@Test
public void testQueryANDOR2(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
wrapper.like("name","hao")
.and(wr -> wr.between("age",18,30));
employeeMapper.selectList(wrapper);
}
自定义Sql ->mapper.xml方式
多表关联查询
多表关联查询时, 可以使用以前 xml 方式进行关联操作
如果使用 mybatis-plus 该怎么写?
mybatis-plus 是没法做多表关联查询: 即无法使用 join, 但支持 额外sql查询
如果需要关联查询 : 如果采用额外sql 方式, 直接用mybatis-plus编码方式即可 ,如果采用关联查询方式 需要自己写 mapper.xml 文件
@Test
public void testQuery(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
List<Employee> employees = employeeMapper.selectList(wrapper);
for (Employee employee : employees) {
employee.setDept(departmentMapper.selectById(employee.getDeptId()));
}
employees.forEach(System.err::println);
}
自定义Sql ->注解方式
@Select4
通用Service接口
/**
* mybatis-plus 通用 service 层接口:
* 1: 自定义 一个业务层 service 层接口 (IEmployeeService)
* 2: 继承一个通用接口 IService<T> 接口
* 3: 指定 泛型为 当前 service 的 实体对象(Employee)
*/
public interface IEmployeeService extends IService<Employee> {
}
业务实现类
/**
* mybatis-plus service 层实现类定义规则:
* 1>继承一个 ServiceImpl 类
* 2>指定 两个泛型: 操作实体对象对应mapper接口: EmployeeMapper
* 操作实体对象 : Employee
* 3>实现自定义service接口 : IEmployeeService
*/
@Service
public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper, Employee> implements IEmployeeService {
}
insert == save
updateById == updateById
deleteById == removeById
selectById == getById
selectList == list
实现分页逻辑
service接口
public interface IEmployeeService extends IService<Employee> {
/**
* 分页操作
*/
IPage<Employee> queryPage(EmployeeQueryObject qo);
}
service实现类
@Service
public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper, Employee> implements IEmployeeService {
@Override
public IPage<Employee> queryPage(EmployeeQueryObject qo) {
//设置分页信息
IPage<Employee> page = new Page<>(qo.getCurrentPage(), qo.getPageSize());
//拼接条件
QueryWrapper<Employee> wrapper = Wrappers.<Employee>query();
return super.page(page,wrapper);
}
}
测试类
/**
* -----------------------分页操作--------------------
*/
@Test
public void testPage(){
EmployeeQueryObject qo = new EmployeeQueryObject();
qo.setPageSize(3);
qo.setCurrentPage(2);
IPage<Employee> page = employeeService.queryPage(qo);
System.out.println("当前页:" + page.getCurrent());
System.out.println("总页数:" + page.getPages());
System.out.println("每页显示条数:" + page.getSize());
System.out.println("总记录数:" + page.getTotal());
System.out.println("当前页显示记录:" + page.getRecords());
}
}