MyBatis-plus总结
常用的注解
-
主键自增使用的注解
- 需要贴在表中主键列对应的字段上,否则系统默认使用的是雪花算法,这不是我们想要的结果
-
需要注意的如果表本来是雪花算法的,实体类的主键id贴上注解@TableId后的数据库的下一个自增的是递增规律的自增,但是数值还是上一个梅花算法加一
@TableId(value = "id",type = IdType.AUTO)//设置主键自增 private Long id; IdType.ASSIGN_ID // 分配ID(主键类型为Number(Long和Integer)或String)(since 3.3.0),使用接口IdentifierGenerator的方法nextId(默认实现类为DefaultIdentifierGenerator雪花算法) // IdType.ASSIGN_UUID // 分配UUID,主键类型为String(since 3.3.0),使用接口IdentifierGenerator的方法nextUUID(默认default方法)
-
@TableName当实体类名和数据库表中的不能一一对应时,可以使用该注解
//@TableName("t_employee") //当表名和类名不一致时可以通过注解来声明 public class Employee {
-
@TableFild
-
当某个字段和数据库中列名对应不上时可以使用该注释来声明
@TableField("ename") private String name;
-
当某个字段不参与表中列数的映射时可以使用该注释排除
@TableField(value ="dept",exist = false) private Department dept;
-
-
@Verison乐观锁的操作字段后续讲解
条件构造器
warpper继承体系
-
父类构造器AbstractWarpper
-
需要说的是以下构造器都可以使用new一个新对象的方式获取,当然也可以使用他MyBatis-plus自己提供的Warppers工具类,
-
修改型:UpdateWarpper继承了父类所有的共有的方法:还有自己独特的set\setSql 其中也设置了开关式选择性的拼接语句,也就是相当于xml配置中的
-
查询型:QueryWarpper继承了父类所有的共有的方法:还有自己独特的select(选择想要展示的列),orderBy系列 groupBy系列 其中也设置了开关式选择性的拼接语句,也就是相当于xml配置中的
-
不能在 WHERE 子句中对分组限定,限制组须使用 HAVING 子句;
-
不能在 WHERE 子句中使用统计函数,而在 HAVING 子句可使用统计函数。
WHERE 分组之前过滤
HAVING 过滤
- SELECT 子句出现的字段,要不在统计函数中,要不出现在 GROUP BY 子句中,否则不合理(整体与个体);
- 在GROUP BY 子句中出现的字段,可以不出现在 SELECT 列表中;
- 统计函数可以单独使用,SQL 中可以没有 GROUP BY 子句;
- 在 GROUP BY 子句中,可以按单列进行分组,也可以在多列上进行分组,多列分组就是按照多个字段的组合进行分组,最终的结果也会按照分组字段进行排序显示。
@Test public void testUpdateAndQuery() { //修改 UpdateWrapper< Employee > updateWrapper = Wrappers.update(); updateWrapper.set(true, "name", "大龙long龙"); updateWrapper.eq("id", 2L); employeeMapper.update(null, updateWrapper); //查询 QueryWrapper< Employee > wrapper = Wrappers.< Employee >query().select("name,age").eq("id", 2L); employeeMapper.selectList(wrapper); //分组 QueryWrapper< Employee > queryWrapper = Wrappers.<Employee>query(). select("dept_Id", "count(id) count").groupBy("dept_id").having("count>{0}", 2); List< Map< String, Object > > maps = employeeMapper.selectMaps(queryWrapper); maps.forEach(System.err::println); }
-
lambda型:参数不在是属性字符串,Employee::getName调用属性,避免了输入参数时输错,但也有部分功能无法实现,例如lambda无法实现 conut(id)没有属性列的时候无法实现
-
修改型:LambdaUpdateWarpper
-
查询型:LambdaQueryWarpper缺点是无法独立完成分组的数量的统计,也就是当表列中和Employee实体类中都不存的属性类时无法展示,这样统计的分组个数就无法完成,所以分组时建议使用非lambda
@Test public void testLambdaUpdateAndQuery(){ //修改 LambdaUpdateWrapper< Employee > lambdaUpdate = Wrappers.lambdaUpdate(); lambdaUpdate.set(true,Employee::getName,"大龙龙"); lambdaUpdate.eq(Employee::getId,2L); employeeMapper.update(null,lambdaUpdate); //查询 LambdaQueryWrapper< Employee > lambdaQuery = Wrappers.lambdaQuery(); LambdaQueryWrapper< Employee > queryWrapper = lambdaQuery.select(Employee::getDeptId,Employee::getName); employeeMapper.selectMaps(queryWrapper); }
-
总结:快捷便捷无错误的使用lambda形式,需要实现一些复杂业务的拼接的使用手动拼接的sql方式也就是非lambda方式
-
高级查询
条件确认
//allEq:将多个条件放入到map中 eq:相等的条件 ne:取反条件
@Test
public void testallEq(){
//LambdaQueryWrapper< Employee > lambdaQuery = Wrappers.< Employee >lambdaQuery();
//不能使用lambda的方式
QueryWrapper< Employee > wrapper = Wrappers.< Employee >query();
Map map = new HashMap< String,Object >();
map.put("name","钱总");
map.put("age",null);
wrapper.allEq(map,false);
employeeMapper.selectList(wrapper);
}
@Test
public void testOR1(){
LambdaQueryWrapper< Employee > lambdaQuery = Wrappers.< Employee >lambdaQuery();
// lambdaQuery.in(Employee::getAge,18,19,20,30,25);
lambdaQuery.eq(Employee::getAge,18).or().like(Employee::getName,"明").or().eq(Employee::getId,1L);
employeeMapper.selectList(lambdaQuery);
}
@Test
public void testNe(){
LambdaQueryWrapper< Employee > lambdaQuery = Wrappers.< Employee >lambdaQuery();
//名字不等的方式,只要不等的全部查出来
lambdaQuery.ne(Employee::getName,"钱总");
employeeMapper.selectList(lambdaQuery);
}
大于 \大于等于\ 小于等于\小于,以及and or的用法
//gt\ge\lt\le的用法
@Test
public void testge_t_l(){
LambdaQueryWrapper< Employee > lambdaQuery = Wrappers.< Employee >lambdaQuery();
lambdaQuery.ne(Employee::getName,"钱总").and(
wp->wp.ge(Employee::getAge,18).le(Employee::getAge,32)
);
employeeMapper.selectList(lambdaQuery);
}
//需求:查询name含有fei字样的并且 年龄在小于18或者大于30的用户
@Test
public void testOR(){
LambdaQueryWrapper< Employee > lambdaQuery = Wrappers.< Employee >lambdaQuery();
lambdaQuery.like(Employee::getName,"明").and(
wp->wp.ge(Employee::getAge,18)
.or()
.le(Employee::getAge,32)
);
employeeMapper.selectList(lambdaQuery);
}
Between NotBetween
//需求:查询年龄介于18~30岁的员工信息
@Test
public void testBetween(){
LambdaQueryWrapper< Employee > lambdaQuery = Wrappers.< Employee >lambdaQuery();
//lambdaQuery.notBetween(Employee::getAge,18,30).orderBy(true,false,Employee::getAge);
lambdaQuery.between(Employee::getAge,18,30).orderBy(true,false,Employee::getAge);
employeeMapper.selectList(lambdaQuery);
}
In
-
in 和notIn支持将包含范围放入一个llist中,或者是可变数组的方式填入 inSql 和notInSql,其实就是一个sql片段,该片段不需要写括号底层会自动拼接
//需求: 查询id不为1, 2 的员工信息 @Test public void testIn(){ LambdaQueryWrapper< Employee > lambdaQuery = Wrappers.< Employee >lambdaQuery(); //lambdaQuery.in(Employee::getId,1l,2l); //lambdaQuery.notIn(Employee::getId,1l,2l); List< Integer > list = Arrays.asList(29, 19); lambdaQuery.notIn(Employee::getAge,list); //lambdaQuery.inSql(Employee::getId,"1,2"); //lambdaQuery.notInSql(Employee::getId,"1,2"); employeeMapper.selectList(lambdaQuery); //不能使用lambda的方式 }
like
-
模糊查询,like notLike这两种比较实用高级查询时
-
noLike就是取反
-
,leftLike rightLike 是拼接时%在左边还是右边的api,这样也是为提高性能时使用 ,看需求去选择使用,
-
支持参数中加入条件判断也就是xml配置中的if标签是的boolean表达式 like(boolean,列名,keyword)
@Test public void testLike(){ LambdaQueryWrapper< Employee > lambdaQueryWrapper = Wrappers.< Employee >lambdaQuery(); lambdaQueryWrapper.like( true,Employee::getName,"明"); employeeMapper.selectList(lambdaQueryWrapper); }
--执行sql leftLike rightLike %只有一个放在左边还是右边有api的选择 ==> Preparing: SELECT id,name,password,email,age,admin,dept_id FROM employee WHERE (name LIKE ?) ==> Parameters: %明%(String)
逻辑运算符 or and
-
链式操作中默认拼接的and,
@Test public void queryLogic(){ QueryWrapper< Employee > wrapper = new QueryWrapper<>(); wrapper.like("name","钱").and( wp->wp.ge("age",18).le("age",30) ); employeeMapper.selectList(wrapper); }
/** * SELECT * * * FROM * employee * WHERE * NAME LIKE "%fei%" * AND (age < 18 OR age > 30) * wrapper.like("name","fei").and( * wp->wp.ge("age",18).le("age",30) * );\ * 可以理解两wrapper的拼接 真实语句SELECT id,name,password,email,age,admin,dept_id FROM employee WHERE (name LIKE ? AND (age => ? AND age <= ?)) */
-
而需要使用or这需要进行声明拼接
// 需求: 查询age = 18 或者 name=dafei 或者 id =1 的用户 @Test public void testOR1(){ LambdaQueryWrapper< Employee > lambdaQuery = Wrappers.< Employee >lambdaQuery(); // lambdaQuery.in(Employee::getAge,18,19,20,30,25); lambdaQuery.eq(Employee::getAge,18) .or() .like(Employee::getName,"明") .or() .eq(Employee::getId,1L); employeeMapper.selectList(lambdaQuery); }
==> Preparing: SELECT id,name,password,email,age,admin,dept_id FROM employee WHERE (age = ? OR name LIKE ? OR id = ?) ==> Parameters: 18(Integer), %明%(String), 1(Long)
单表,关联多表查询
-
需要说明的是MyBatis-plus只在原来的基础上做增强不做修改,也就是说原先的xml配置自定义sql的方式还是可以使用
-
在做多表查询时其缺点也就暴露出来了,想要完成多表查询就需要多发一条sql,这样在性能上远远差于原本的mybatis的自定义sql
//需求:查询员工信息的同时将其部门信息查询出来 //MyBatis-plus实现方式 @Test public void testEmp2deptByPlus(){ LambdaQueryWrapper< Employee > lambdaQuery = Wrappers.< Employee >lambdaQuery(); List< Employee > employees = employeeMapper.selectList(lambdaQuery); for (Employee employee : employees) { employee.setDept(departmentMapper.selectById(employee.getDeptId())); } employees.forEach(System.err::println); }
-
MyBatis-plus有注释支持拼接SQL语句,可以将多表查询的sql语句拼接到注释中,但是这违背,SQL和java代码分离的思想且耦合性极高,不便于后续的维护
@Select("select e.*, d.id d_id, d.name d_name, d.sn d_sn from employee e left join department d on e.dept_id = d.id") @Results({ @Result(column="d_id", property = "dept.id"), @Result(column="d_name", property = "dept.name"), @Result(column="d_sn", property = "dept.sn") }) List<Employee> listByAnnoJoin();
Mapper接口方法
日志配置更改
#logging.level.cn.wolfcode.mp.mapper=debug#旧的
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
insert
- 方法和原装的mybatis没有差别,只是如果实体类没有设置主键自增的注解会导致出现雪花算法乱串数字
update
-
问题引出:单个或非完全修改对象信息,就会出现基本数据类型数据更改导致数据错乱,而引用数据类型则不会,因为引用数据类型默认值为null,mybaits-plus会因为null不进行sql拼接那么也就不会出现数据错误修改的情况, 而基本数据类型int就不能避免这个问题了;
-
解决问题:
-
修改int类型为integer
-
updateById 先查询出整个对象,后进行单个修改然后update,显然很繁琐
public void testUpdateBySelect(){ //这里看到我们年龄并没有消失变成为零,先查询后修改方法 Employee employee = employeeMapper.selectById(3L); employee.setId(3L); employee.setEmail("dafei@wolfcode.cn"); employee.setName("xiaofei"); employee.setPassword("11"); employeeMapper.updateById(employee); }
-
update mybaits-plus自带的优势方法,使用UpdateWrapper来设置值,选择性的拼接语句
-
这里使用**eq()**来设置修改的条件或者说是确定目标,set,setSql ApI来设置修改的地方
-
并且支持链式操作,与顺序无关Api有关,且setSql和set可以混合使用
public void testUpdateByWarpper(){ UpdateWrapper< Employee > wrapper = new UpdateWrapper<>(); //wrapper.set("name","大龙"); //这里使用是eq API来确实修改的条件 wrapper.eq("id",3L); wrapper.setSql("name='大大条龙'");//这种方式使用时sql的片段的方式 employeeMapper.update(null,wrapper); }
-
delete
-
单个删除deleteById使用方法和mybatis没有区别
-
批量删除deleteBatchIds需要传入一个集合ids
//Preparing: DELETE FROM employee WHERE id IN ( ? , ? ) 使用的是in关键字 @Test public void testDeletePlus3(){ List< Long > list = Arrays.asList(19L, 18L); employeeMapper.deleteBatchIds(list); }
-
支持deleteByMap设置删除
@Test public void testDeletePlus2(){ Map< String, Object > map = new HashMap<>(); map.put("name","大大条龙"); employeeMapper.deleteByMap(map); }
-
delete配置warpper的使用,设置列使用条件构造器
@Test public void testDeletePlus(){ LambdaUpdateWrapper< Employee > wrapper = Wrappers.< Employee >lambdaUpdate(); wrapper.eq(Employee::getPassword,"111"); employeeMapper.delete(wrapper); }
Service层的调用
实体Mapper接口继承BaseMapper
-
就会继承其中的17个方法
public interface EmployeeMapper extends BaseMapper< Employee > { //可以在这里添加自定义sql,当然需要配合xml文件使用(写自定义sql) List<Employee> selectAll(); }
Service层的构建
-
Service层的接口需要继承接口IService但需要加入泛型的当前实体列
public interface IEmployeeService extends IService< Employee > { //分页方法 Page<Employee> query(EmployeeQuery qo); }
-
实现类则需要实现定义的接口,而其中需要先继承ServiceImpl<XxxMapper,Xxx>再实现定义的Service层定义的接口
-
看到的好处是可以选择性是实现接口方法,并且能直接调用父类实现的方法,后续业务会体验到他的好处
-
@Service public class EmployeeServiceImpl extends ServiceImpl< EmployeeMapper, Employee > implements IEmployeeService { }
-
-
分页的实现
-
首先需要配置一个分页拦截器的配置,这个配置建议写配置类下,利用@Bean生成对象交给Spring容器管理
-
//正常需要重新配置一个配置类来进行java配置 @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL); paginationInnerInterceptor.setOverflow(true);//合理化,是为了页数输入是-1时也会直接跳转到首页1 interceptor.addInnerInterceptor(paginationInnerInterceptor); return interceptor; }
-
定义分页的条件QueryObject XxxQueryObject
public class QueryObject { private int currentPage = 1; private int pageSize = 3; } public class EmployeeQuery extends QueryObject { private String keyWord; }
-
再实现分页定义的方法
-
@Override public Page< Employee > query(EmployeeQuery qo) { Page< Employee > page = new Page<>(qo.getCurrentPage(),qo.getPageSize()); //拼接条件使用的 LambdaQueryWrapper< Employee > wrapper = Wrappers.< Employee >lambdaQuery(); //拼接条件就相当于之前的where标签中的套条件当然也需要判空一下 wrapper.like(StringUtils.hasLength(qo.getKeyWord()),Employee::getName,qo.getKeyWord()); return super.page(page,wrapper); }
-
方法中支持事务只需要贴上响应的注释就行@Transactional
码云中有整个项目里面还有一些需要的依赖https://gitee.com/maker_8g/mybatis-plus.git
-