框架 - mybatis plus(尚硅谷)
1、mybatis plus简介
-
MyBatis-Plus(简称MP),是一个MyBatis的增强工具包,只做增强不做改变。为简化开发工作、提高生产率而生。是一个由中国团队开发的工具包。
MyBatis-Plus提供了通用的mapper和service,可以在不编写任何SQL语句的情况下,快速的实现对单表的CRUD、批量、逻辑删除、分页等操作。
-
MP核心功能:代码生成器、通用CRUD、条件构造器
-
springboot集成mybatis-plus
-
需要导入依赖:其它依赖跟集成mybatis时一样
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.1</version> </dependency>需要注意的是,要根据springboot的版本来选择合适的mybatis_plus版本,否则会有问题。
-
编写配置:
# 只需要数据库配置 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver password: '' username: 'root' url: 'jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC' # 加入日志:加入以下默认使用的配置,就会使用mybatis默认使用的日志框架,实现日志记录。 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl -
之后就可以直接使用了
-
-
springboot中使用MP的相关配置示例
mybatis-plus: # 设置MyBatis-Plus的全局配置 global-config: db-config: # 设置实体类所对应的表的统一前缀 table-prefix: t_ # 也可以接着设置主键生成策略、字段映射规则等。也就是类似@TableName,@Fieldname等注解的配置 -
spring集成mybatis-plus
-
需要导入依赖:其它依赖跟集成mybatis时一样
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus</artifactId> <version>3.4.3</version> </dependency> -
先在spring中集成mybatis,在做其它操作
-
Mybatis-Plus 的集成非常简单,对于Spring,我们仅仅需要把Mybatis自带的MybatisSqlSessionFactoryBean替换为 MP 自带的即可。
-
2、通用CRUD
-
提出问题:假设我们已存在一张tbl employee表,且已有对应的实体类Employee。实现employee表的 CRUD操作我们需要做什么呢?
-
基于Mybatis:需要编写EmployeeMapper接口,并手动编写CRUD 方法,提供 EmployeeMapper.xml映射文件,并手动编写每个方法对应的SQL语句.
-
基于 MP:只需要创建EmployeeMapper接口,并继承 BaseMapper接口。这就是使用 MP需要完成的所有操作,甚至不需要创建SQL映射文件。
-
-
核心接口:
BaseMapper<T>,泛型指定的就是当前Mapper接口所操作的实体类类型。接口中定义了基本的CRUD操作、通过主键查询、分页查询等等。 -
基本的insert操作
-
定义dao层接口
// 这里BaseMapper的泛型表示Mapper所操作的实体类类型,之后会根据实体类类名去数据库中找对应的表。 @Repository("studentDao") public interface StudentDao extends BaseMapper<Student> {} -
实体类
/** * 会根据类名去数据库中找到表,这里如果类名跟对应的数据库表表名不一致的话, * 需要使用@TableName来指定表名映射。 */ @TableName("students") public class Student { private Integer sid; private String sname; private String cid; } -
然后直接使用StudentDao接口,调用insert方法就可以了
StudentDao studentDao = applicationContext.getBean("studentDao", StudentDao.class); Student student = new Student(); student.setCid("1001"); student.setSname("MP"); int insert = studentDao.insert(student); -
@TableName注解:MP默认使用实体类的类名去数据库中查找对应的表,这个注解可以用于指定实体类与数据库表的映射关系 -
@TableId注解:用于指定表主键和实体类字段的映射关系(value属性),以及配置表的主键的更新策略(type参数)。MP默认会将实体类中的id属性映射为表主键字段。下图为type的可选值,也就是可选的主键更新策略。![]()
-
数据库字段映射:通过实体类的属性名自动映射的,默认开启驼峰匹配等模糊匹配机制。同时也可以使用
@TableField注解来指定字段与数据库中表字段的映射关系。 -
获取新插入的数据的主键值(mybatis中也有类似的操作):MP会自动将主键值回显到实体类中,如上述的示例中,主键值就会被存到student对象中。
-
-
基本的update操作
-
首先依然是要定义dao层接口、以及对应要操作的实体类
-
然后直接使用StudentDao接口,调用
updateById(实体类对象)方法就可以了,没啥打不了的,这里不示例了哈,哈哈哈!!!需要注意的是:修改哪些表项是根据实体类的属性值是否为null来判断的,实体类的字段值为null则不修改对应的表项。
-
-
基本的select操作
-
2.X 版本有以下方法可以用来查询数据:
![]()
-
3.X版本不太一样了:大多数需要使用条件构造器来查询数据了
![]()
-
需要注意的是:这里面的分页查询实际上不是真实的分页查询,依然是查询全部然后再去做相关的操作,效率是不行的,一般分页查询的操作还是需要使用分页插件例如mybatis plus提供的,或者PageHelper插件。
-
-
基本的delete操作:通过id删除、通过Map集合构造删除条件、通过id列表批量删除数据
![]()
-
逻辑删除示例:
需要在表中添加一个字段,例如isDelete int,表示是否被删除,默认值为0表示未删除。然后在实体类中也定义相同的属性,并使用
@TableLogin标注,之后调用delete方法,就会自动变更为update操作,去修改isDelete的值。
-
-
MP启动注入SQL原理分析
略
-
如果mybatis_plus提供的操作无法满足需求,就需要自定义一些操作,其实就是使用原来的mybatis,建立mapper.xml文件,编写sql来实现。
-
以下示例是在springboot中实现的
-
首先需要配置mapper文件扫描:
# 如果不配置id话,默认的文件扫描路径也是这个。所以可以选择不配置,使用这个路径即可。 mybatis-plus: mapper-locations: /mapper/**/*.xml -
然后在/mapper路径下创建mapper.xml文件,然后像原始的mybatis那样去使用即可。
-
-
通用IService
-
IService <实体类>是 MyBatis-Plus 提供的一个通用 Service 层接口,它封装了常见的 CRUD 操作,包括插入、删除、查询和分页等。通过继承 IService 接口,可以快速实现对数据库的基本操作,同时保持代码的简洁性和可维护性。 -
ServiceImpl是MP提供的Iservice的实现类,实现了部分基本CRUD功能,可以直接使用。
-
-
IService使用方式:用于构建service层功能。
-
首先创建一个service接口,实现
IService接口,注意泛型是要操作的实体类对象。public interface CarService extends IService<Car> {} -
创建service接口的实现类,实现前面创建的service接口,并继承
ServiceImpl<E, T>类,注意第一个泛型E表示BaseMapper接口,第二个泛型表示要操作的实体类。@Service public class CarServiceImpl extends ServiceImpl<CarMapper, Car> implements CarService {} -
然后就可以直接使用service实现类来实现基本的CRUD操作了。
-
-
在java中,一些常量通常使用枚举来表示,例如性别的男和女。
-
假如数据库中,性别为int类型,1表示男,2表示女
-
在实体类中,性别字段的值使用枚举来表示
-
这个时候,如果要插入数据,直接使用insert方法插入数据,由于存在枚举类型的数据,插入之后会报错!!!
-
修改:
-
在枚举类中,使用
@EnumValue,将注解所标识的属性的值存储到数据库中@Getter public enum SexEnum { MALE(sex:1,sexName:"男"), FEMALE(sex:2, sexName:"女"); @EnumValue //将注解所标识的属性的值存储到数据库中 private Integer sex; private string sexName; } -
同时还需要在MP的全局配置中,加入枚举的扫描包配置
# 扫描通用枚举的包 type-enums-package: com.atguigu.mybatisplus.enums -
这样,就可以将枚举值存入到数据库中,不会报错。
-
-
3、条件构造器EntityWrapper
-
提出需求:分页查询 tbl employee表中,年龄在18~50之间性别为男且姓名为 xx 的所有用户,这时候我们该如何实现上述需求呢?
-
MyBatis : 需要在SQL 映射文件中编写带条件查询的SQL,并基于 PageHelper插件完成分页。实现以上一个简单的需求,往往需要我们做很多重复单调的工作。
-
MP:依旧不用编写SQL 语句,MP提供了功能强大的条件构造器EntityWrappert
-
-
EntityWrapper简介
![]()
-
Mybatis-Plus 通过
EntityWrapper(简称EW,MP封装的一个查询条件构造器)或者Condition(与EW类似)来让用户自由的构建查询条件,简单便捷,没有额外的负担,能够有效提高开发效率 -
实体包装器,主要用于处理sgl拼接,排序,实体参数查询等
-
注意:使用的是数据库字段,不是Java 属性!
-
-
使用示例:
// 分页查询 car表中,价格在0~500之间且类型为好车 的所有车 // 构造条件 QueryWrapper<Car> queryWrapper = new QueryWrapper<Car>().between("guide_price", 0, 500).eq("car_type", "好车"); // 查询数据:由于暂时没有分页插件,所以其实没有得到分页效果 // 加入分页插件之后,才能真正实现分页。 Page page = carDao.selectPage(new Page(2, 2), queryWrapper); List records = page.getRecords(); //获取查询到的数据由于暂时没有分页插件,所以其实没有得到分页效果。加入分页插件之后,才能真正实现分页。
-
其它的使用构造条件的查询或者修改、删除的操作也是类似的用法,此处略过,基本方法就是使用QueryWrapper构造查询条件,然后将QueryWrapper对象作为方法参数即可。
注意一个常用方法:
last(String sql); 作用是将参数sql拼接在sql语句的最后面。 -
条件优先级:体会以下例子
//将用户名中包含有a并且(年龄大于20或邮箱为null)的用户信息修改 QueryWrapper<User>queryWrapper = new QueryWrapper<>(); queryWrapper.like("user_name", "a") .and( //lambda中的条件优先执行 i->i.gt("age",20).or().isNull("email") ); user user = new User(); user.setName("小红"); user.setEmail("test@atguigu.com"); // 执行修改操作 int result = userMapper.update(user,queryWrapper); -
子查询条件
//select * from t_car where id in (select id from t_car where id>=50 and id <=100); QueryWrapper<Car> queryWrapper = new QueryWrapper<>(); // 构建子查询条件 queryWrapper.inSql("id","select id from t_car where id>=50 and id <=100"); List<Car> cars = carMapper.selectList(queryWrapper); System.out.println(cars); -
现实的开发中,前端传送过来的数据,需要进一步的判断是否满足条件,例如是否为空,长度是否合适等,这就需要使用条件构造器的另一批方法:
![]()
在带condition参数的方法中,对前端传送的数据进行判断即可
String username = ""; // 通过username查询数据 QueryWrapper<Account> queryWrapper = new QueryWrapper<>(); // 添加condition条件判断,判断username字段是否非空。 queryWrapper.eq(StringUtils.isNotBlank(username),"username",username); List<Account> accountsccount = carMapper.selectList(queryWrapper); -
LambdaUpdateWrapper和LambdaQueryWrapper,可以防止条件构造器中写字段名的时候写错,使用函数式接口来访问响应的字段![]()
-
Condition类可以实现与Wrapper一样的效果,用法也差不多啦
4、ActiveRecord(活动记录)
-
Active Record(活动记录),是一种领域模型模式,特点是一个模型类对应关系型数据库中的一个表,而模型类的一个实例对应表中的一行记录。
ActiveRecord一直广受动态语言(PHP、Ruby等)的喜爱,而Java作为准静态语言,对于 ActiveRecord往往只能感叹其优雅,所以MP也在 AR 道路上进行了一定的探索。
-
如何使用AR模式?仅仅需要让实体类继承
Model类且实现主键指定方法,即可开启AR 之旅 -
AR插入操作
-
创建实体类student:注意同样可以使用
@TableName等注解,如果需要的话,实现与之前一样的效果。@Data @AllArgsConstructor @NoArgsConstructor @TableName("students") public class Student extends Model<Student> { // TableId用于指定主键映射,以及指定主键的更新策略 private Integer sid; private String sname; private String cid; // 重写这个方法,指定哪个值为主键 @Override public Serializable pkVal() { return sid; } } -
插入操作:
Student student = new Student(null, "老二","1001"); boolean insert = student.insert(); System.out.println(insert);
-
-
查询操作、删除操作、修改操作类似,此处略。
-
AR 模式提供了一种更加便捷的方式实现CRUD操作,其本质还是调用的Mybatis 对应的方法,类似于语法糖。
4、代码生成器
-
代码生成器MP提供了大量的自定义设置,生成的代码完全能够满足各类型的需求。MP 的代码生成器和Mybatis MBG代码生成器的区别:
-
MP 的代码生成器都是基于java代码来生成。MBG基于xml文件进行代码生成。
-
MyBatis的代码生成器可生成:实体类、Mapper接口、Mapper 映射文件
-
MP 的代码生成器可生成:实体类(可以选择是否支持AR)、Mapper 接口、Mapper映射
文件、Service 层、Controller层.
-
-
注意:表及字段命名策略选择
-
在MP中,建议数据库表名和表字段名采用驼峰命名方式,如果采用下划线命名方式,需要开启全局下划线开关,如果表名字段名命名方式不一致使用注解指定,建议最好保持一致,有助于提高执行效率。
-
这么做的原因是为了避免在对应实体类时产生的性能损耗,这样字段不用做映射就能直接和实体类对应。当然如果项目里不用考虑这点性能损耗,那么你采用下滑线也是没问题的,只需要在生成代码时配置
dbColumnUnderline属性就可以
-
-
集成代码生成器需要的依赖
模板引擎:MP 的代码生成器默认使用的是Apache的Velocity模板,当然也可以更换为别的模板技术,例如freemarker。需要引入模板引擎依赖,此处使用Velocity模板。
<!-- 需要加入Apache Velocity的依赖--> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <version>2.0</version> </dependency> <!-- 加入 slf4j,查看日志输出信息--> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.7</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.7</version> </dependency> -
生成代码:MP2.X版本
-
代码生成器代码:
GlobalConfig config = new GlobalConfig(); //1.全局配置 GlobalConfig config = new GlobalConfig(); config.setActiveRecord(true) // 是否支持AR模式 .setAuthor("weiyunhui") // 作者 .setOutputDir("D:\\workspace_mp\\mp03\\src\\main\\java") // 生成路径 .setFile0verride(true) // 文件覆盖 .setIdType(IdType.AUTO) // 主键策略 //设置生成的servvice接口的名字的首字母是否为I,默认是IXxxxxService .setServiceName("%sService") .setBaseResultMap(true) .setBaseColumnList(true); //2.数据源配置 DataSourceConfig dsConfig = new DataSourceConfig(); dsConfig.setDbType(DbType.MYSQL) //设置数据库类型 .setDriverName("com.mysql.jdbc.Driver") .setUrl("jdbc:mysql://localhost:3306/mp") .setUsername("root") .setPassword("1234"); //3.策略配置 StrategyConfig stConfig = new StrategyConfig(); stConfig.setCapitalMode(true) //全局大写命名 .setDbColumnUnderline(true) //指定表名字段名是否使用下划线 .setNaming(NamingStrategy.underline_to_camnel) // 数据库表映射到实体的命名策略 .setTablePrefix("tb1_") .setInclude("tbl_employee"); //生成的表 //4.包名策略配置 PackageConfig pkConfig = new PackageConfig(); pkConfig.setParent("com.atguigu.mp") .setMapper("mapper") .setService("service") .setController("controller") .setEntity("beans") .setXml("mapper"); //5.整合配置 AutoGenerator ag = new AutoGenerator(); ag.setGlobalConfig(config) .setDataSource(dsConfig) .setStrategy(stConfig) .setPackageInfo(pkConfig); //6.执行 ag.execute(); -
生成了service层代码,可以看到EmployeeServiceImpl继承了ServiceImpl
-
在ServiceImpl中已经完成Mapper对象的注入,直接在EmployeeServiceImpl中进行使用
-
在ServiceImpl中也帮我们提供了常用的CRUD方法,基本的一些CRUD方法在Service中不需要我们自己定义。
-
-
-
多数据源的配置:包括数据库集群,不同数据库服务器,主从复制等。
略
5、插件扩展
-
插件机制
-
Mybatis 通过插件(Interceptor)可以做到拦截四大对象相关方法的执行,根据需求,完成相关数据的动态改变。
-
四大对象:
- Executor
- StatementHandler
- ParameterHandler
- ResultSetHandler
-
插件原理:
四大对象的每个对象在创建时,都会执行interceptorChain.pluginAll(),会经过每个插件对象的 plugin()方法,目的是为当前的四大对象创建代理。代理对象就可以拦截到四大对象相关方法的执行,因为要执行四大对象的方法需要经过代理,从而实现对操作的增强(例如对查询进行分页,就是一种增强 )
-
-
使用分页插件
-
首先需要将插件注册进来,有三种方式
-
方式一:在mybatis配置文件中配置
<plugins> <plugin interceptor="com.baomidou.mybatisplus.plugins.PaginationInterceptor"></plugin> </plugins> -
方式二:在spring配置文件中配置
<!--SqlSessionFactoryBean配置--> <bean class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean" id="factoryBean"> <!-- 添加插件--> <property name="plugins"> <list> <bean class="com.baomidou.mybatisplus.plugins.PaginationInterceptor"/> </list> </property> <!--注入mybatis核心配置文件路径--> <property name="configLocation" value="mybatis-config.xml"/> <!--指定别名包--> <property name="typeAliasesPackage" value="shh.pojo"/> <!--注入数据源--> <property name="dataSource" ref="dataSource"/> </bean> -
方式三:使用配置类注册组件,在配置类中加入以下配置(springboot项目)
@Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interieptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; }
理解:引入插件之后,插件就会去拦截四大对象,对四大对象进行代理,从而达到增强功能的目的。例如这里就是去代理Statament对象,增加分页的功能。
-
-
使用插件:直接调用selectPage方法,会发现成功实现了分页。
-
-
执行分析插件
-
SQL 执行分析拦截器,只支持MySQL5.63以上版本。该插件的作用是分析DELETE UPDATE 语句,防止小白+或者恶意进行DELETEUPDATE 全表操作。只建议在开发环境中使用,不建议在生产环境使用。
-
在插件的底层通过SQL语句分析命令:Explain分析当前的SQL语句,根据结果集中的Extra列来断定当前是否全表操作。
-
首先需要注册插件:
combaomidoumybatisplus plugins SqlExplainInterceptorw -
之后执行sql操作的时候,就会自动检测了。
-
-
性能分析插件.
-
性能分析拦截器,用于输出每条SQL语句项其执行时间,SQL性能执行分析,开发环境使用,超过指定时间,停止运行。有助于发现问题
-
注册插件:
com.baomidou.mybatisplus.plugins.Performanceinterceptor。可以指定超时时间等属性,用来限制sql执行。 -
之后执行sql操作,就会自动分析sql操作的执行时间,写在日志中。
-
-
乐观锁插件
-
如果想实现如下需求:当要更新一条记录的时候,希望这条记录没有被别人更新
-
乐观锁的实现原理:
-
取出记录时,获取当前version
-
更新时,带上这个version(2
-
执行更新时,set version =yourVersion+1 where version = yourVersion,如果 version 不对,就更新失败
-
-
@Version 用于注解实体字段,必须要有。
在实体类中必须有一个
version版本号属性,并使用@Version标注。数据库表中也需要有这个属性。 -
首先是注册插件:
com.baomidou.mybatisplus.plugins.OptimisticLockerInterceptor -
之后进行更新等操作的时候,就会自动加锁,也就是去判断version的值,从而保证并发安全。
-
6、自定义全局操作
-
根据Mybatisplus 的
AutoSalinjector可以自定义各种你想要的sgl,注入到全局中,相当于自定义Mybatisplus自动注入的方法。之前需要在xml 中进行配置的sQL 语句,现在通过扩展
AutoSqlinjector在加载mybatis环境时就注入。其实说白了就是mybatis-plus支持自定义一些sql操作,而不是只能使用它已经定义好的那些。
-
自定义全局操作示例(MP2.X版本):略过
-
在 Mapper接口中定义相关的CRUD 方法
-
扩展
AutoSqllnjector的inject方法,实现Mapper接口中方法要注入的 SQL -
在MP全局策略中,配置自定义注入器
-
-
自定义全局SQL注入器应用之---逻辑删除(MP2.X版本):略
7、公共字段自动填充
-
公共字段自动填充的作用:在进行插入或者更新操作的时候,有一些表字段没有插入或者修改数据,那么希望它被插入默认值,就可以使用公共字段自动填充功能。
-
元数据处理器接口:
com.baomidou.mybatisplus.mapper.MetaObjectHandler,实现数据填充的接口-
insertFill(MetaObject metaObject)方法:用于处理插入操作的字段填充
-
updateFill(MetaObject metaObject)方法;:用于处理更新操作的字段填充
-
metaobject:元对象,是Mybatis提供的一个用于更加方便,更加优雅的访问对象的属性,给对象的属性设置值的一个对象.还会用于包装对象.支持对object 、Map、Collection等对象进行包装。
-
本质上 metaObject获取对象的属性值或者是给对象的属性设置值,最终是要通过 Reflector反射机制获取到属性的对应方法的Invoker,最终invoke.
-
-
开发步骤
-
注解标注填充字段:需要在实体类中使用
@TableFile(fill = FieldFill.INSERT)来标注需要填充的字段,查看FieldFillpublic enum FieldFill{ DEFAULT(0,"默认不处理"), INSERT(1,"插入填充字段"), UPDATE(2,"更新填充字段"), INSERT_UPDATE(3,"插入和更新填充字段"); } -
自定义公共字段填充处理器:定义要填充的数据。其实就是定义一个类去继承
MetaObjectHandler接口,实现insertFill和updateFill方法即可。 -
MP全局注入自定义公共字段填充处理器
-
之后,对表进行更新和插入的时候,就会依据定义的填充规则,对字段数据进行填充
-








浙公网安备 33010602011771号