简介
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
特性
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本 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 操作智能分析阻断,也可自定义拦截规则,预防误操作
入门 HelloWorld
配置
-
创建表
-- 创建库 CREATE DATABASE mp; -- 使用库 USE mp; -- 创建表 CREATE TABLE tbl_employee( id INT(11) PRIMARY KEY AUTO_INCREMENT, last_name VARCHAR(50), email VARCHAR(50), gender CHAR(1), age INT ); INSERT INTO tbl_employee(last_name,email,gender,age) VALUES('Tom','tom@atguigu.com',1,22); INSERT INTO tbl_employee(last_name,email,gender,age) VALUES('Jerry','jerry@atguigu.com',0,25); INSERT INTO tbl_employee(last_name,email,gender,age) VALUES('Black','black@atguigu.com',1,30); INSERT INTO tbl_employee(last_name,email,gender,age) VALUES('White','white@atguigu.com',0,35); SELECT * FROM tbl_employee; EXPLAIN DELETE FROM tbl_employee WHERE id = 100 CREATE TABLE tbl_user( id INT(11) PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR(50), logic_flag INT(11) )
-
导入maven坐标
<dependencies> <!-- mybatis-plus依赖 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus</artifactId> <version>2.3</version> </dependency> <!-- junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <!-- log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <!-- c3p0 --> <dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.2</version> </dependency> <!-- mysql --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.32</version> </dependency> <!-- spring --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> </dependencies>
-
配置Spring核心配置文件和Mybatis核心配置文件
<!-- 数据源 --> <context:property-placeholder location="classpath:jdbcConfig.properties"/> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driver}"/> <property name="jdbcUrl" value="${jdbc.url}"/> <property name="user" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <!-- 事务管理器 --> <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 基于注解的事务管理 --> <tx:annotation-driven transaction-manager="dataSourceTransactionManager"/> <!-- 配置 SqlSessionFactoryBean Mybatis提供的:org.mybatis.spring.SqlSessionFactoryBean Mybatis-Plus提供的:com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean --> <bean id="sqlSessionFactoryBean" class="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean"> <!-- 数据源 --> <property name="dataSource" ref="dataSource"></property> <property name="configLocation" value="classpath:SqlMapConfig.xml"></property> <!-- 别名处理 --> <property name="typeAliasesPackage" value="com.it.domain"></property> </bean> <!-- 配置 mybatis 扫描 mapper 接口的路径 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.it.dao"></property> </bean>
<configuration> </configuration>
-
编写实体类
public class Employee { private Integer id; private String lastName; private String email; private Integer gender; private Integer age; }
-
编写Mapper类(继承BaseMapper接口,泛型是实体类)
/** * 基于MP:让XXXDao接口继承BaseMapper接口即可 */ public interface EmployeeDao extends BaseMapper<Employee> { }
通用CRUD
插入操作
insert方法
//初始化一个Employee对象
Employee employee = new Employee();
//employee.setLastName("奇奇");
employee.setEmail("qiqi@it.com");
employee.setGender(0);
employee.setAge(20);
//插入到数据库
Integer result = employeeDao.insert(employee);
此时会报一个错误
org.apache.ibatis.reflection.ReflectionException: Could not set property 'id' of 'class com.it.domain.Employee' with value '1296012965098635266' Cause: java.lang.IllegalArgumentException: argument type mismatch
原因:是因为MybatisPlus主键策略 (默认 ID_WORKER)要使用(ID_AUTO)的主键策略
解决方法:
-
在映射表中主键的属性上加上@TableId注解
/** * @TableId: * value:指定表中的主键列的列名,如果实体类中属性名和列名一致,可以省略不写 * type:指定主键策略 */ @TableId(type = IdType.AUTO) private Integer id;
-
在Spring核心配置文件中定义MybatisPlus的全局策略配置,并且MybatisSqlSessionFactoryBean中配置
<!-- 定义MybatisPlus的全局策略配置 --> <bean id="globalConfiguration" class="com.baomidou.mybatisplus.entity.GlobalConfiguration"> <!-- 全局的主键策略 --> <property name="idType" value="0"></property> </bean>
<bean id="sqlSessionFactoryBean" class="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean"> <!-- 数据源 --> <property name="dataSource" ref="dataSource"></property> <property name="configLocation" value="classpath:SqlMapConfig.xml"></property> <!-- 别名处理 --> <property name="typeAliasesPackage" value="com.it.domain"></property> <!-- 注入全局MP配置 --> <property name="globalConfig" ref="globalConfiguration"></property> </bean>
再次执行插入操作时又会报新的错误:
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'mp.employee' doesn't exist
不存在employee这张表
原因:是因为直接将实体类名当作表名
解决:
-
在实体类上添加@TableName注解 并指定表名
@TableName(value = "tbl_employee") public class Employee {
-
在定义MybatisPlus的全局策略配置加入表前缀策略配置
<!-- 定义MybatisPlus的全局策略配置 --> <bean id="globalConfiguration" class="com.baomidou.mybatisplus.entity.GlobalConfiguration"> <!-- 在2.3版本后,dbColumnUnderline默认值就是true --> <property name="dbColumnUnderline" value="true"></property> <!-- 全局的主键策略 --> <property name="idType" value="0"></property> <!-- 全局的表前缀策略配置 --> <property name="tablePrefix" value="tbl_"></property> </bean>
对于MybatisPlus的全局策略配置中dbColumnUnderline是设置是否驼峰命名,在2.3版本后,dbColumnUnderline默认值就是true
insertAllColumn方法
//初始化一个Employee对象
Employee employee = new Employee();
//employee.setLastName("奇奇");
employee.setEmail("qiqi@it.com");
employee.setGender(0);
employee.setAge(20);
//插入到数据库
Integer result = employeeDao.insertAllColumn(employee);
该方法会将实体类所有的属性进行插入,若实体类中存在于数据库表中不对应的属性 会报错
解决:在实体类属性上添加@TableField注解
/**
* @TableField:
* value:指定表中的列名
* exist:判断表中是否存在列名与其一致,不存在设置为false则不添加
*/
@TableField(value = "last_name",exist = false)
private String lastName;
insert与insertAllColumn比较
insert方法:在插入时,会根据实体类的每个属性进行非空判断,只有非空的属性对应的字段才会出现在sql语句中
insertAllColumn方法:在插入时,不管属性是否非空,属性对应的字段都会出现在sql语句中
注意:MybatisPlus支持主键自增的数据库插入数据获取主键值
//获取数据在数据库的主键值
Integer id = employee.getId();
System.out.println("id:"+id);
更新操作
updateById方法
//初始化一个Employee对象
Employee employee = new Employee();
employee.setId(5);
//employee.setLastName("怪怪");
employee.setEmail("guaiguai@it.com");
employee.setGender(1);
employee.setAge(27);
//修改数据
Integer result = employeeDao.updateById(employee);
updateAllColumnById方法
//初始化一个Employee对象
Employee employee = new Employee();
employee.setId(5);
//employee.setLastName("怪怪");
employee.setEmail("guaiguai@it.com");
employee.setGender(1);
employee.setAge(27);
//修改数据
Integer result = employeeDao.updateAllColumnById(employee);
查询操作
selectById方法
//1.通过id查询
Employee result = employeeDao.selectById(5);
selectOne方法
//2.通过多个列进行查询
Employee employee = new Employee();
employee.setAge(20);
employee.setId(1);
Employee result = employeeDao.selectOne(employee);
selectBatchIds方法
//3.通过多个id进行查询
List<Integer> list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
List<Employee> employees = employeeDao.selectBatchIds(list);
selectByMap方法
//4.通过Map封装条件查询 map集合中key值要为数据库的列名
Map<String,Object> map = new HashMap<String, Object>();
map.put("last_name","Tom");
map.put("gender",1);
List<Employee> employees = employeeDao.selectByMap(map);
selectPage方法
该方法的第二个参数下面会说,此时可以设为null
//5.分页查询
List<Employee> employees = employeeDao.selectPage(new Page(2, 2), null);
System.out.println(employees);
删除操作
deleteById方法
//1.根据id进行删除
Integer result = employeeDao.deleteById(5);
System.out.println(result);
deleteByMap方法
//2.根据条件进行删除
Map<String,Object> map = new HashMap<String, Object>();
map.put("last_name","奇奇");
map.put("email","qiqi@it.com");
Integer result = employeeDao.deleteByMap(map);
System.out.println(result);
deleteBatchIds方法
//3.批量删除
List<Integer> list = new ArrayList<Integer>();
list.add(15);
list.add(16);
Integer result = employeeDao.deleteBatchIds(list);
System.out.println(result);
MP 启动注入 SQL 原理分析
xxxMapper 继承了 BaseMapper<T>, BaseMapper 中提供了通用的 CRUD 方法,
方法来源于 BaseMapper, 有方法就必须有 SQL,
因为 MyBatis 最终还是需要通过 SQL 语句操作数据.
通过现象看到本质
- employeeMapper 的本质 org.apache.ibatis.binding.MapperProxy
- MapperProxy 中 sqlSession –>SqlSessionFactory
- SqlSessionFacotry 中 → Configuration→ MappedStatements
每一个 mappedStatement 都表示 Mapper 接口中的一个方法与 Mapper 映射文件中的一个 SQL。
MP 在启动就会挨个分析 xxxMapper 中的方法,并且将对应的 SQL 语句处理好,保存到 configuration 对象中的 mappedStatements 中 - 本质:
- Configuration: MyBatis 或者 MP 全局配置对象
- MappedStatement:一个 MappedStatement 对象对应 Mapper 配置文件中的一个
select/update/insert/delete 节点,主要描述的是一条 SQL 语句 - SqlMethod : 枚举对象 ,MP 支持的 SQL 方法
- TableInfo:数据库表反射信息 ,可以获取到数据库表相关的信息
- SqlSource: SQL 语句处理对象
- MapperBuilderAssistant: 用于缓存、SQL 参数、查询方剂结果集处理等.通过 MapperBuilderAssistant 将每一个 mappedStatement添加到 configuration 中的 mappedstatements 中
条件构造器 EntityWrapper
简介
- Mybatis-Plus 通过 EntityWrapper(简称 EW,MP 封装的一个查询条件构造器)或者
Condition(与 EW 类似) 来让用户自由的构建查询条件,简单便捷,没有额外的负担,
能够有效提高开发效率 - 实体包装器,主要用于处理 sql 拼接,排序,实体参数查询等
- 注意: 使用的是数据库字段,不是 Java 属性!
- 参数
带条件的查询
/**
* 条件构造器 分页查询
*/
@Test
public void testEntityWrapperSelect(){
//在tbl_employee表中分页查询gender为1且age在20到30之间的数据
List<Employee> employees = employeeDao.selectPage(new Page<Employee>(1,1),
new EntityWrapper<Employee>()
.eq("gender", 1)
.between("age", 20, 30)
);
System.out.println(employees);
}
/**
* 条件构造器 查询操作
*/
@Test
public void testSelectList(){
//查询tbl_employee表中,性别为女或邮箱包含"b"
List<Employee> employees = employeeDao.selectList(
new EntityWrapper<Employee>()
.eq("gender", 0)
.or()
.like("email", "b")
);
System.out.println(employees);
}
带条件的修改
/**
* 条件构造器 修改操作
*/
@Test
public void testEntityWrapperUpdate(){
//将性别为女且年龄小于30的姓名修改为Tom
Employee employee = new Employee();
employee.setLastName("Tom");
Integer result = employeeDao.update(
employee,
new EntityWrapper<Employee>()
.lt("age", 30)
.eq("gender", 0)
);
System.out.println(result);
}
带条件的删除
/**
* 条件构造器 删除操作
*/
@Test
public void testEntityWrapperDelete(){
//删除姓名为Tom且性别为女的数据
Integer result = employeeDao.delete(
new EntityWrapper<Employee>()
.eq("last_name", "Tom")
.eq("gender", 0)
);
System.out.println(result);
}
使用 Condition 的方式打开如上需求
List<Employee> userListCondition = employeeMapper.selectPage(
new Page<Employee>(2,3),
Condition.create().
eq("gender", 1).
eq("last_name", "MyBatisPlus").
between("age", 18, 50));
ActiveRecord(活动记录)
简介
Active Record(活动记录),是一种领域模型模式,特点是一个模型类对应关系型数据库中的
一个表,而模型类的一个实例对应表中的一行记录。
ActiveRecord 一直广受动态语言( PHP 、 Ruby 等)的喜爱,而 Java 作为准静态语言,
对于 ActiveRecord 往往只能感叹其优雅,所以 MP 也在 AR 道路上进行了一定的探索
如何使用 AR 模式
仅仅需要让实体类继承 Model 类且实现主键指定方法,即可开启 AR 之旅
@TableName("tbl_employee")
public class Employee extends Model<Employee>{
// .. fields
// .. getter and setter
@Override
protected Serializable pkVal() {
return this.id;
}
插入操作
/**
* AR 插入操作
*/
@Test
public void testARInsert() {
Employee employee = new Employee();
employee.setLastName("宋老师");
employee.setEmail("sls@atguigu.com");
employee.setGender(1);
employee.setAge(35);
boolean result = employee.insert();
System.out.println("result:" +result );
}
修改操作
/**
* AR 修改操作
*/
@Test
public void testARUpdate() {
Employee employee = new Employee();
employee.setId(20);
employee.setLastName("宋老湿");
employee.setEmail("sls@atguigu.com");
employee.setGender(1);
employee.setAge(36);
boolean result = employee.updateById();
System.out.println("result:" +result );
}
查询操作
/**
* AR 查询操作
*/
@Test
public void testARSelect() {
Employee employee = new Employee();
//第一种用法
Employee result = employee.selectById(14);
//第二种用法
employee.setId(14);
Employee result = employee.selectById();
System.out.println(result );
}
List<Employee> emps = employee.selectAll();
System.out.println(emps);
List<Employee > emps= employee.selectList(new EntityWrapper<Employee>().like("last_name", "老师"));
System.out.println(emps);
Integer result = employee.selectCount(new EntityWrapper<Employee>().eq("gender", 0));
System.out.println("result: " +result );
删除操作
/**
* AR 删除操作
*
* 注意: 删除不存在的数据 逻辑上也是属于成功的.
*/
@Test
public void testARDelete() {
Employee employee = new Employee();
boolean result = employee.delete(new EntityWrapper<Employee>().like("last_name", "小"));
System.out.println(result );
}
boolean result = employee.deleteById(2);
employee.setId(2);
boolean result = employee.deleteById();
System.out.println("result:" +result );
分页复杂操作
/**
* AR 分页复杂操作
*/
@Test
public void testARPage() {
Employee employee = new Employee();
Page<Employee> page = employee.selectPage(new Page<>(1, 1),
new EntityWrapper<Employee>().like("last_name", "老"));
List<Employee> emps = page.getRecords();
System.out.println(emps);
}
AR 小结
- AR 模式提供了一种更加便捷的方式实现 CRUD 操作,其本质还是调用的 Mybatis 对应的方法,类似于语法糖
语法糖是指计算机语言中添加的某种语法,这种语法对原本语言的功能并没有影响.
可以更方便开发者使用,可以避免出错的机会,让程序可读性更好. - 到此,我们简单领略了 Mybatis-Plus 的魅力与高效率,值得注意的一点是:
我们提供了强大的代码生成器,可以快速生成各类代码,真正的做到了即开即用
代码生成器
- MP 提供了大量的自定义设置,生成的代码完全能够满足各类型的需求
- MP 的代码生成器 和 Mybatis MBG 代码生成器:
MP 的代码生成器都是基于 java 代码来生成。MBG 基于 xml 文件进行代码生成
MyBatis 的代码生成器可生成: 实体类、Mapper 接口、Mapper 映射文件
MP 的代码生成器可生成: 实体类(可以选择是否支持 AR)、Mapper 接口、Mapper 映射文件、 Service 层、Controller 层. - 表及字段命名策略选择
在 MP 中,我们建议数据库表名 和 表字段名采用驼峰命名方式, 如果采用下划线命名方式 请开启全局下划线开关,如果表名字段名命名方式不一致请注解指定,我们建议最好保持一致。
这么做的原因是为了避免在对应实体类时产生的性能损耗,这样字段不用做映射就能直接和实体类对应。
当然如果项目里不用考虑这点性能损耗,那么你采用下滑线也是没问题的,只需要在生成代码时配置 dbColumnUnderline 属性就可以
代码生成器依赖
1 MP 的代码生成器默认使用的是 Apache 的 Velocity 模板
2 加入 slf4j ,查看日志输出信息
3 生成controller时需要的依赖
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.0</version>
</dependency>
<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>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
MP 代码生成器示例代码
//1.全局配置
GlobalConfig config = new GlobalConfig();
config.setActiveRecord(true) //是否支持AR模式
.setAuthor("wyc") //作者
.setOutputDir("C:\\Users\\11\\IdeaProjects\\study_mybatisplus\\src\\main\\java") //生成路径
.setFileOverride(true) //文件是否覆盖
.setServiceName("%sService") //设置生成的Service接口的首字母是否为I
.setBaseResultMap(true)
.setBaseColumnList(true);
//2.数据源配置
DataSourceConfig dataSourceConfig = new DataSourceConfig();
dataSourceConfig.setDbType(DbType.MYSQL) //设置数据库类型
.setDriverName("com.mysql.jdbc.Driver")
.setUrl("jdbc:mysql:///mp2")
.setUsername("root")
.setPassword("123456");
//3.策略配置
StrategyConfig strategyConfig = new StrategyConfig();
strategyConfig.setCapitalMode(true) //全局大写
.setDbColumnUnderline(true) //指定表名字段名是否用下划线
.setNaming(NamingStrategy.underline_to_camel) //数据库表映射到实体的命名策略
.setTablePrefix("tbl_")
.setInclude("tbl_dept"); //生成的表
//4.包名策略配置
PackageConfig packageConfig = new PackageConfig();
packageConfig.setParent("com.it")
.setMapper("dao")
.setService("service")
.setController("controller")
.setEntity("domain")
.setXml("dao");
//5.整合配置
AutoGenerator autoGenerator = new AutoGenerator();
autoGenerator.setGlobalConfig(config)
.setDataSource(dataSourceConfig)
.setStrategy(strategyConfig)
.setPackageInfo(packageConfig);
//6.执行
autoGenerator.execute();
ServiceImpl 说明
EmployeeServiceImpl 继承了 ServiceImpl 类,mybatis-plus 通过这种方式为我们注入了 EmployeeMapper,这样可以使用 service 层默认为我们提供的很多方法,也可以调用我们自己在 dao 层编写的操作数据库的方法