1. MyBatisPlus简介
1.1 MyBatisPlus
Mybatis-Plus(简称MP)是一个 Mybatis 的增强工具/框架,在 Mybatis 的基础上只做增强不做改变,为简化开发、提高效率而生。官方文档说的愿景是成为 Mybatis 最好的搭档,就像魂斗罗中的1P、2P,基友搭配,效率翻倍。
官方网站: https://mp.baomidou.com/
- 对MyBatis的进一步封装,是MyBatis的增强工具/框架。
- 在MyBatis的基础上只做增强不做改变,为简化开发,提高效率而生。
1.2 MyBatisPlus特性
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本 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 操作智能分析阻断,也可自定义拦截规则,预防误操作
1.3 架构原理
2. Mybatis Plus框架的搭建
2.1 搭建SSM开发环境
导入jar包
配置MyBatisPlus的工厂
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<!-- 配置注解包扫描位置 -->
<context:component-scan base-package="cn.zj.ssm"/>
<!-- 读取数据库配置 -->
<context:property-placeholder location="classpath:db.properties"/>
<!-- 配置连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="maxActive" value="${jdbc.maxActive}"/>
</bean>
<!-- 集成配置MyBatis -->
<!-- 创建MyBatis的工厂对象SqlSessionfactory -->
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<!-- 配置数据源 -->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 使用包扫描创建mapper包下面所有映射接口的代理对象-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 配置映射接口的包 -->
<property name="basePackage" value="cn.zj.ssm.mapper"/>
</bean>
<!-- 事务配置
1,配置事务管理器
2,配置事务通知
3,使用AOP将事务切入到Service层
-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 配置数据源 -->
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<!-- DQL 查询方法 -->
<tx:method name="select*" read-only="true" />
<!-- DML 增删改 -->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* cn.zj.ssm.service..*.*(..))"/>
</aop:config>
</beans>
数据库中创建测试表
** 创建t_student表并添加数据 **
CREATE TABLE `t_student` (
`sid` int(11) NOT NULL auto_increment,
`s_name` varchar(10) DEFAULT NULL,
`sage` int(11) DEFAULT NULL,
`sphone` varchar(11) DEFAULT NULL,
PRIMARY KEY (`sid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
在pojo包下创建student表的实体类
@TableName("t_student")
public class Student {
@TableId(value = "sid",type = IdType.AUTO)
private Integer sid;
@TableField("s_name")
private String sname;
private Integer sage;
private String sphone;
//补全get、set方法
}
在mapper层创建StudentMapper接口并继承BaseMapper接口
package cn.zj.ssm.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import cn.zj.ssm.pojo.Student;
public interface StudentMapper extends BaseMapper<Student> {
}
创建测试类进行测试
package cn.zj.ssm.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import cn.zj.ssm.mapper.StudentMapper;
import cn.zj.ssm.pojo.Student;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:springdao.xml")
public class StudentMapperTest {
@Autowired
private StudentMapper studentMapper;
@Test
public void testInsert() {
Student student = new Student(null, "小明", 18, "13552431632");
int row = studentMapper.insert(student);
System.out.println("row :"+row);
}
@Test
public void testDeleteById() {
int row = studentMapper.deleteById(1);
System.out.println("row :"+row);
}
@Test
public void testUpdateById() {
Student student = new Student(2, "丽丽", 19, "12321321312");
int row = studentMapper.updateById(student);
System.out.println("row :"+row);
}
@Test
public void testSelectById() {
Student sutdent = studentMapper.selectById(2);
System.out.println(sutdent);
}
}
2.2 Mybatis和Mybatis Plus的比较
- MybatisPlus包含了Mybatis的所有功能,也就说在MybatisPlus中我们仍然可以按照Mybatis的方式来完成数据库的操作(无侵入)。
- MybatisPlus的数据库操作的Mapper层的接口只需要继承BaseMapper接口,就自动的拥有了当前对应的表的基本的CRUE操作,无需声明接口方法及其xml文件,极大的提升了开发效率(MybatisPlus是通过实体类来逆向动态生成对应的表的基本的Sql语句)。
3. MyBatis Plus条件构造器Wrapper
3.1 Wrapper条件构造器介绍
问题
目前我们可以使用mp完成基本的增删改查操作,但是我们在进行数据操作时,很多时候Sql语句中的筛选条件是非常复杂的,比如or关键,>,<,模糊查询等,怎么办?
解决
mp提供了功能非常强大的Wrapper条件构造器
本质
条件构造器其实就是一个对象,以方法的形式提供了数据库操作的筛选关键字我们调用该对象,来拼接我们的筛选条件即可。
实现
QueryWrapper
使用
创建QueryWrapper对象,使用该对象中提供的对应的数据库操作的方法,来完成条件的拼接,QueryWrapper对象最终存储拼接好的Sql片段,将片段拼接在Sql语句中。
3.2 QueryWrapper条件参数说明
3.3 待条件查询的代码示例
@Test
public void testList() throws Exception {
QueryWrapper<Student> queryWrapper = new QueryWrapper<>();
queryWrapper.like("s_name", "哥");
queryWrapper.or();//多条件默认and管理,加上or变或者关系
queryWrapper.gt("sage", 18);
List<Student> students = studentMapper.selectList(queryWrapper);
for (Student student : students) {
System.out.println(student);
}
}
4. 日记输出配置
4.1 日志输出配置
<!-- 创建MyBatis的工厂对象SqlSessionfactory -->
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="globalConfig" ref="globalConfig"></property>
<!-- 配置数据源 -->
<property name="dataSource" ref="dataSource" />
<property name="configuration">
<!--配置日志输出-->
<bean class="com.baomidou.mybatisplus.core.MybatisConfiguration">
<property name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl"></property>
</bean>
</property>
</bean>
4.2 输出日志之后输出的效果
5. MyBatis Plus的分页查询
5.1 分页查询
问题:
对于传统的分页Sql语句,需要我们自己在Sql语句中使用limit关键字来实现分页查询。但是呢,在MybatisPlus中,Sql语句是动态生成的,那么如何完成数据的分页查询呢?
解决
使用分页插件
使用
在配置文件中配置分页插件,在代码中调用分页效果。
5.2 分页查询的配置
<!-- 创建MyBatis的工厂对象SqlSessionfactory -->
<bean id="sqlSessionFactory"
class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="globalConfig" ref="globalConfig"></property>
<!-- 配置数据源 -->
<property name="dataSource" ref="dataSource" />
<property name="configuration">
<!--配置日志输出-->
<bean class="com.baomidou.mybatisplus.core.MybatisConfiguration">
<property name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl"></property>
</bean>
</property>
<!-- 配置物理分页插件 -->
<property name="plugins">
<array>
<bean class="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor">
<!-- 配置方言 -->
<property name="dialectType" value="mysql"/>
</bean>
</array>
</property>
</bean>
5.3 分页查询的使用
@Test
public void testSelectPage() throws Exception {
IPage<Student> page = new Page<Student>(2,10);
QueryWrapper<Student> queryWrapper = new QueryWrapper<>();
queryWrapper.like("s_name", "a");
IPage<Student> studentPage = studentMapper.selectPage(page, queryWrapper);
List<Student> students = studentPage.getRecords();
System.out.println("当前页结果集 :" + students);
System.out.println("当前分页总页数 :" + studentPage.getPages());
System.out.println("当前总记录数 :" + studentPage.getTotal());
}
6. 自定义SQL使用MyBatis Plus的分页和条件查询
** 问题 **
一般开发中,可能有复杂的多表连接查询,开发者自定义SQL语句。自定义的SQL语句是否也可以使用MyBatisPlus的分页和条件呢?
** 解决 **
- 直接在对应的自定义SQL方法上面注入分页对象和条件对象
- 在自定义的SQL语句中加上 : ${ew.customSqlSegment} (官方文档语法)
- 运行过程中MP会自动加上条件和分页
6.1 MybatisPlus中获取自增的主键值
** 在Mybatis中需要使用 useGeneratedKeys,keyProperty,keyColumn 设置自增主键值的回返,在实体类对象中获取即可。在MybatisPlus中在进行数据新增时,在新增成功后,会自动的将自增的主键值返回到实体类对象中,前提是需要在实体类中使用@TableId表明主键字段,并且为自增类型。**
6.2 StudentMapper.java
public interface StudentMapper extends BaseMapper<Student> {
/**
* 条件查询
* @param wrapper 条件对象
* @return 多表的字段每一行直接返回Map集合,使用List集合封装每个Map
*/
@Select("select * from t_student left join t_class on t_student.cid = t_class.cid ${ew.customSqlSegment}")
List<Map<String,Object>> selectStudents(@Param(Constants.WRAPPER) Wrapper<Map<String,Object>> wrapper);
/**
* 带分页的插件查询
* @param ipage 分页对象
* @param wrapper 条件对象
* @return 分页对象(包含结果集,总记录,页数等...)
*/
@Select("select * from t_student left join t_class on t_student.cid = t_class.cid ${ew.customSqlSegment}")
IPage<Map<String,Object>> selectStudents1(IPage<Map<String,Object>> ipage, @Param(Constants.WRAPPER) Wrapper<Map<String,Object>> wrapper);
}
6.3 测试代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:springdao.xml")
public class MpTest {
@Autowired
private StudentMapper studentMapper;
//自定义查询
@Test
public void selectQueryWarpper(){
QueryWrapper<Map<String, Object>> warpper = new QueryWrapper<>();
//根据班级名称搜索
warpper.eq("cname","一年级(1)");
// warpper.like("s_name","明");
List<Map<String, Object>> students = studentMapper.selectStudents(warpper);
for (Map<String, Object> student : students) {
System.out.println("student :"+student);
}
}
//自定义分页+条件
@Test
public void selectPage(){
QueryWrapper<Map<String, Object>> warpper = new QueryWrapper<>();
//根据班级名称搜索
//warpper.eq("cname","一年级(1)");
// warpper.like("s_name","明");
IPage<Map<String, Object>> page = new Page<>(2,12);
IPage<Map<String, Object>> mapIPage = studentMapper.selectStudents1(page, warpper);
System.out.println("当前页结果集 :"+mapIPage.getRecords());
System.out.println("总记录:"+mapIPage.getTotal());
System.out.println("总页数:"+mapIPage.getPages());
}
7. MyBatis Plus的常用注解
问题
在使用MybatisPlus后,我们不用再声明Sql语句了,只需要我们的Mapper接口继承BaseMapper接口即可拥有对应的CRUD操作。通过我们之前的学习我们发现,MyBatisPlus其实在根据我们的实体类来动态的生成对象的Sql语句默认会按照类名即是对应的表的表名,属性名即是对应的表的字段名。但是如果实体类名和表名不一致,或者属性名和字段名不一致怎么办?
解决
在实体类上使用注解表名对应的映射关系。
注意
建议在开发时尽量保证实体类和表之间的对应关系是相同的。这样就不用声明注解。
8. MyBatis Plus的全局配置策略
问题
假如我们每个实体类和数据库中的表名都不一致,表的格式都是t_表名类名呢?没有t_字符,比如t_student表和Student类。这样每个实体类上我们都要使用@TableName注解来表名类和表的映射关系,过于麻烦,怎么办?
解决
使用MP的全局配置策略。GlobalConfig
作用
配置表和类名映射关系的前缀,配置全局主键自增 。
9. Active Record
** 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作。**
9.1 AR模式和MP的Mapper模式的比较
原有MP模式
- 创建项目完成Spring和MP的集成
- 创建数据库表对应的实体类
- 创建mapper接口并继承BaseMapper接口
- 从Spring容器中获取Mapper接口的实例化对象完成数据库操作
- 描述:通过以上流程,MP的操作模式较于原有Mybatis的数据库操作流程没有任何变化,只是我们在编写代码的时候不用在mapper层声明Sql语句或者XML文件了,提升开发效率。
MP的AR模式
- 创建项目完成Spring和MP的集成
- 创建数据库表对应的实体类,继续Model类
- 在实体类中覆写pkVal方法.
- 创建Mapper接口并继承BaseMapper接口
- 创建Spring对象,让Spring容器完成对Mapper层的实例化扫描
- 创建实体类对象,直接调用实体类从Model中继承的数据库方法完成数据库操作。
流程比较分析
** MP的AR模式其实底层仍然使用的是Mapper层在完成数据库操作。只不过由我们自己调用Mappe对象操作数据库,变成了通过实体类对象来调用Mapper完成数据库操作。从代码的物理视图上我们是看不到实体类调用Mapper的过程的。也就说,本质上仍然是Mapper层在操作数据库。 **
9.2 AR模式的特点
** AR模式较于传统的MP模式操作数据库,在代码体系中,我们不用在获取Mapper对象,然后再将实体类传入给mapper层完成数据库操作,直接使用实体类即可完成操作。提升开发效率。**
9.3 AR模式的使用代码实例
Student.java
Data
@NoArgsConstructor
@AllArgsConstructor
public class Student extends Model<Student> {
@TableId(value = "sid")
private Integer sid;
@TableField("s_name")
private String sname;
private Integer sage;
private String sphone;
private Integer cid;
//声明主键,返回主键属性
@Override
protected Serializable pkVal() {
return super.pkVal();
}
}
测试代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:springdao.xml")
public class StudentMapperTest {
//增
@Test
public void testInsert() {
Student student = new Student(null, "xiaoxiao", 17, "1222222", 1);
boolean insertFlag = student.insertOrUpdate();
System.out.println(insertFlag);
System.out.println(student);
}
//删:根据id删
@Test
public void testDelete() {
Student student = new Student();
student.setSid(5);
boolean deleteFlag = student.deleteById();
System.out.println(deleteFlag);
}
//改:根据id改
@Test
public void update() {
Student student = new Student(7, "xiaoxiao", 17, "1222222", 1);
boolean updateFlag = student.insertOrUpdate();
System.out.println(updateFlag);
System.out.println(student);
}
//查:根据id查
@Test
public void testSelectById() {
Student student = new Student();
student.setSid(2);
Student student1 = student.selectById();
System.out.println(student1);
}
//查:根据条件查
@Test
public void testSelectByCondition() {
Student student = new Student();
QueryWrapper<Student> wrapper = new QueryWrapper<>();
wrapper.eq("sage",20).or().like("s_name","玉");
List<Student> students = student.selectList(wrapper);
System.out.println(students);
}
/**
* 分页查询
*/
@Test
public void testSelectPage() {
Student student = new Student();
QueryWrapper<Student> wrapper = new QueryWrapper<>();
wrapper.eq("sage",18).or().like("s_name","玉");
Page<Student> page = new Page<>(2,10);
Page<Student> studentPage = student.selectPage(page, wrapper);
List<Student> records = studentPage.getRecords();
System.out.println("当前页结果集:" + records);
System.out.println("当前分页数:" + studentPage.getPages());
System.out.println("当前总记录数:" + studentPage.getTotal());
}
}
10. AutoGenerator代码生成器
10.1 代码生成器
问题
** 目前我们在开发SSM项目的时候,会先搭建一个SSM的开发环境。我们会根据数据库的表在pojo包中创建对应的实体类,而且可以不关心项目功能的同时,在mapper层对数据库的表的基础增删改查功能提前实现,同理,在service层可以将基础的业务代码提前声明。然后再根据项目功能完成对应的复杂操作。而整个过程需要程序员手动完成搭建,效率低下。 **
解决
** 创建一个代码生成类,调用该类的对象,并将对应的数据库表作为参数传递进入以及一些生成代码的其他要求也传递给该对象,让该对象帮助我们生成基础的开发代码。
实现:MP的代码生成器 **
作用
** 根据数据库中的表动态的生成表对应的mapper,service,pojo,controller层的基础代码,提升开发效率。**
10.2 MP代码生成器的使用
导入代码生成器相关jar包
使用生成器生成代码
@Test
public void testGenerator() throws Exception {
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置策略
GlobalConfig gc = new GlobalConfig();
String path = System.getProperty("user.dir");// 动态获取当前项目的路径
System.out.println(path);
gc.setFileOverride(false);// 是否覆盖同名文件,默认是false
gc.setActiveRecord(false);// 不需要ActiveRecord特性的请改为false
gc.setEnableCache(false);// XML 二级缓存
gc.setBaseResultMap(false);// XML ResultMap
gc.setBaseColumnList(false);// XML columList
gc.setOutputDir(path + "/src/main/java");
gc.setIdType(IdType.AUTO);// 设置主键策略
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/mybatisplus?useUnicode=true&useSSL=false&characterEncoding=utf8");
// dsc.setSchemaName("public");
dsc.setDriverName("com.mysql.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("root");
// 包配置
PackageConfig pc = new PackageConfig();
pc.setParent("cn.sxt").setMapper("mapper").setService("service").setController("controller")
.setEntity("pojo").setXml("mapper");
// 策略配置
StrategyConfig stConfig = new StrategyConfig();
stConfig.setCapitalMode(true) // 全局大写命名
.setNaming(NamingStrategy.underline_to_camel) // 数据库表映射到实体的命名策略
.setTablePrefix("t_").setInclude("t_student","t_class"); // 生成的表,多个表继续传递即可,String类型的可变参数
// 将策略配置对象集成到代码生成器中
mpg.setGlobalConfig(gc);
mpg.setDataSource(dsc);
mpg.setPackageInfo(pc);
mpg.setStrategy(stConfig);
// 执行生成
mpg.execute();
}
11. LomBok和MyBatis X
11.1 Lombok
** 是一个可以通过简单的注解的形式来帮助我们简化消除一些必须有但显得很臃肿的 Java 代码的工具,简单来说,比如我们新建了一个类,然后在其中写了几个属性,然后通常情况下我们需要手动去建立getter和setter方法啊,构造函数啊之类的,lombok的作用就是为了省去我们手动创建这些代码的麻烦,它能够在我们编译源码的时候自动帮我们生成这些方法LmoBok。
常用注解如下: **
11.2 MyBatis X
** MybatisX 是一款基于 IDEA 的快速开发插件,为效率而生。安装方法:打开 IDEA,进入 File -> Settings -> Plugins -> Browse Repositories,输入 mybatisx 搜索并安装。
功能特点:Java与XML调回跳转、Mapper方法自动生成XML等。**