Mybatis-plus
MyBatis-Plus 是基于 MyBatis 的扩展,它提供了更多的功能和优化,并且减少了开发人员需要手动编写的 SQL 代码,从而提高了效率和代码质量。MyBatis-Plus 还提供了一些高级功能,例如自动生成代码,分页查询,数据库字段自动映射等。
一.源码
简要介绍MyBatis的源码分析。
SqlSessionFactoryBuilder类:该类用于构建SqlSessionFactory对象,负责解析MyBatis的配置文件、创建DataSource数据源对象和创建Transaction事务管理器对象。
SqlSessionFactory类:该类是MyBatis的核心类,负责创建SqlSession对象,管理MyBatis的所有配置信息和资源,提供了对数据库的操作接口和方法。
SqlSession类:该类是MyBatis的数据访问层接口,封装了对数据库的所有操作和管理,包括查询、插入、更新和删除等,同时支持事务和缓存管理。
Mapper接口:该接口是MyBatis的映射接口,用于定义数据访问的方法和SQL语句,通过注解或XML文件实现与数据库的映射关系。
MapperProxy类:该类是MyBatis的Mapper接口的动态代理类,通过反射机制实现了Mapper接口的具体方法调用,可以在Mapper接口上面实现事务和缓存管理。
Configuration类:该类是MyBatis的配置类,包括了MyBatis的所有配置信息和资源,例如DataSource、Mapper接口、SQL语句等。
Executor类:该类是MyBatis的执行器类,负责执行Mapper接口定义的方法,解析SQL语句、处理参数、执行查询和更新等操作。
StatementHandler类:该类是MyBatis的SQL语句处理类,负责处理SQL语句、参数绑定、结果集映射等操作。
ParameterHandler类:该类是MyBatis的参数处理类,负责处理SQL语句中的参数绑定、转换和处理,支持多种数据类型和对象。
ResultSetHandler类:该类是MyBatis的结果集处理类,负责将数据库的结果集映射成Java对象或集合,支持多种映射方式和数据类型。
二.优势
注意事项:
1.ServiceImpl父类已经注入了UserMapper对象,名称叫做baseMapper,所以当前实现类直接可以使用baseMapper完成操作 2.因为ServiceImpl已经实现了IService下的方法,所以当前服务类没有必要再次实现
思想:共性的业务代码交给框架封装维护,非共性的业务,在接口UserService定义,然后在当前的服务类下实现;
快速使用
一.自定义一个接口继承IService接口
二.自定义一个类继承ServiceImpl类和实现自定义的接口
/**
* 自定义一个服务层实现类,实现自定义的服务层接口
* 因为这个服务层接口继承的公共服务层接口,有许多待实现的方法
* 所以我们还有继承一个ServiceImpl类,这个类实现了IService接口的所有方法
* 第一个泛型是要使用的dao层mapper,ServiceImpl会自动注入这个mapper bean,所以自定义的实现类就不用注入mapper了
* 注入的这个mapper成员变量叫baseMapper
*/
MP封装Service实现CRUD操作
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响。
- 损耗小:启动即会自动注入基本CURD,性能基本无损耗,直接面向对象操作。
- 强大的CRUD操作:内置通用Mapper、通用Service,仅仅通过少量配置即可实现单表大部分CRUD操作,更有强大的条件构造器,满足各类使用需求。
- 支持Lambda形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写
错。
-
支持主键自动生成:支持多达4种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题。
- 支持ActiveRecord模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作。
-
支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )。
-
内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用。
-
内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询。
-
分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、
Postgre、SQLServer 等多种数据库。
-
内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询。
-
内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作。
-
(二)常用的Lombok注解介绍
@Data 注在类上,提供类的get、set、equals、hashCode、toString等方法
@Getter 加在类上,可以自动生成参数的getter方法 @Setter 加在类上,可以自动生成参数的setter方法 @ToString 加在类上,调用toString()方法,可以输出实体类中所有属性的值 @RequiredArgsConstructor 生成一个包含常量,和标识了NotNull的变量的构造方法。生成的构造方法是私有的private @EqualsAndHashCode 生成equals和hashCode方法,
默认使用非静态的属性,
可以通过exclude参数排除不需要生成的属性。
通过of参数来指定需要生成的属性
@NoArgsConstructor
生成一个无参数的构造方法
@AllArgsConstructor
生成一个包含所有变量的构造方法。
@Value
这个注解要和Spring的@Value注解区分,Spring的是从配置文件读取内容,这个注解是在类中的所有字段默认全部声明为private final类型,只会生成Getter方法,不会生成Setter方法。
@Cleanup
主要用于关闭资源使用。
- 使用注解@TableId指定id生成策略,让其使用主键自增,而不是使用雪花算法生成一个不重复的Long型id
-
查询功能
MP实现分页查询
MP的分页拦截器并没有自动装配,所以需要我们在配置类自己维护一个分页拦截器的bean
-
@Test
-
public void test05(){
-
int page=2;
-
int pageSize=4;
-
//构建分页对象
-
IPage<User> pageInfo = new Page<>(page, pageSize);
-
//pageInfo==userIPage-->true,这两个对象是同一个,地址相同
-
//null为无条件查询
-
// IPage<User> userIPage = userMapper.selectPage(pageInfo, null);
-
//TODO:因为这两是同一个对象,所以可以直接不定义一个新对象
-
userMapper.selectPage(pageInfo,null);
-
//获取分页参数
-
long total =pageInfo.getTotal();//获取总记录数
-
long pages =pageInfo.getPages();//获取总页数
-
long size = pageInfo.getSize();//获取当前页大小
-
long current= pageInfo.getCurrent();//获取当前的页数
-
List<User> users =pageInfo.getRecords();//获取当前页的对象集合
-
QueryWrapper模糊查询like
模糊查询常用方法
- like("表列名","条件值"); 作用:查询包含关键字的信息,底层会自动添加匹配关键字,比如:%条件值%
- likeLeft("表列名","条件值"); 作用:左侧模糊搜索,也就是查询以指定条件值结尾的数据,比如:%条件值
- likeRight("表列名","条件值");作用:右侧模糊搜索,也就是查询以指定条件值开头的数据,比如:条件值%
-
MP实现Service封装
MybatisPlus为了开发更加快捷,对业务层也进行了封装,直接提供了相关的接口和实现类; 我们在进行业务层开发时,可以继承它提供的接口和实现类,使得编码更加高效;
实现流程
- 定义一个服务扩展接口,该接口继承公共接口IService;
- 定义一个服务实现类,该类继承ServiceImpl<Mapper,Entity>,并实现自定义的扩展接口;
-
/**
-
* 自定义一个服务层接口继承公共服务接口,泛型是实体类对象(通过@TableName注解知道要操作的数据表)
-
*/
-
public interface UserService extends IService<User> {
-
IPage<User> getUserGtId(IPage<User>page,Long id);
-
}
-
/**
-
* 自定义一个服务层实现类,实现自定义的服务层接口
-
* 因为这个服务层接口继承的公共服务层接口,有许多待实现的方法
-
* 所以我们还有继承一个ServiceImpl类,这个类实现了IService接口的所有方法
-
* 第一个泛型是要使用的dao层mapper,ServiceImpl会自动注入这个mapper bean,所以自定义的实现类就不用注入mapper了
-
* 注入的这个mapper成员变量叫baseMapper
-
*/
-
@Service
-
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
-
/**
-
* 只需要实现UserService接口的方法
-
*/
-
@Override
-
public IPage<User> getUserGtId(IPage<User> page, Long id) {
-
//直接使用ServiceImpl注入的mapper bean即可
-
return baseMapper.findUserGtId(page,3L);
-
}
-
}
-
/**
-
* @Description 测试条件查询,且仅返回一个
-
* getOne:sql查询的结果必须为1条或者没有,否则报错 !!!!
-
*/
-
@Test
-
public void test2(){
-
LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery(User.class);
-
wrapper.gt(User::getAge,20);
-
User one = userService.getOne(wrapper);
-
System.out.println(one);
-
}
-
/**
-
* @Description 根据条件批量查询
-
*/
-
@Test
-
public void test3(){
-
LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery(User.class);
-
wrapper.gt(User::getAge,20);
-
List<User> list = userService.list(wrapper);
-
System.out.println(list);
-
}
-
/**
-
* @Description 根据条件批量查询并分页
-
*/
-
@Test
-
public void test4(){
-
LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery(User.class);
-
wrapper.gt(User::getAge,20);
-
//构建分页对象
-
IPage<User> page=new Page<>(2,3);
-
userService.page(page,wrapper);
-
System.out.println(page.getRecords());
-
System.out.println(page.getPages());
-
System.out.println(page.getTotal());
-
}
-
/**
-
* @Description 测试服务层save保存单条操作
-
*/
-
@Test
-
public void test5(){
-
User user1 = User.builder().name("wangwu").userName("laowang4").
-
email("444@163.com").age(20).password("333").build();
-
boolean isSuccess = userService.save(user1);
-
System.out.println(isSuccess?"保存成功":"保存失败");
-
}
-
/**
-
* @Description 测试服务层批量保存
-
*/
-
@Test
-
public void test6(){
-
User user2 = User.builder().name("wangwu2").userName("laowang2").
-
email("444@163.com").age(20).password("333").build();
-
User user3 = User.builder().name("wangwu3").userName("laowang3").
-
email("444@163.com").age(20).password("333").build();
-
boolean isSuccess = userService.saveBatch(Arrays.asList(user2, user3));
-
System.out.println(isSuccess?"保存成功":"保存失败");
-
}
-
/**
-
* @Description 根据id删除操作
-
*/
-
@Test
-
public void test7(){
-
boolean isSuccess = userService.removeById(17l);
-
System.out.println(isSuccess?"保存成功":"保存失败");
-
}
-
/**
-
* @Description 根据条件批量删除
-
*/
-
@Test
-
public void test8(){
-
LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery(User.class);
-
wrapper.gt(User::getId,12)
-
.gt(User::getAge,20);
-
boolean remove = userService.remove(wrapper);
-
System.out.println(remove);
-
}
-
/**
-
* @Description 测试根据id更新数据
-
*/
-
@Test
-
public void test9(){
-
//UPDATE tb_user SET password=?, t_name=? WHERE id=?
-
User user2 = User.builder().name("wangwu2").password("333").id(3l).build();
-
boolean success = userService.updateById(user2);
-
System.out.println(success);
-
}
-
/**
-
* @Description 测试根据条件批量更新
-
*/
-
@Test
-
public void test10(){
-
LambdaUpdateWrapper<User> wrapper = Wrappers.lambdaUpdate(User.class);
-
//UPDATE tb_user SET age=? WHERE (id IN (?,?,?))
-
wrapper.in(User::getId,Arrays.asList(1l,3l,5l)).set(User::getAge,40);
-
boolean update = userService.update(wrapper);
-
System.out.println(userService);
-
}
-
@Test
-
public void test02(){
-
User user1 = User.builder().name("wangwu2").realName("laowang2").
-
email("444@163.com").age(20).password("333").build();
-
userService.saveOrUpdate(user1);//没有设置主键id就是save(新增操作)
-
User user2 = User.builder().id(user1.getId()).name("wangwu3").realName("laowang3").
-
email("444@163.com").age(20).password("333").build();
-
userService.saveOrUpdate(user2);//设置了主键id就是update(修改操作)
一.添加成员变量
-
/**
-
* 插入时给这个成员变量赋值
-
*/
-
@TableField(fill = FieldFill.INSERT)
-
private Date createTime;
-
/**
-
* 插入或者更新时给这个成员变量赋值
-
*/
-
@TableField(fill = FieldFill.INSERT_UPDATE)
-
private Date updateTime;
二.配置拦截器实现MetaObjectHandler
-
/**
-
* 一个拦截器,在数据准备插入数据库时进行拦截
-
*/
-
@Component
-
public class FillHandler implements MetaObjectHandler {
-
/**
-
* 在进行插入数据时进行拦截
-
* @param metaObject 封装了准备插入数据库的entity对象
-
*/
-
@Override
-
public void insertFill(MetaObject metaObject) {
-
//第一个参数时类的成员变量名
-
metaObject.setValue("createTime",new Date());
-
metaObject.setValue("updateTime",new Date());
-
}
-
/**
-
* 在进行更新数据时进行拦截
-
* @param metaObject 封装了准备更新数据库数据的entity对象
-
*/
-
@Override
-
public void updateFill(MetaObject metaObject) {
-
metaObject.setValue("updateTime",new Date());
-
}
-
}
三.测试
-
@Test
-
public void test02(){
-
User user=User.builder()
-
.realName("hah")
-
.email("153@qq.com")
-
.age(18)
-
.name("a")
-
.password("123")
-
.build();
-
int insert = userMapper.insert(user);
-
System.out.println(insert);
-
}
拦截到的数据,此时createTime和updateTime成员变量没有值
赋值后,再进行插入