MyBatis-Plus(簡稱 MP)是一個 MyBatis的增強工具,在 MyBatis 的基礎上只做增強不做改變,為簡化開發、提高效率而生。
整合SpringBoot
創建數據庫和數據表:
CREATE DATABASE mybatisplus;
CREATE TABLE tbl_employee(
id INT(11) PRIMARY KEY AUTO_INCREMENT,
last_name VARCHAR(255),
email VARCHAR(255),
gender CHAR(1),
age INT(11)
);
INSERT INTO tbl_employee(last_name,email,gender,age) VALUES('tom','tom@qq.com',1,20);
INSERT INTO tbl_employee(last_name,email,gender,age) VALUES('jack','jack@qq.com',1,21);
INSERT INTO tbl_employee(last_name,email,gender,age) VALUES('jerry','jerry@qq.com',0,22);
INSERT INTO tbl_employee(last_name,email,gender,age) VALUES('smith','smith@qq.com',0,23);
創建數據表對應的Bean類:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {
private Integer id;
private String lastName;
private String email;
private Integer gender;
private Integer age;
}
添加依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>MySQL</groupId>
<artifactId>mysql-connector-JAVA</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.13</version>
</dependency>
配置數據源:
spring:
datasource:
url: jdbc:mysql:///mybatisplus?serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
mybatis-plus:
configuration:
log-impl: org.Apache.ibatis.logging.stdout.StdOutImpl # 輸出sql
最后在啟動類上添加MApper接口掃描注解:
@SpringBootApplication
@MapperScan("com.wwj.mybatisplusdemo.mapper")
public class MybatisplusDemoApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisplusDemoApplication.class, args);
}
}
通用CRUD
回想一下傳統的MyBatis開發,若是想要實現員工信息的增刪改查,該如何實現?我們需要編寫Mapper接口,并創建對應的映射文件,然后配置每一個接口方法對應的sql,對于一些非常簡單的操作,這些步驟顯然非常麻煩,那么有沒有可能讓這些簡單的增刪改查自動實現呢?MyBatisPlus幫我們實現了這個想法,在MyBatisPlus中,我們只需要創建Mapper接口并繼承BaseMapper即可獲得員工表的增刪改查方法。
創建Mapper接口:
public interface EmployeeMapper extends BaseMapper<Employee> {
}
既然繼承了BaseMapper接口就擁有了增刪改查方法,那么這些方法肯定是從BaseMapper中繼承下來的,所以來看看BaseMapper的源碼:
public interface BaseMapper<T> extends Mapper<T> {
int insert(T entity);
int deleteById(Serializable id);
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
int updateById(@Param(Constants.ENTITY) T entity);
int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);
T selectById(Serializable id);
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
<E extends IPage<T>> E selectPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
<E extends IPage<Map<String, Object>>> E selectMapsPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
}
該接口定義了非常多的方法,
新增
接下來我們使用MyBatisPlus實現一下員工信息的增刪改查,首先是新增操作:
@Autowired
private EmployeeMapper employeeMapper;
@Test
void contextLoads() {
Employee employee = new Employee(null,"aaa","aaa@qq.com",1,30);
employeeMapper.insert(employee);
}
然后執行該方法會產生一個異常:
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.reflection.ReflectionException: Could not set property 'id' of 'class com.wwj.mybatisplusdemo.bean.Employee' with value '1364829918590943234' Cause: java.lang.IllegalArgumentException: argument type mismatch
這是因為我們沒有為MyBatisPlus指定主鍵策略,MyBatisPlus共支持以下四種主鍵策略:
值 |
描述 |
AUTO |
數據庫ID自增 |
INPUT |
insert前自行set主鍵值 |
ASSIGN_ID |
分配ID(主鍵類型為Number(Long和Integer)或String)(since 3.3.0),使用接口IdentifierGenerator的方法nextId(默認實現類為 |
ASSIGN_UUID |
分配UUID,主鍵類型為String(since 3.3.0),使用接口IdentifierGenerator的方法nextUUID(默認default方法) |
只需在主鍵屬性上添加@TableId注解即可設置主鍵策略:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {
@TableId(type = IdType.AUTO)
private Integer id;
private String lastName;
private String email;
private Integer gender;
private Integer age;
}
此時執行方法仍然出現一個異常:
### Cause: java.sql.SQLSyntaxErrorException: Table 'mybatisplus.employee' doesn't exist
; bad SQL grammar []; nested exception is java.sql.SQLSyntaxErrorException: Table 'mybatisplus.employee' doesn't exist
這是因為我們的實體類名為Employee,數據表名為tbl_employee,而MyBatisPlus默認會以實體類名去數據庫中尋找對應的表,導致二者無法進行映射,為此,我們還需要設置一下實體類對應的表名:
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("tbl_employee")
public class Employee {
@TableId(type = IdType.AUTO)
private Integer id;
private String lastName;
private String email;
private Integer gender;
private Integer age;
}
在剛才的案例中,我們發現實體類中的屬性lastName和數據表的列名last_name并不相同,但是新增數據仍然成功了,而且我們也并沒有配置任何與屬性名映射相關的配置,其實是因為MyBatisPlus有默認的全局策略配置,在MyBatisConfiguration配置類中有這樣的一段配置:
public MybatisConfiguration() {
super();
this.mapUnderscoreToCamelCase = true;
languageRegistry.setDefaultDriverClass(MybatisXMLLanguageDriver.class);
}
這是配置類的無參構造方法,它將mapUnderscoreToCamelCase屬性設置為true,而我們知道,該屬性為true則會開啟駝峰命名,所以駝峰命名映射是默認開啟的,若是想關閉,則設置該屬性為false即可:
mybatis-plus:
configuration:
map-underscore-to-camel-case: false
我們還發現,在實體類上標注@TableId和@TableName注解讓人非常不愉快,并且當實體類足夠多時,這也是一個繁瑣且麻煩的操作,為此,我們可以使用全局配置來解決這一問題,查看GlobalConfig類的源碼,它是MyBatisPlus的全局策略配置類:
@Data
public static class DbConfig {
/**
* 主鍵類型
*/
private IdType idType = IdType.ASSIGN_ID;
}
在源碼中找到這樣一段配置,說明MyBatisPlus默認的主鍵策略是 分配ID ,所以我們需要將其配置為主鍵自增:
mybatis-plus:
global-config:
db-config:
id-type: auto
表名的映射也可以進行全局配置:
mybatis-plus:
global-config:
db-config:
id-type: auto
table-prefix: tbl_
此時就可以將實體類上的@TableId和@TableName注解去掉了。
駝峰命名映射只能解決諸如lastName和last_name的映射關系,當兩者差別過大時,比如:lastName和name,此時我們需要使用@TableField注解來解決:
@TableField(value = "name")
private String lastName;
但通常情況下數據表的列名和實體類的屬性名一定是駝峰命名映射或者完全相同的,所以該注解一般用于另外一種場景,即:當前實體類中有些屬性在數據表中是不存在的,此時就會出現異常,而@TableField注解能夠通過屬性設置來聲明某個屬性是數據表中不存在的屬性,這樣MyBatisPlus在生成sql語句時就不會帶上它:
@TableField(exist = false)
private Integer salary;
更新
更新操作也非常簡單,直接調用自動生成的方法即可:
@Autowired
private EmployeeMapper employeeMapper;
@Test
void contextLoads() {
Employee employee = new Employee(5,"bbb","bbb@qq.com",1,30,3000);
employeeMapper.updateById(employee);
}
該方法是根據id進行員工信息的更新,所以id屬性不能為空,而且它會根據屬性是否為空動態生成sql,即:只更新不為空的屬性值。
查詢
查詢是最為頻繁的操作,故提供的查詢方法也最多,先來看看selectById方法:
@Autowired
private EmployeeMapper employeeMapper;
@Test
void contextLoads() {
Employee employee = employeeMapper.selectById(1);
System.out.println(employee);
}
該方法通過id值查詢員工信息。然后是selectBatchIds方法:
@Autowired
private EmployeeMapper employeeMapper;
@Test
void contextLoads() {
List<Employee> emps = employeeMapper.selectBatchIds(Arrays.asList(1, 2, 3));
for (Employee emp : emps) {
System.out.println(emp);
}
}
該方法是批量查詢方法,通過一個id的集合查詢這些id的所屬員工信息。selectByMap方法:
@Autowired
private EmployeeMapper employeeMapper;
@Test
void contextLoads() {
Map<String,Object> map = new HashMap<>();
map.put("last_name","tom");
map.put("gender",1);
List<Employee> emps = employeeMapper.selectByMap(map);
for (Employee emp : emps) {
System.out.println(emp);
}
}
該方法仍然是一個批量查詢,通過Map封裝查詢條件,需要注意Map中的鍵為數據表中的字段名而非實體類中的屬性名。
刪除
最后來看看刪除操作:
@Autowired
private EmployeeMapper employeeMapper;
@Test
void contextLoads() {
employeeMapper.deleteById(10);
}
根據id刪除員工信息。然后是deleteByMap方法:
@Autowired
private EmployeeMapper employeeMapper;
@Test
void contextLoads() {
Map<String,Object> map = new HashMap<>();
map.put("last_name","aaa");
employeeMapper.deleteByMap(map);
}
該方法通過Map集合封裝的條件進行刪除,Map中的鍵也為數據表的列名。deleteBatchIds方法:
@Autowired
private EmployeeMapper employeeMapper;
@Test
void contextLoads() {
employeeMapper.deleteBatchIds(Arrays.asList(1,2,3));
}
該方法是批量刪除方法,會刪除集合中的所有id對應的員工信息。
條件構造器
剛才我們體驗了MyBatisPlus的增刪改查操作,不過這都是一些最基本的操作方法,對于查詢,MyBatis提供了一個條件構造器——QueryWrapper,使用它能夠自由構建查詢條件,簡單便捷,能夠極大提升開發效率。
現在有一個需求,查詢年齡在10~30歲之間的員工信息,并分頁顯示:
@Test
void contextLoads() {
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
wrapper.between("age", 10, 30);
Page<Employee> page = employeeMapper.selectPage(new Page<>(1, 2), wrapper);
List<Employee> emps = page.getRecords();
for (Employee emp : emps) {
System.out.println(emp);
}
}
首先構造QueryWrapper對象,通過between拼接sql,注意其中的參數必須是表字段名,然后通過selectPage方法進行分頁查詢,分頁規則為 new Page<>(1,2) ,即:按每頁兩行數據進行分頁,顯示第一頁,最后從Page對象中取出數據即可,不要忘記注冊一下分頁攔截器:
@Configuration
public class MyBatisConfig {
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
執行結果:
==> Preparing: SELECT COUNT(1) FROM tbl_employee WHERE (age BETWEEN ? AND ?)
==> Parameters: 10(Integer), 30(Integer)
<== Columns: COUNT(1)
<== Row: 5
==> Preparing: SELECT id,last_name,email,gender,age FROM tbl_employee WHERE (age BETWEEN ? AND ?) LIMIT ?,?
==> Parameters: 10(Integer), 30(Integer), 0(Long), 2(Long)
<== Columns: id, last_name, email, gender, age
<== Row: 1, tom, tom@qq.com, 1, 20
<== Row: 2, jack, jack@qq.com, 1, 21
<== Total: 2
Employee(id=1, lastName=tom, email=tom@qq.com, gender=1, age=20, salary=null)
Employee(id=2, lastName=jack, email=jack@qq.com, gender=1, age=21, salary=null)
若是想要查詢年齡在10~30歲且性別為男的員工信息,該怎么實現呢?
@Test
void contextLoads() {
QueryWrapper<Employee> wrapper = new QueryWrapper<Employee>()
.between("age", 10, 30)
.eq("gender",1);
Page<Employee> page = employeeMapper.selectPage(new Page<>(1, 2), wrapper);
List<Employee> emps = page.getRecords();
for (Employee emp : emps) {
System.out.println(emp);
}
}
直接在QueryWrapper對象上鏈式調用即可,需要什么條件就加什么條件。
再看selectList方法,提出一個需求,查詢性別為男,名字中帶有字母j或者郵箱中帶有字母b的員工信息,實現如下:
@Test
void contextLoads() {
QueryWrapper<Employee> wrapper = new QueryWrapper<Employee>()
.eq("gender", 1)
.like("last_name", "j")
.or()
.like("email", "b");
List<Employee> emps = employeeMapper.selectList(wrapper);
for (Employee emp : emps) {
System.out.println(emp);
}
}
或關系只需要調用or()方法即可實現,執行結果:
==> Preparing: SELECT id,last_name,email,gender,age FROM tbl_employee WHERE (gender = ? AND last_name LIKE ? OR email LIKE ?)
==> Parameters: 1(Integer), %j%(String), %b%(String)
<== Columns: id, last_name, email, gender, age
<== Row: 2, jack, jack@qq.com, 1, 21
<== Row: 5, bbb, bbb@qq.com, 1, 30
<== Total: 2
Employee(id=2, lastName=jack, email=jack@qq.com, gender=1, age=21, salary=null)
Employee(id=5, lastName=bbb, email=bbb@qq.com, gender=1, age=30, salary=null)
既然有QueryWrapper提供查詢,那么就應該有UpdateWrapper用于更新,比如修改名字中帶字母j并且年齡為21歲的員工信息,將其年齡修改為30歲,實現如下:
@Test
void contextLoads() {
Employee employee = new Employee();
employee.setAge(30);
UpdateWrapper<Employee> wrapper = new UpdateWrapper<Employee>()
.like("last_name", "j")
.eq("age", 21);
employeeMapper.update(employee, wrapper);
}
對于刪除操作,我們仍然使用QueryWrapper來完成條件的限定,比如刪除名字中帶字母b并且年齡為30歲的男性員工信息,實現如下:
@Test
void contextLoads() {
QueryWrapper<Employee> wrapper = new QueryWrapper<Employee>()
.like("last_name", "b")
.eq("age", 30)
.eq("gender", 1);
employeeMapper.delete(wrapper);
}
條件構造器的功能遠不止于此,具體細節可以參照官方文檔:
https://mybatis.plus/guide/wrapper.html
代碼生成器
與MyBatis一樣,MyBatisPlus同樣提供了代碼生成器,而且是基于Java代碼進行生成的,相比于MyBatis,也更具有擴展性。
引入依賴:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.2</version>
</dependency>
然后編寫生成代碼:
@Test
void contextLoads() {
// 全局配置
GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setAuthor("wwj") // 作者
.setOutputDir("C:/Users/Administrator/Desktop/ideaworkspace/mybatisplus-demo/src/main/java") // 生成路徑
.setFileOverride(true) // 若多次生成,則文件覆蓋
.setIdType(IdType.AUTO) // 主鍵策略
.setServiceName("%sService") // 設置生成的service接口名不攜帶首字母I --> IEmployeeService
.setBaseResultMap(true)
.setBaseColumnList(true); // 生成sql片段
// 數據源配置
DataSourceConfig dataSourceConfig = new DataSourceConfig();
dataSourceConfig.setDbType(DbType.MYSQL) // 設置數據庫類型
.setDriverName("com.mysql.cj.jdbc.Driver")
.setUrl("jdbc:mysql:///mybatisplus?serverTimezone=UTC")
.setUsername("root")
.setPassword("123456");
// 策略配置
StrategyConfig strategyConfig = new StrategyConfig();
strategyConfig.setCapitalMode(true) // 開啟全局大寫命名
.setColumnNaming(NamingStrategy.underline_to_camel) // 設置字段名與屬性的駝峰命名映射
.setTablePrefix("tbl_") // 設置表名前綴
.setInclude("tbl_employee"); // 作用于哪張表
// 包名配置
PackageConfig packageConfig = new PackageConfig();
packageConfig.setParent("com.wwj.mybatisplusdemo") // 設置父包名
.setMapper("mapper") // 設置Mapper接口生成的位置
.setService("service") // 設置Service生成的位置
.setController("controller") // 設置Controller生成的位置
.setEntity("bean") // 設置實體類生成的位置
.setXml("mapper"); // 設置Mapper映射文件生成的位置
// 整合配置
AutoGenerator autoGenerator = new AutoGenerator();
autoGenerator.setGlobalConfig(globalConfig)
.setDataSource(dataSourceConfig)
.setStrategy(strategyConfig)
.setPackageInfo(packageConfig);
// 執行
autoGenerator.execute();
}
最后執行即可生成實體類、Mapper接口、Service、Controller和Mapper映射文件:
需要注意的是Service的實現類:
@Service
public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper, Employee> implements EmployeeService {
}
它繼承自ServiceImpl:
public class ServiceImpl<M extends BaseMapper<T>, T> implements IService<T> {
protected Log log = LogFactory.getLog(getClass());
@Autowired
protected M baseMapper;
......
}
該類提供了一些簡單的服務方法,所以我們無需編寫任何代碼就可以使用一些簡單的增刪改查服務代碼,而且它自動注入了M泛型的baseMapper,所以我們也能夠直接在EmployeeServiceImpl中使用該Mapper。
插件
MyBatisPlus提供了一些非常好用的框架來簡化我們的開發,一起來看看吧。
分頁插件
在前面的分頁方法中我們已經使用到了分頁插件,這里再簡單介紹一下吧。
要想使用分頁插件,首先就需要將該插件注冊到容器中:
@Configuration
public class MyBatisConfig {
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
然后就可以使用了:
@Test
void contextLoads() {
Page<Employee> page = employeeMapper.selectPage(new Page<>(1, 2), null);
List<Employee> emps = page.getRecords();
for (Employee emp : emps) {
System.out.println(emp);
}
}
Page對象中封裝了非常多的信息,比如:
@Test
void contextLoads() {
Page<Employee> page = employeeMapper.selectPage(new Page<>(1, 2), null);
// 獲取分頁數據
List<Employee> emps = page.getRecords();
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());
}
執行結果:
獲取總條數:4
獲取當前頁碼:1
獲取總頁碼:2
獲取每頁顯示的數據條數:2
是否有上一頁:false
是否有下一頁:true
執行分析插件
該插件的作用是分析update和delete操作,防止小白誤操作或惡意的攻擊,需要注意的是該插件僅支持MySQL5.6.3以上版本。
使用也非常簡單,首先將插件注冊到容器中:
@Bean
public BlockAttackInnerInterceptor blockAttackInnerInterceptor(){
return new BlockAttackInnerInterceptor();
}
此時我們執行全表刪除操作:
@Test
void contextLoads() {
employeeMapper.delete(null);
}
MyBatisPlus會幫助我們自動停止該操作,并拋出一個異常。
以上便是有關MyBatisPlus的全部內容。