typora-copy-images-to: Mybatis Plus
typora-root-url: Mybatis Plus
Mybatis Plus插件使用
一 介绍
1.1 MyBatis Plus简介
MyBatis-Plus(简称 MP),是一个 MyBatis 的增强工具包,**只做增强不做改变. 发工作、提高生产率而生。**
我们的愿景是成为 Mybatis 最好的搭档,就像 魂斗罗 中的 1P、2P,基友搭配,效率翻倍。(来自官方文档)
1.2 MyBatis Plus的下载
官方地址:
http://mp.baomidou.com
代码发布地址:
Github:
https://github.com/baomidou/mybatis-plus
Gitee:
https://gitee.com/baomidou/mybatis-plus
文档发布地址:
http://mp.baomidou.com/
1.3 知识准备
我们将通过一个简单的 Demo 来阐述 MyBatis-Plus 的强大功能,在此之前,我们假设您已经对以下知识掌握:
Mybatis
Spring
Maven
二 环境搭建
2.1创建测试表
现有一张Employee表,其表结构如下:
其对应的数据库 Schema 脚本如下:
-- 创建库
CREATE DATABASE mpdb;
-- 使用库
USE mpdb;
-- 创建表
CREATE TABLE employee(
id INT(11) PRIMARY KEY AUTO_INCREMENT,
last_name VARCHAR(50),
email VARCHAR(50),
gender CHAR(1),
age int
);
INSERT INTO employee(last_name,email,gender,age) VALUES('Tom','tom@163.com',1,22);
INSERT INTO employee(last_name,email,gender,age) VALUES('Jerry','jerry@163.com',0,25);
INSERT INTO employee(last_name,email,gender,age) VALUES('Black','black@163.com',1,30);
INSERT INTO employee(last_name,email,gender,age) VALUES('White','white@163.com',0,35);
2.2 创建javaBean
public class Employee {
private Integer id;
private String lastName;
private String email;
private Integer gender;
private Integer age;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Employee(Integer id, String lastName, String email, Integer gender, Integer age) {
super();
this.id = id;
this.lastName = lastName;
this.email = email;
this.gender = gender;
this.age = age;
}
public Employee() {
super();
}
@Override
public String toString() {
return "Employee [id=" + id + ", lastName=" + lastName + ", email=" + email + ", gender=" + gender + ", age=" + age + "]";
}
}
2.3依赖配置
-
在 pom.xml 中加入对 MP、Spring、连接池、Junit、Mysql 驱动等依赖
<dependencies>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>2.3</version>
</dependency>
<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.37</version>
</dependency>
<!-- spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>4.3.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.8.RELEASE</version>
</dependency>
</dependencies>
特别说明: Mybatis及Mybatis-Spring 依赖请勿加入项目配置,以免引起版本冲突!!!
Mybatis-Plus 会自动帮你维护!
-
加入 MyBatis 的全局配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
</configuration>
这也可以不用,如果用spring整合可以直接在spring配置文件中配置相应的内容
-
加入 log4j.properties
log4j.rootLogger=INFO,Console,File
#\u5B9A\u4E49\u65E5\u5FD7\u8F93\u51FA\u76EE\u7684\u5730\u4E3A\u63A7\u5236\u53F0
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.Target=System.out
#\u53EF\u4EE5\u7075\u6D3B\u5730\u6307\u5B9A\u65E5\u5FD7\u8F93\u51FA\u683C\u5F0F\uFF0C\u4E0B\u9762\u4E00\u884C\u662F\u6307\u5B9A\u5177\u4F53\u7684\u683C\u5F0F
log4j.appender.Console.layout = org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=[%c] - %m%n
#\u6587\u4EF6\u5927\u5C0F\u5230\u8FBE\u6307\u5B9A\u5C3A\u5BF8\u7684\u65F6\u5019\u4EA7\u751F\u4E00\u4E2A\u65B0\u7684\u6587\u4EF6
log4j.appender.File = org.apache.log4j.RollingFileAppender
#\u6307\u5B9A\u8F93\u51FA\u76EE\u5F55
log4j.appender.File.File = logs/ssm.log
#\u5B9A\u4E49\u6587\u4EF6\u6700\u5927\u5927\u5C0F
log4j.appender.File.MaxFileSize = 10MB
# \u8F93\u51FA\u6240\u4EE5\u65E5\u5FD7\uFF0C\u5982\u679C\u6362\u6210DEBUG\u8868\u793A\u8F93\u51FADEBUG\u4EE5\u4E0A\u7EA7\u522B\u65E5\u5FD7
log4j.appender.File.Threshold = ALL
log4j.appender.File.layout = org.apache.log4j.PatternLayout
log4j.appender.File.layout.ConversionPattern =[%p] [%d{yyyy-MM-dd HH\:mm\:ss}][%c]%m%n
- 加入数据库连接信息
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/mpdb?useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=123
- 加入 spring 的配置文件 applicationContext.xml
<?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: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.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--加载配置文件-->
<!--使用spring自带的占位符替换功能 -->
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<!-- 忽略没有找到的资源文件 -->
<property name="ignoreResourceNotFound" value="false" />
<!-- 配置资源文件 -->
<property name="locations">
<list>
<value>classpath:jdbc.properties</value>
</list>
</property>
</bean>
<!--扫描包-->
<context:component-scan base-package="com.dream"/>
<!--配置数据源-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 配置sqlsession工厂 -->
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean">
<!--配置扫描mybatis配置文件,用于加入插件-->
<property name="configLocation" value="classpath:mybatis-config.xml" />
<property name="dataSource" ref="dataSource"></property>
<!--配置别名自动扫描bean-->
<property name="typeAliasesPackage" value="com.dream.bean"></property>
<!--配置扫描地mappper.xml文件-->
<property name="mapperLocations" value="classpath:com/dream/mapper/*.xml"></property>
</bean>
<!-- DAO接口所在包名,Spring会自动查找其下的类 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.dream.mapper" />
</bean>
<!--配置事务管理器-->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--开启事务驱动支持-->
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
</beans>
在配置session工厂的时候,我们需要配置 **MybatisSqlSessionFactoryBean**这个类,并且在配置 mapper扫描路径的时候需要存在该路径,否则会报错
2.4 测试
1)测试spring-mybatis环境
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestMp {
@Autowired
DataSource ds;
@Test
public void testDataSource(){
System.out.println(ds);
}
}
2.5 集成 Mybatis-Plus
Mybatis-Plus 的集成非常简单,对于 Spring,我们仅仅需要把 Mybatis自带的
MybatisSqlSessionFactoryBean 替换为 MP 自带的即可。
三 快速开始CRUD
3.1通用 CRUD
1)提出问题:
假设我们已存在一张 tbl_employee 表, 且已有对应的实体类 Employee,实现tbl_employee 表的 CRUD 操作我们需要做什么呢?
2)实现方式:
-
基于 Mybatis需要编写 EmployeeMapper 接口,并手动编写 CRUD 方法提供 EmployeeMapper.xml 映射文件,并手动编写每个方法对应的 SQL 语句.
-
基于 MP只需要创建 EmployeeMapper 接口, 并继承 BaseMapper 接口.这就是使用 MP需要完成的所有操作,甚至不需要创建 SQL 映射文件。
3.2 Mapper接口
/**
* Mapper接口
* 基于Mybatis: 在Mapper接口中编写CRUD相关的方法 提供Mapper接口所对应的SQL映射文件 以及 方法对应的SQL语句.
* 基于MP: 让XxxMapper接口继承 BaseMapper接口即可.
* BaseMapper<T>:泛型指定的就是当前Mapper接口所操作的实体类类型
*/
public interface EmployeeMapper extends BaseMapper<Employee> {
}
让XxxMapper接口继承 BaseMapper接口,我们就可以使用mp自带的常用CRUD方法,基本上可以满足我们的需求
BaseMapper.java
public interface BaseMapper<T> {
/**
* <p>
* 插入一条记录
* </p>
*
* @param entity 实体对象
* @return int
*/
Integer insert(T entity);
/**
* <p>
* 插入一条记录
* </p>
*
* @param entity 实体对象
* @return int
*/
@Deprecated
Integer insertAllColumn(T entity);
/**
* <p>
* 根据 ID 删除
* </p>
*
* @param id 主键ID
* @return int
*/
Integer deleteById(Serializable id);
/**
* <p>
* 根据 columnMap 条件,删除记录
* </p>
*
* @param columnMap 表字段 map 对象
* @return int
*/
Integer deleteByMap(@Param("cm") Map<String, Object> columnMap);
/**
* <p>
* 根据 entity 条件,删除记录
* </p>
*
* @param wrapper 实体对象封装操作类(可以为 null)
* @return int
*/
Integer delete(@Param("ew") Wrapper<T> wrapper);
/**
* <p>
* 删除(根据ID 批量删除)
* </p>
*
* @param idList 主键ID列表
* @return int
*/
Integer deleteBatchIds(@Param("coll") Collection<? extends Serializable> idList);
/**
* <p>
* 根据 ID 修改
* </p>
*
* @param entity 实体对象
* @return int
*/
Integer updateById(@Param("et") T entity);
/**
* <p>
* 根据 ID 修改
* </p>
*
* @param entity 实体对象
* @return int
*/
Integer updateAllColumnById(@Param("et") T entity);
/**
* <p>
* 根据 whereEntity 条件,更新记录
* </p>
*
* @param entity 实体对象(更新的数据, name='Abc')
* @param wrapper 实体对象封装操作类(更新条件,可以为 null)
* @return
*/
Integer update(@Param("et") T entity, @Param("ew") Wrapper<T> wrapper);
/**
* <p>
* 根据 whereEntity 条件,更新记录
* </p>
*
* @param setStr set字符串
* @param wrapper 实体对象封装操作类(可以为 null)
* @return
*/
Integer updateForSet(@Param("setStr") String setStr, @Param("ew") Wrapper<T> wrapper);
/**
* <p>
* 根据 ID 查询
* </p>
*
* @param id 主键ID
* @return T
*/
T selectById(Serializable id);
/**
* <p>
* 查询(根据ID 批量查询)
* </p>
*
* @param idList 主键ID列表
* @return List<T>
*/
List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList);
/**
* <p>
* 查询(根据 columnMap 条件)
* </p>
*
* @param columnMap 表字段 map 对象
* @return List<T>
*/
List<T> selectByMap(@Param("cm") Map<String, Object> columnMap);
/**
* <p>
* 根据 entity 条件,查询一条记录
* </p>
*
* @param entity 实体对象
* @return T
*/
T selectOne(@Param("ew") T entity);
/**
* <p>
* 根据 Wrapper 条件,查询总记录数
* </p>
*
* @param wrapper 实体对象
* @return int
*/
Integer selectCount(@Param("ew") Wrapper<T> wrapper);
/**
* <p>
* 根据 entity 条件,查询全部记录
* </p>
*
* @param wrapper 实体对象封装操作类(可以为 null)
* @return List<T>
*/
List<T> selectList(@Param("ew") Wrapper<T> wrapper);
/**
* <p>
* 根据 Wrapper 条件,查询全部记录
* </p>
*
* @param wrapper 实体对象封装操作类(可以为 null)
* @return List<T>
*/
List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> wrapper);
/**
* <p>
* 根据 Wrapper 条件,查询全部记录
* 注意: 只返回第一个字段的值
* </p>
*
* @param wrapper 实体对象封装操作类(可以为 null)
* @return List<Object>
*/
List<Object> selectObjs(@Param("ew") Wrapper<T> wrapper);
/**
* <p>
* 根据 entity 条件,查询全部记录(并翻页)
* </p>
*
* @param rowBounds 分页查询条件(可以为 RowBounds.DEFAULT)
* @param wrapper 实体对象封装操作类(可以为 null)
* @return List<T>
*/
List<T> selectPage(RowBounds rowBounds, @Param("ew") Wrapper<T> wrapper);
/**
* <p>
* 根据 Wrapper 条件,查询全部记录(并翻页)
* </p>
*
* @param rowBounds 分页查询条件(可以为 RowBounds.DEFAULT)
* @param wrapper 实体对象封装操作类
* @return List<Map<String, Object>>
*/
List<Map<String, Object>> selectMapsPage(RowBounds rowBounds, @Param("ew") Wrapper<T> wrapper);
}
3.3 实体类注解配置
1)@TableName
2)@TableField
**忽略实体类中的某个属性**
3)@TableId
**Mybatis: 需要通过 useGeneratedKeys以及keyProperty 来设置**
**MP: 自动将主键值回写到实体类中**
/*
* MybatisPlus会默认使用实体类的类名到数据中找对应的表.
*/
@TableName(value="employee")
public class Employee {
/*
* @TableId:
* value: 指定表中的主键列的列名, 如果实体属性名与列名一致,可以省略不指定.
* type: 指定主键策略.
*/
@TableId(value="id" , type =IdType.AUTO)
private Integer id;
@TableField(value = "last_name")
private String lastName;
@TableField(value = "email")
private String email;
@TableField(value = "gender")
private Integer gender;
@TableField(value = "age")
private Integer age;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Employee(Integer id, String lastName, String email, Integer gender, Integer age) {
super();
this.id = id;
this.lastName = lastName;
this.email = email;
this.gender = gender;
this.age = age;
}
public Employee(String lastName, String email, Integer gender, Integer age) {
super();
this.lastName = lastName;
this.email = email;
this.gender = gender;
this.age = age;
}
public Employee() {
super();
}
@Override
public String toString() {
return "Employee [id=" + id + ", lastName=" + lastName + ", email=" + email + ", gender=" + gender + ", age=" + age + "]";
}
}
3.4 插入测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestCRUD {
@Autowired
EmployeeMapper em;
@Test
public void testAdd(){
Employee e=new Employee("zhangsan", "zhangsan@163.com", 1, 18);
int count = em.insert(e);
System.out.println(count>0?"成功":"失败");
}
}
@Test
public void testAdd1(){
Employee e=new Employee("zhangsan", "zhangsan@163.com");
int count = em.insertAllColumn(e);
System.out.println(count>0?"成功":"失败");
System.out.println("新增成功之后的主键是:"+e.getId());
}
3.5 全局策略配置
1)@TableName
全局的 MP 配置:
2)@TableField
全局的 MP 配置:
3)@TableId
全局的 MP 配置:
支持主键自增的数据库插入数据获取主键值
<bean id="sqlSessionFactoryBean"
class="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean">
<!-- 数据源 -->
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<!-- 别名处理 -->
<property name="typeAliasesPackage" value="com.mp.bean"></property>
<!-- 注入全局MP策略配置 -->
<property name="globalConfig" ref="globalConfiguration"/>
</bean>
<!-- 定义MybatisPlus的全局策略配置 -->
<bean id="globalConfiguration" class="com.baomidou.mybatisplus.entity.GlobalConfiguration">
<!-- 在2.3版本以后,dbColumnUnderline 默认值就是true -->
<property name="dbColumnUnderline" value="true"/>
<!-- 全局的主键策略 -->
<property name="idType" value="0"/>
<!-- 全局的表前缀策略配置 -->
<property name="tablePrefix" value="t_"/>
</bean>
3.6 更新操作
1)Integer updateById(@Param(“et”) T entity);
@Test
public void testUopdate(){
Employee e=new Employee(2,"xiaozhang", "zhangsan@163.com", 1, 18);
Integer count = em.updateById(e);
System.out.println(count>0?"成功":"失败");
}
2)Integer updateAllColumnById(@Param(“et”) T entity)
@Test
public void testUopdate1(){
Employee e=new Employee(2,"xiaozhang", "zhangsan@163.com");
Integer count = em.updateAllColumnById(e);
System.out.println(count>0?"成功":"失败");
}
updateAllColumnById不存在动态sql,会更新所有的列,效率低;updateById具有动态sql,字段为null的不会更新
3.7 查询操作
1)T selectById(Serializable id); 通过id查询
@Test
public void testselectById(){
Employee e = em.selectById(2);
System.out.println(e);
}
2)T selectOne(@Param(“ew”) T entity); 通过多个列进行查询
@Test
public void testselectOne() {
Employee employee = new Employee();
employee.setLastName("Black");
employee.setGender(0);
Employee result = em.selectOne(employee);
System.out.println("result: " + result);
}
3)List selectBatchIds(List<? extends Serializable> idList); 通过多个id进行查询
@Test
public void testselectBatchIds(){
List<Integer> idList = new ArrayList<>();
idList.add(4);
idList.add(5);
idList.add(6);
idList.add(7);
List<Employee> emps = em.selectBatchIds(idList);
System.out.println(emps);
}
-
List<T> selectByMap(@Param("cm") Map<String,Object> columnMap);通过Map封装条件查询
@Test
public void testselectByMap(){
Map<String, Object> columnMap = new HashMap<>();
columnMap.put("last_name", "Tom");
columnMap.put("gender", 1);
List<Employee> emps = em.selectByMap(columnMap);
System.out.println(emps);
}
5)List selectPage(RowBounds rowBounds, @Param(“ew”) Wrapper wrapper);
@Test
public void testselectPage() {
List<Employee> emps = em.selectPage(new Page<>(3, 2), null);
for (Employee employee : emps) {
System.out.println(employee);
}
}
3.8 删除操作
- Integer deleteById(Serializable id); 根据id进行删除
@Test
public void testdeleteById() {
Integer result = em.deleteById(1);
System.out.println("result: " + result);
}
-
Integer deleteByMap(@Param("cm") Map<String, Object>columnMap); 根据条件进行删除
@Test
public void testdeleteByMap(){
Map<String,Object> columnMap = new HashMap<>();
columnMap.put("last_name", "White");
columnMap.put("email", "white@163.com");
Integer result = em.deleteByMap(columnMap);
System.out.println("result: " + result );
}
-
Integer deleteBatchIds(List<? extends Serializable>idList); 批量删除
@Test
public void testdeleteBatchIds(){
List<Integer> idList = new ArrayList<>();
idList.add(10);
idList.add(11);
idList.add(12);
Integer result = em.deleteBatchIds(idList);
System.out.println("result: " + result );
}
3.9MP 启动注入 SQL 原理分析
-
问题: xxxMapper 继承了 BaseMapper, BaseMapper 中提供了通用的 CRUD 方法, 方法来源于 BaseMapper, 有方法就必须有 SQL, 因为 MyBatis 最终还是需要通过SQL 语句操作数据.
前置知识:MyBatis 源码中比较重要的一些对象, MyBatis 框架的执行流程
Configuration MappedStatement ……
-
通过现象看到本质
A. employeeMapper 的本质org.apache.ibatis.binding.MapperProxy
B. MapperProxy中sqlSession –>SqlSessionFactory
C. 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 中
3.10 通用CRUD小结
- 以上是基本的 CRUD 操作,如您所见,我们仅仅需要继承一个 BaseMapper 即可实现大部分单表 CRUD 操作。BaseMapper 提供了多达 17 个方法给大家使用, 可以极其方便的实现单一、批量、分页等操作。极大的减少开发负担,难道这就是 MP 的强大之处了吗?
2)提出需求:现有一个需求,我们需要分页查询 tbl_employee 表中,年龄在 18~50 之间性别为男且姓名为 xx 的所有用户,这时候我们该如何实现上述需求呢?
MyBatis : 需要在 SQL 映射文件中编写带条件查询的 SQL,并基于 PageHelper 插件完成分页. 实现以上一个简单的需求,往往需要我们做很多重复单调的工作。普通的 Mapper 能够解决这类痛点吗?
MP: 依旧不用编写 SQL 语句, MP 提供了功能强大的条件构造器 EntityWrapper
四 EntityWrapper 简介
4.1EntityWrapper简介
-
Mybatis-Plus 通过 EntityWrapper(简称 EW,MP 封装的一个查询条件构造器)或者Condition(与 EW 类似) 来让用户自由的构建查询条件,简单便捷,没有额外的负担, 能够有效提高开发效率
-
实体包装器,主要用于处理 sql 拼接,排序,实体参数查询等
-
注意: 使用的是数据库字段,不是 Java 属性!
-
条件参数说明
4.2使用 EntityWrapper 的方式打开如上需求
@Test
public void test1() {
List<Employee> emps = em.selectPage(new Page<Employee>(1, 3),
new EntityWrapper<Employee>().eq("last_name", "Black"));
for (Employee e : emps) {
System.out.println(e);
}
}
4.3带条件的查询操作
@Test
public void test2() {
// 分页查询t_employee表中,年龄在18~50之间且性别为男且姓名为Tom的所有用户
List<Employee> emps = em.selectPage(new Page<Employee>(1, 2),
new EntityWrapper<Employee>().between("age", 18, 50).eq("gender", 1).eq("last_name", "Tom"));
for (Employee e : emps) {
System.out.println(e);
}
}
@Test
public void tes3() {
List<Employee> emps = em.selectPage(new Page<Employee>(1, 2),
Condition.create().between("age", 18, 50).eq("gender", "1").eq("last_name", "Tom"));
System.out.println(emps);
}
- OR的关系:
@Test
public void test4(){
// 查询t_employee表中, 性别为女并且名字中带有"老师" 或者 邮箱中带有"a"
List<Employee> emps = em.selectList(
new EntityWrapper<Employee>()
.eq("gender", 0)
.like("last_name", "zhangsan")
//.or() // SQL: (gender = ? AND last_name LIKE ? OR email LIKE ?)
.orNew() // SQL: (gender = ? AND last_name LIKE ?) OR (email LIKE ?)
.like("email", "a")
);
System.out.println(emps);
}
-
排序
@Test public void test5(){ // 查询性别为男的, 根据age进行排序(asc/desc), 简单分页 List<Employee> emps = em.selectList( new EntityWrapper<Employee>() .eq("gender", 1) .orderBy("age") //.orderDesc(Arrays.asList(new String [] {"age"})) .last("desc limit 1,3") ); System.out.println(emps); }
4.4 带条件修改操作
@Test
public void testUpdate1(){
Employee employee = new Employee();
employee.setLastName("Jack");
employee.setEmail("cls@163.com");
employee.setGender(0);
int count=em.update(employee,
new EntityWrapper<Employee>()
.eq("last_name", "Black")
.eq("age", 44)
);
System.out.println(count);
}
4.5 带条件删除操作
/**
* 条件构造器 删除操作
*/
@Test
public void testDelete1() {
int count=em.delete(
new EntityWrapper<Employee>()
.eq("last_name", "Tom")
.eq("age", 22)
);
System.out.println(count);
}
4.6小结
MP(条件构造器):
EntityWrapper
Condition
MyBatis MBG :xxxExample→Criteria : QBC( Query By Criteria)
Hibernate :→Criteria : QBC( Query By Criteria)
五 代码生成器
-
MP提供了大量的自定义设置,生成的代码完全能够满足各类型的需求
-
MP 的代码生成器 和 Mybatis MBG 代码生成器:
MP 的代码生成器都是基于 java 代码来生成。MBG 基于 xml 文件进行代码生成
MyBatis 的代码生成器可生成: 实体类、Mapper 接口、Mapper 映射文件
MP 的代码生成器可生成:实体类(可以选择是否支持 AR)、Mapper接口、Mapper 映射文件、Service层、 Controller 层.
-
表及字段命名策略选择
在MP中,我们建议数据库表名和表字段名采用驼峰命名方式, 如果采用下划线命名方式 请开启全局下划线开关,如果表名字段名命名方式不一致请注解指定,我们建议最好保持一致。
这么做的原因是为了避免在对应实体类时产生的性能损耗,这样字段不用做映射就能直接和实体类对应。当然如果项目里不用考虑这点性能损耗,那么你采用下滑线也是没问题的,只需要在生成代码时配置 dbColumnUnderline 属性就可以.
5.1 代码生成器依赖
-
模板引擎
MP的代码生成器默认使用的是Apache 的Velocity 模板,当然也可以更换为别的模板技术,例如 freemarker。此处不做过多的介绍。
需要加入Apache Velocity的依赖
<!-- Apache Velocity的依赖 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.0</version>
</dependency>
2)加入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>
5.2 MP代码生成器示例代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(“classpath:applicationContext.xml”)
public class TestMp {
@Autowired
DataSource ds;
@Test
public void testGenerator() {
// 1. 全局配置
GlobalConfig config = new GlobalConfig();
config.setActiveRecord(true) // 是否支持AR模式
.setAuthor("bruceliu") // 作者
.setOutputDir("H:\\myworkspace123\\MyBatis-plus-Auto\\src\\main\\java") // 生成路径
.setFileOverride(true) // 文件覆盖
.setIdType(IdType.AUTO) // 主键策略
.setServiceName("%sService") // 设置生成的service接口的名字的首字母是否为I
// IEmployeeService
.setBaseResultMap(true).setBaseColumnList(true);
//2. 数据源配置
DataSourceConfig dsConfig = new DataSourceConfig();
dsConfig.setDbType(DbType.MYSQL) // 设置数据库类型
.setDriverName("com.mysql.jdbc.Driver")
.setUrl("jdbc:mysql://127.0.0.1:3306/mpdb?useUnicode=true&characterEncoding=utf8")
.setUsername("root")
.setPassword("123");
//3. 策略配置
StrategyConfig stConfig = new StrategyConfig();
stConfig.setCapitalMode(true) //全局大写命名
.setDbColumnUnderline(true) // 指定表名 字段名是否使用下划线
.setNaming(NamingStrategy.underline_to_camel) // 数据库表映射到实体的命名策略
.setTablePrefix("t_")
.setInclude("t_employee"); // 生成的表
//4. 包名策略配置
PackageConfig pkConfig = new PackageConfig();
pkConfig.setParent("com.mp")
.setMapper("mapper")
.setService("service")
.setController("controller")
.setEntity("bean")
.setXml("mapper");
//5. 整合配置
AutoGenerator ag = new AutoGenerator();
ag.setGlobalConfig(config)
.setDataSource(dsConfig)
.setStrategy(stConfig)
.setPackageInfo(pkConfig);
//6. 执行
ag.execute();
}
**EmployeeServiceImpl 继承了 ServiceImpl 类,mybatis-plus 通过这种方式为我们注入了 EmployeeMapper,这样可以使用 service 层默认为我们提供的很多方法,也可以调用我们自己在 dao 层编写的操作数据库的方法.**
六 插件扩展
6.1 Mybatis 插件机制简介
-
插件机制:
Mybatis 通过插件(Interceptor) 可以做到拦截四大对象相关方法的执行,根据需求,完成相关数据的动态改变。
Executor
StatementHandler
ParameterHandler
ResultSetHandler -
插件原理
四大对象的每个对象在创建时,都会执行interceptorChain.pluginAll(),会经过每个插件对象的 plugin()方法,目的是为当前的四大对象创建代理。代理对象就可以拦截到四大对象相关方法的执行,因为要执行四大对象的方法需要经过代理.
6.1 分页插件
1) com.baomidou.mybatisplus.plugins.PaginationInterceptor
- 配置分页插件
在mybatis-config.xml中
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<plugins>
<plugin interceptor="com.baomidou.mybatisplus.plugins.PaginationInterceptor"></plugin>
</plugins>
</configuration>
或者
<bean id="sqlSessionFactoryBean"
class="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean">
<!-- 数据源 -->
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<!-- 别名处理 -->
<property name="typeAliasesPackage" value="com.mp.bean"></property>
<!-- 注入全局MP策略配置 -->
<property name="globalConfig" ref="globalConfiguration" />
<!-- 插件注册 -->
<property name="plugins">
<list>
<!-- 注册分页插件 -->
<bean class="com.baomidou.mybatisplus.plugins.PaginationInterceptor"></bean>
<!-- 注册执行分析插件 -->
<bean class="com.baomidou.mybatisplus.plugins.SqlExplainInterceptor">
<property name="stopProceed" value="true"></property>
</bean>
</list>
</property>
</bean>
测试分页
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestPlugin {
@Autowired
EmployeeMapper employeeMapper;
/**
* 测试分页插件
*/
@Test
public void testPage() {
Page<Employee> page = new Page<Employee>(1,3);
List<Employee > emps = employeeMapper.selectPage(page, null);
System.out.println(emps);
System.out.println("===============获取分页相关的一些信息======================");
System.out.println("总条数:" +page.getTotal());
System.out.println("当前页码: "+ page.getCurrent());
System.out.println("总页码:" + page.getPages());
System.out.println("每页显示的条数:" + page.getSize());
System.out.println("是否有上一页: " + page.hasPrevious());
System.out.println("是否有下一页: " + page.hasNext());
//将查询的结果封装到page对象中
page.setRecords(emps);
}
}
6.3 性能分析插件
1)com.baomidou.mybatisplus.plugins.PerformanceInterceptor
2)性能分析拦截器,用于输出每条 SQL 语句及其执行时间
3)SQL 性能执行分析,开发环境使用,超过指定时间,停止运行。有助于发现问题
- 配置插件
<!-- 插件注册 -->
<property name="plugins">
<list>
<!-- 注册分页插件 -->
<bean class="com.baomidou.mybatisplus.plugins.PaginationInterceptor"></bean>
<!-- 注册执行分析插件 -->
<bean class="com.baomidou.mybatisplus.plugins.SqlExplainInterceptor">
<property name="stopProceed" value="true"></property>
</bean>
<!-- 注册性能分析插件 -->
<bean class="com.baomidou.mybatisplus.plugins.PerformanceInterceptor">
<property name="format" value="true"></property>
<!-- <property name="maxTime" value="5"></property> -->
</bean>
</list>
</property>
-
测试插件
/** * 测试 性能分析插件 */ @Test public void testPerformance() { Employee employee = new Employee(); employee.setLastName("mly"); employee.setEmail("mly@sina.com"); employee.setGender("0"); employee.setAge(22); employeeMapper.insert(employee); }