目录
4.2.1、mapUnderscoreToCamelCase
1. Mybatis-Plus概念
1.1 Mybatis-Plus介绍
官⽹:https://mp.baomidou.com/
MyBatis-Plus(简称 MP)是⼀个 MyBatis 的增强⼯具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提
⾼效率⽽⽣。
1.2 特性
- ⽆侵⼊:只做增强不做改变,引⼊它不会对现有⼯程产⽣影响,如丝般顺滑。
- 损耗⼩:启动即会⾃动注⼊基本 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 安装
全新的 MyBatis-Plus 3.0 版本基于 JDK8,提供了 lambda 形式的调⽤,所以安装集成 MP3.0 要求如下:
- JDK 8+
- Maven or Gradle
Spring Boot(Spring Boot工程中集成Mybatis-Plus)
Maven:
com.baomidou mybatis-plus-boot-starter 3.4.0
Spring MVC(普通Maven工程集成Mybatis-Plus)
Maven:
com.baomidou mybatis-plus 3.4.0
对于Mybatis整合MP有常常有三种⽤法,分别是Mybatis+MP、Spring+Mybatis+MP、SpringBoot+Mybatis+MP。
2.2 创建数据库以及表
-- 创建测试表DROP TABLE IF EXISTS tb_user;CREATE TABLE user( id BIGINT(20) NOT NULL COMMENT '主键ID', name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名', age INT(11) NULL DEFAULT NULL COMMENT '年龄', email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱', PRIMARY KEY (id) );-- 插⼊测试数据INSERT INTO user (id, name, age, email) VALUES(1, 'Jone', 18, 'test1@baomidou.com'), (2, 'Jack', 20, 'test2@baomidou.com'), (3, 'Tom', 28, 'test3@baomidou.com'), (4, 'Sandy', 21, 'test4@baomidou.com'),(5, 'Billie', 24, 'test5@baomidou.com');
2.3 创建⼯程
创建一个Maven工程 com.zm.mp
导⼊依赖:
com.baomidou mybatis-plus 3.1.1 mysql mysql-connector-java 5.1.47 com.alibaba druid 1.0.11 org.projectlombok lombok 1.18.4 junit junit 4.12 org.slf4j slf4j-log4j12 1.6.4 org.apache.maven.plugins maven-compiler-plugin 1.8
2.4 Mybatis + MP
下⾯演示,通过纯Mybatis与Mybatis-Plus整合。
创建⼦Module: zm-mybatis-plus-simple
zm-mybatis-plus com.zm.mp 1.0-SNAPSHOT 4.0.0 zm-mybatis-plus-simple
引入log4j.properties:
log4j.rootLogger=DEBUG,A1log4j.appender.A1=org.apache.log4j.ConsoleAppenderlog4j.appender.A1.layout=org.apache.log4j.PatternLayoutlog4j.appender.A1.layout.ConversionPattern=[%t] [%c]-[%p] %m%n
Mybatis实现查询User
第⼀步,编写sqlMapConfig.xml⽂件:
第⼆步,编写User实体对象:(这⾥使⽤lombok进⾏了进化bean操作)
@Data // getter setter @toString@NoArgsConstructor@AllArgsConstructorpublic class User { private Long id; private String name; private Integer age; private String email; }
第三步,编写UserMapper接⼝:
public interface UserMapper { List findAll();}
第四步,编写UserMapper.xml⽂件:
第五步,编写TestMybatis测试⽤例:
public class MPTest { @Test public void test1() throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession sqlSession = sqlSessionFactory.openSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List all = mapper.findAll(); for (User user : all) { System.out.println(user); } }}
测试结果:
注:如果实体类名称和表名称不⼀致,可以在实体类上添加注解@TableName("指定数据库表名")
Mybatis+MP实现查询User
第⼀步,将UserMapper继承BaseMapper,将拥有了BaseMapper中的所有⽅法:
import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.zm.pojo.User;public interface UserMapper extends BaseMapper {}
第⼆步,使⽤MP中的MybatisSqlSessionFactoryBuilder进行构建:
@Testpublic void test2()throws IOException{ InputStream resourceAsStream=Resources.getResourceAsStream("sqlMapConfig.xml"); //这⾥使⽤的是MP中的MybatisSqlSessionFactoryBuilder SqlSessionFactory sqlSessionFactory=new MybatisSqlSessionFactoryBuilder().build(resourceAsStream); SqlSession sqlSession=sqlSessionFactory.openSession(); UserMapper mapper=sqlSession.getMapper(UserMapper.class); // 可以调⽤BaseMapper中定义的⽅法 // List all = mapper.findAll(); List all=mapper.selectList(null); for(User user:all){ System.out.println(user); }}
测试:
注:如果实体类名称和表名称不⼀致,可以在实体类上添加注解@TableName("指定数据库表名")。
简单说明:
- 由于使⽤了 MybatisSqlSessionFactoryBuilder进⾏了构建,继承的BaseMapper中的⽅法就载⼊到了SqlSession中,所以就可以直接使⽤相关的⽅法;
- 如图:
2.5 Spring + Mybatis + MP
引⼊了Spring框架,数据源、构建等⼯作就交给了Spring管理。
创建⼦Module(zm-mybatis-plus-spring)
zm-mybatis-plus com.zm.mp 1.0-SNAPSHOT 4.0.0 zm-mybatis-plus-spring 5.1.6.RELEASE org.springframework spring-webmvc ${spring.version} org.springframework spring-jdbc ${spring.version} org.springframework spring-test ${spring.version}
实现查询User
第⼀步,编写jdbc.properties:
jdbc.driver=com.mysql.jdbc.Driverjdbc.url=jdbc:mysql://127.0.0.1:3306/mp?serverTimezone=GMT%2B8&useSSL=falsejdbc.username=rootjdbc.password=root
第⼆步,编写applicationContext.xml
第三步,编写User对象以及UserMapper接⼝:
@Data@NoArgsConstructor@AllArgsConstructorpublic class User { private Long id; private String name; private Integer age; private String email; }
public interface UserMapper extends BaseMapper { List findAll();}
第四步,编写测试⽤例:
@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(locations = "classpath:applicationContext.xml")public class TestSpringMP { @Autowired private UserMapper userMapper; @Test public void test2() throws IOException { List users = this.userMapper.selectList(null); for (User user : users) { System.out.println(user); } }}
2.6 SpringBoot + Mybatis + MP
使⽤SpringBoot将进⼀步的简化MP的整合,需要注意的是,由于使⽤SpringBoot需要继承parent,所以需要重新创建⼯程,并不是创建⼦Module。
创建SpringBoot⼯程并导⼊对应依赖
org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-starter-logging org.springframework.boot spring-boot-starter-test test org.projectlombok lombok true com.baomidou mybatis-plus-boot-starter 3.1.1 mysql mysql-connector-java 5.1.47 org.slf4j slf4j-log4j12 org.springframework.boot spring-boot-maven-plugin
引入log4j.properties:
log4j.rootLogger=DEBUG,A1log4j.appender.A1=org.apache.log4j.ConsoleAppenderlog4j.appender.A1.layout=org.apache.log4j.PatternLayoutlog4j.appender.A1.layout.ConversionPattern=[%t] [%c]-[%p] %m%n
编写application.properties:
spring.datasource.driver-class-name=com.mysql.jdbc.Driverspring.datasource.url=jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL=falsespring.datasource.username=rootspring.datasource.password=root
编写pojo:
@Data@NoArgsConstructor@AllArgsConstructorpublic class User { private Long id; private String name; private Integer age; private String email; }
编写mapper:
public interface UserMapper extends BaseMapper {}
编写启动类:
package com.zm.mp;import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.WebApplicationType;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.boot.builder.SpringApplicationBuilder;@MapperScan("com.zm.mp.mapper") //设置mapper接⼝的扫描包@SpringBootApplicationpublic class MyApplication { public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); }}
编写测试⽤例测试:
package com.zm.mp;import com.zm.mp.mapper.UserMapper;import com.zm.mp.pojo.User;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import java.util.List;@RunWith(SpringRunner.class)@SpringBootTestpublic class UserMapperTest { @Autowired private UserMapper userMapper; @Test public void testSelect() { List userList = userMapper.selectList(null); for (User user : userList) { System.out.println(user); } }}
结果:
User(id=1, name=Jone, age=18, email=test1@baomidou.com)User(id=2, name=Jack, age=20, email=test2@baomidou.com)User(id=3, name=Tom, age=28, email=test3@baomidou.com)User(id=4, name=Sandy, age=21, email=test4@baomidou.com)User(id=5, name=Billie, age=24, email=test5@baomidou.com)
3. 通⽤CRUD
通过前⾯的学习,我们了解到通过继承BaseMapper就可以获取到各种各样的单表操作,接下来我们将详细讲解这些操作。
3.1 插⼊操作
⽅法定义
int insert(T entity);
测试⽤例
@RunWith(SpringRunner.class)@SpringBootTestpublic class UserMapperTest { @Autowired private UserMapper userMapper; @Test public void testInsert(){ User user = new User(); user.setAge(18); user.setEmail("123456@qq.com"); user.setName("张三"); //返回的result是受影响的⾏数,并不是⾃增后的id int result = userMapper.insert(user); System.out.println(result); System.out.println(user.getId()); }}
结果:
查看数据库表中数据:
通过数据库可以看到,数据已经写⼊到了数据库,但是,id的值不正确,我们期望的是数据库⾃增⻓,实际是MP⽣成了id的值写⼊到了数据库。
如何设置id的⽣成策略呢?
MP⽀持的id策略:
package com.baomidou.mybatisplus.annotation;import lombok.Getter;@Getterpublic enum IdType { AUTO(0), NONE(1), INPUT(2), ID_WORKER(3), UUID(4), ID_WORKER_STR(5); private final int key; IdType(int key) { this.key = key; }}
修改User对象:
package com.zm.mp.pojo;import com.baomidou.mybatisplus.annotation.IdType;import com.baomidou.mybatisplus.annotation.TableId;import com.baomidou.mybatisplus.annotation.TableName;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;@Data@NoArgsConstructor@AllArgsConstructor@TableName("tb_user")public class User { @TableId(type = IdType.AUTO) //指定id类型为⾃增⻓ private Long id; private String userName; private String password; private String name; private Integer age; private String email;}
注意:由于我们刚开始创建表的时候并没有指定主键id自增,这里的用@TableId设置主键自增策略时需要在数据表中也要设置主键自增,所以我们修改一下User表中的id字段为自增。再测试。
通过测试发现数据插⼊成功,id的值也是正确的。
@TableField
在MP中通过@TableField注解可以指定字段的⼀些属性,常常解决的问题有2个:
对象中的属性名和字段名不⼀致的问题(⾮驼峰)
2、对象中的属性字段在表中不存在的问题
使⽤:
其他⽤法,如⼤字段不加⼊查询字段(意思就是查询的时候不返回该字段的值):
效果:
3.2 更新操作
在MP中,更新操作有2种,⼀种是根据id更新,另⼀种是根据条件更新。
3.2.1 根据id更新
⽅法定义:
int updateById(@Param(Constants.ENTITY) T entity);
测试:
@RunWith(SpringRunner.class)@SpringBootTestpublic class UserMapperTest { @Autowired private UserMapper userMapper; @Test public void testUpdateById() { User user = new User(); user.setId(5L); //主键 user.setAge(21); //更新的字段 //根据id更新,更新不为null的字段 this.userMapper.updateById(user); }}
3.2.2 根据条件更新
⽅法定义:
int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) WrapperupdateWrapper);
测试⽤例:
package com.zm.mp;import com.zm.mp.mapper.UserMapper;import com.zm.mp.pojo.User;import com.baomidou.mybatisplus.core.conditions.Wrapper;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;import net.minidev.json.writer.UpdaterMapper;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import java.util.List;@RunWith(SpringRunner.class)@SpringBootTestpublic class UserMapperTest { @Autowired private UserMapper userMapper; @Test public void testUpdate() { User user = new User(); //更新的字段 user.setAge(30); //更新的条件 QueryWrapper wrapper = new QueryWrapper<>(); wrapper.eq("name", "张三"); //执⾏更新操作 int result = this.userMapper.update(user, wrapper); System.out.println("result = " + result); }}
或者,通过UpdateWrapper进⾏更新:
@Testpublic void testUpdate(){ //更新的条件以及字段 UpdateWrapper wrapper=new UpdateWrapper<>(); wrapper.eq("name","张三").set("age",30); //执⾏更新操作 int result=this.userMapper.update(null,wrapper); System.out.println("result = "+result);}
均可达到更新的效果。
3.3 删除操作
3.3.1 deleteById(根据 ID 删除)
⽅法定义:
int deleteById(Serializable id);
测试⽤例:
package com.zm.mp;import com.zm.mp.mapper.UserMapper;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;@RunWith(SpringRunner.class)@SpringBootTestpublic class UserMapperTest { @Autowired private UserMapper userMapper; @Test public void testDeleteById() { //执⾏删除操作 int result = this.userMapper.deleteById(5L); System.out.println("result = " + result); }}
3.3.2 deleteByMap(根据 columnMap 条件,删除记录)
⽅法定义:
int deleteByMap(@Param(Constants.COLUMN_MAP) Map columnMap);
测试⽤例:
package com.zm.mp;import com.zm.mp.mapper.UserMapper;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import java.util.HashMap;import java.util.Map;@RunWith(SpringRunner.class)@SpringBootTestpublic class UserMapperTest { @Autowired private UserMapper userMapper; @Test public void testDeleteByMap() { Map columnMap = new HashMap<>(); columnMap.put("age", 30); columnMap.put("name", "张三"); //将columnMap中的元素设置为删除的条件,多个之间为and关系 int result = this.userMapper.deleteByMap(columnMap); System.out.println("result = " + result); }}
3.3.3 delete(根据 entity 条件,删除记录)
⽅法定义:
int delete(@Param(Constants.WRAPPER) Wrapper wrapper);
测试⽤例:
package com.lagou.mp;import com.zm.mp.mapper.UserMapper;import com.zm.mp.pojo.User;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import java.util.HashMap;import java.util.Map;@RunWith(SpringRunner.class)@SpringBootTestpublic class UserMapperTest { @Autowired private UserMapper userMapper; @Test public void testDeleteByMap() { User user = new User(); user.setAge(30); user.setName("张三"); //将实体对象进⾏包装,包装为操作条件 QueryWrapper wrapper = new QueryWrapper<>(user); int result = this.userMapper.delete(wrapper); System.out.println("result = " + result); }}
3.3.4 deleteBatchIds(根据ID 批量删除)
⽅法定义:
int deleteBatchIds(@Param(Constants.COLLECTION) Collection extends Serializable>idList);
测试⽤例:
package com.zm.mp;import com.zm.mp.mapper.UserMapper;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import java.util.Arrays;@RunWith(SpringRunner.class)@SpringBootTestpublic class UserMapperTest { @Autowired private UserMapper userMapper; @Test public void testDeleteByMap() { //根据id集合批量删除 int result = this.userMapper.deleteBatchIds(Arrays.asList(1L, 10L, 20L)); System.out.println("result = " + result); }}
3.4 查询操作
MP提供了多种查询操作,包括根据id查询、批量查询、查询单条数据、查询列表、分⻚查询等操作。
3.4.1、selectById(根据 ID 查询)
⽅法定义:
T selectById(Serializable id);
测试⽤例:
package com.zm.mp;import com.zm.mp.mapper.UserMapper;import com.zm.mp.pojo.User;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;@RunWith(SpringRunner.class)@SpringBootTestpublic class UserMapperTest { @Autowired private UserMapper userMapper; @Test public void testSelectById() { //根据id查询数据 User user = this.userMapper.selectById(2L); System.out.println("result = " + user); }}
结果:
result = User(id=2, name=Jack, age=20, email=test2@baomidou.com)
3.4.2、selectBatchIds(根据ID 批量查询)
⽅法定义:
List selectBatchIds(@Param(Constants.COLLECTION) Collection extends Serializable>idList);
测试⽤例:
package com.zm.mp;import com.zm.mp.mapper.UserMapper;import com.zm.mp.pojo.User;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import java.util.Arrays;import java.util.List;@RunWith(SpringRunner.class)@SpringBootTestpublic class UserMapperTest { @Autowired private UserMapper userMapper; @Test public void testSelectBatchIds() { //根据id集合批量查询 List users = this.userMapper.selectBatchIds(Arrays.asList(2L, 3L)); for (User user : users) { System.out.println(user); } }}
结果:
User(id=2, name=Jack, age=20, email=test2@baomidou.com)User(id=3, name=Tom, age=28, email=test3@baomidou.com)
3.4.3、selectOne(根据 entity 条件,查询⼀条记录,如果查询结果超过一条会报错)
⽅法定义:
T selectOne(@Param(Constants.WRAPPER) Wrapper queryWrapper);
测试用例:
package com.zm.mp;import com.zm.mp.mapper.UserMapper;import com.zm.mp.pojo.User;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;@RunWith(SpringRunner.class)@SpringBootTestpublic class UserMapperTest { @Autowired private UserMapper userMapper; @Test public void testSelectOne() { QueryWrapper wrapper = new QueryWrapper(); wrapper.eq("name", "jack"); //根据条件查询⼀条数据,如果结果超过⼀条会报错 User user = this.userMapper.selectOne(wrapper); System.out.println(user); }}
结果:
User(id=2, name=Jack, age=20, email=test2@baomidou.com)
3.4.4、selectCount(根据 Wrapper 条件,查询总记录数)
⽅法定义:
Integer selectCount(@Param(Constants.WRAPPER) Wrapper queryWrapper);
测试用例:
package com.zm.mp;import com.zm.mp.mapper.UserMapper;import com.zm.mp.pojo.User;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;@RunWith(SpringRunner.class)@SpringBootTestpublic class UserMapperTest { @Autowired private UserMapper userMapper; @Test public void testSelectCount() { QueryWrapper wrapper = new QueryWrapper(); wrapper.gt("age", 23); //年龄⼤于23岁 //根据条件查询数据条数 Integer count = this.userMapper.selectCount(wrapper); System.out.println("count = " + count); }}}
结果:
count = 2
3.4.5、selectList(根据 entity 条件,查询全部记录)
⽅法定义:
List selectList(@Param(Constants.WRAPPER) Wrapper queryWrapper);
测试用例:
package com.zm.mp;import com.zm.mp.mapper.UserMapper;import com.zm.mp.pojo.User;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import java.util.List;@RunWith(SpringRunner.class)@SpringBootTestpublic class UserMapperTest { @Autowired private UserMapper userMapper; @Test public void testSelectList() { QueryWrapper wrapper = new QueryWrapper(); wrapper.gt("age", 23); //年龄⼤于23岁 //根据条件查询数据 List users = this.userMapper.selectList(wrapper); for (User user : users) { System.out.println("user = " + user); } }}
结果:
user = User(id=3, name=Tom, age=28, email=test3@baomidou.com)user = User(id=5, name=Billie, age=24, email=test5@baomidou.com)
3.4.6、selectPage(根据 entity 条件,查询全部记录(并分⻚))
⽅法定义:
IPage selectPage(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper);
配置分页插件:
package com.zm.mp;import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;import org.mybatis.spring.annotation.MapperScan;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configuration@MapperScan("com.zm.mp.mapper") //设置mapper接⼝的扫描包public class MybatisPlusConfig { @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); }}
测试用例:
package com.zm.mp;import com.zm.mp.mapper.UserMapper;import com.zm.mp.pojo.User;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import com.baomidou.mybatisplus.core.metadata.IPage;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import java.util.List;@RunWith(SpringRunner.class)@SpringBootTestpublic class UserMapperTest { @Autowired private UserMapper userMapper; @Test public void testSelectPage() { QueryWrapper wrapper = new QueryWrapper(); wrapper.gt("age", 10); //年龄⼤于10岁 //第一个参数:当前页 第二个参数:每页的条数 Page page = new Page<>(1, 2); //根据条件查询数据 IPage iPage = this.userMapper.selectPage(page, wrapper); System.out.println("数据总条数:" + iPage.getTotal()); System.out.println("总⻚数:" + iPage.getPages()); System.out.println("分页数据:" + iPage.getRecords()); }}
结果:
数据库表数据:
运行结果:
3.5 SQL注⼊的原理
前⾯我们已经知道,MP在启动后会将BaseMapper中的⼀系列的⽅法注册到meppedStatements中,那么究竟是如何注⼊的呢?流程⼜是怎么样的?下⾯我们将⼀起来分析下。
在MP中,ISqlInjector负责SQL的注⼊⼯作,它是⼀个接⼝,AbstractSqlInjector是它的实现类,实现关系如下:
在AbstractSqlInjector中,主要是由inspectInject()⽅法进⾏注⼊的,如下:
@Overridepublic void inspectInject(MapperBuilderAssistant builderAssistant,Class>mapperClass){ Class> modelClass=extractModelClass(mapperClass); if(modelClass!=null){ String className=mapperClass.toString(); Set mapperRegistryCache=GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration()); if(!mapperRegistryCache.contains(className)){ List methodList=this.getMethodList(); if(CollectionUtils.isNotEmpty(methodList)){ TableInfo tableInfo=TableInfoHelper.initTableInfo(builderAssistant,modelClass); // 循环注⼊⾃定义⽅法 methodList.forEach(m->m.inject(builderAssistant,mapperClass,modelClass,tableInfo)); }else{ logger.debug(mapperClass.toString()+", No effective injection method was found."); } mapperRegistryCache.add(className); } }}
在实现⽅法中, methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass,tableInfo)); 是关键,循环遍历⽅法,进⾏注⼊。
最终调⽤抽象⽅法injectMappedStatement进⾏真正的注⼊:
public abstract MappedStatement injectMappedStatement(Class> mapperClass, Class> modelClass, TableInfo tableInfo);
查看该⽅法的实现:
以SelectById为例查看:
public class SelectById extends AbstractMethod { @Override public MappedStatement injectMappedStatement(Class> mapperClass, Class>modelClass, TableInfo tableInfo) { SqlMethod sqlMethod = SqlMethod.LOGIC_SELECT_BY_ID; SqlSource sqlSource = new RawSqlSource(configuration, String.format(sqlMethod.getSql(), sqlSelectColumns(tableInfo, false), tableInfo.getTableName(), tableInfo.getKeyColumn(), tableInfo.getKeyProperty(), tableInfo.getLogicDeleteSql(true, false)), Object.class); return this.addSelectMappedStatement(mapperClass, sqlMethod.getMethod(), sqlSource, modelClass, tableInfo); }}
可以看到,⽣成了SqlSource对象,再将SQL通过addSelectMappedStatement⽅法添加到meppedStatements中。
4. 配置
在MP中有⼤量的配置,其中有⼀部分是Mybatis原⽣的配置,另⼀部分是MP的配置,详情:
下⾯我们对常⽤的配置做讲解。
4.1、基本配置
4.1.1、configLocation
MyBatis 配置⽂件位置,如果有单独的 MyBatis 配置,请将其路径配置到 configLocation 中。 MyBatisConfiguration 的具体内容请参考MyBatis 官⽅⽂档
Spring Boot环境中(在application.properties中加载mybatis的核心配置文件sqlMapConfig.xml):
#加载全局配置文件mybatis-plus.config-location = classpath:sqlMapConfig.xml
Spring MVC:
4.1.2、mapperLocations
MyBatis Mapper 所对应的 XML ⽂件位置,如果在 Mapper 中有⾃定义⽅法(XML 中有⾃定义实现),需要进⾏该配置,告诉 Mapper 所对应的 XML ⽂件位置。
Spring Boot:
#加载映射配置文件mybatis-plus.mapper-locations = classpath*:mapper // sql语句--> SELECT * FROM user WHERE name = ? AND age IS NULL wrapper.allEq(params); // sql语句--> SELECT * FROM user WHERE name = ? wrapper.allEq(params,false); // sql语句--> SELECT * FROM user WHERE name = ? AND age IS NULL wrapper.allEq((k, v) -> (k.equals("name") || k.equals("age")),params); // sql语句--> SELECT * FROM user WHERE name = ? wrapper.allEq((k, v) -> (k.equals("name")),params); List users = this.userMapper.selectList(wrapper); for (User user : users) { System.out.println(user); } }}
5.2、基本⽐较操作
- eq
-- 等于 = - ne
-- 不等于 <> - gt
-- ⼤于 > - ge
-- ⼤于等于 >= - lt
-- ⼩于 < - le
-- ⼩于等于 <= - between
-- BETWEEN 值1 AND 值2 - notBetween
-- NOT BETWEEN 值1 AND 值2 - in
-- 字段 IN (value.get(0), value.get(1), ...) - notIn
-- 字段 NOT IN (v0, v1, ...)
测试⽤例:
package com.zm.mp;import com.zm.mp.mapper.UserMapper;import com.zm.mp.pojo.User;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import java.util.List;@RunWith(SpringRunner.class)@SpringBootTestpublic class UserMapperTest { @Autowired private UserMapper userMapper; @Test public void testWrapper() { QueryWrapper wrapper = new QueryWrapper<>(); //SELECT * FROM user WHERE password = ? AND age >= ? AND name IN (?,?,?) wrapper.eq("email", "test2@baomidou.com") .ge("age", 20) .in("name", "jack", "jone", "tom"); List users = this.userMapper.selectList(wrapper); for (User user : users) { System.out.println(user); } }}
5.3、模糊查询
- like
-- LIKE '%值%'
-- 例: like("name", "王") ---> name like '%王%' - notLike
-- NOT LIKE '%值%'
-- 例: notLike("name", "王") ---> name not like '%王%' - likeLeft
-- LIKE '%值' 例: likeLeft("name", "王") ---> name like '%王' - likeRight
-- LIKE '值%'
-- 例: likeRight("name", "王") ---> name like '王%'
测试⽤例:
package com.zm.mp;import com.zm.mp.mapper.UserMapper;import com.zm.mp.pojo.User;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import java.util.List;@RunWith(SpringRunner.class)@SpringBootTestpublic class UserMapperTest { @Autowired private UserMapper userMapper; @Test public void testWrapper() { QueryWrapper wrapper = new QueryWrapper<>(); //SELECT * FROM user WHERE name LIKE ? // 即:查询姓名中 包含'子' 的这条数据 //Parameters: %⼦%(String) wrapper.like("name", "⼦"); List users = this.userMapper.selectList(wrapper); for (User user : users) { System.out.println(user); } }}
5.4、排序
- orderBy
-- 排序:ORDER BY 字段, ...
-- 例: orderBy(true, true, "id", "name") ---> order by id ASC,name ASC - orderByAsc(正序或升序)
-- 排序:ORDER BY 字段, ... ASC
--- 例: orderByAsc("id", "name") ---> order by id ASC,name ASC - orderByDesc(倒序或降序)
-- 排序:ORDER BY 字段, ... DESC
-- 例: orderByDesc("id", "name") ---> order by id DESC,name DESC
测试⽤例:
package com.zm.mp;import com.zm.mp.mapper.UserMapper;import com.zm.mp.pojo.User;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import java.util.List;@RunWith(SpringRunner.class)@SpringBootTestpublic class UserMapperTest { @Autowired private UserMapper userMapper; @Test public void testWrapper() { QueryWrapper wrapper = new QueryWrapper<>(); // SELECT * FROM user ORDER BY age DESC // 根据年龄倒序排序查找 wrapper.orderByDesc("age"); List users = this.userMapper.selectList(wrapper); for (User user : users) { System.out.println(user); } }}
5.5、逻辑查询
- or
-- 拼接 OR
-- 主动调⽤ or 表示紧接着下⼀个⽅法不是⽤ and 连接!(不调⽤ or 则默认为使⽤ and 连接) - and
-- AND 嵌套
-- 例: and(i -> i.eq("name", "李⽩").ne("status", "活着")) ---> and (name = '李⽩' and status <> '活着')
测试⽤例:
package com.zm.mp;import com.zm.mp.mapper.UserMapper;import com.zm.mp.pojo.User;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import java.util.List;@RunWith(SpringRunner.class)@SpringBootTestpublic class UserMapperTest { @Autowired private UserMapper userMapper; @Test public void testWrapper() { QueryWrapper wrapper = new QueryWrapper<>(); //SELECT * FROM user WHERE name = ? OR age = ? wrapper.eq("name", "jack").or().eq("age", 24); List users = this.userMapper.selectList(wrapper); for (User user : users) { System.out.println(user); } }}
5.6、select
在MP查询中,默认查询所有的字段。如果有需要也可以通过select⽅法进⾏指定字段查询。
package com.zm.mp;import com.zm.mp.mapper.UserMapper;import com.zm.mp.pojo.User;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import java.util.List;@RunWith(SpringRunner.class)@SpringBootTestpublic class UserMapperTest { @Autowired private UserMapper userMapper; @Test public void testWrapper() { QueryWrapper wrapper = new QueryWrapper<>(); //SELECT id,name FROM user WHERE name = ? OR age = ? wrapper.eq("name", "jack") .or() .eq("age", 24) .select("id", "name"); List users = this.userMapper.selectList(wrapper); for (User user : users) { System.out.println(user); } }}
6. ActiveRecord
ActiveRecord(简称AR)⼀直⼴受动态语⾔( PHP 、 Ruby 等)的喜爱,⽽ Java 作为准静态语⾔,对于ActiveRecord 往往只能感叹其优雅,所以我们也在 AR 道路上进⾏了⼀定的探索,希望⼤家能够喜欢(来自官网)。
什么是ActiveRecord?
ActiveRecord也属于ORM(对象关系映射)层,由Rails最早提出,遵循标准的ORM模型:表映射到记录,记录映射到对象,字段映射到对象属性。配合遵循的命名和配置惯例,能够很⼤程度的快速实现模型的操作,⽽且简洁易懂。ActiveRecord的主要思想是:
- 每⼀个数据库表对应创建⼀个类,类的每⼀个对象实例对应于数据库中表的⼀⾏记录;通常表的每个字段在类中都有相应的Field;
- ActiveRecord同时负责把⾃⼰持久化,在ActiveRecord中封装了对数据库的访问,即CURD;;
- ActiveRecord是⼀种领域模型(Domain Model),封装了部分业务逻辑;
6.1、开启AR之旅
在MP中,开启AR⾮常简单,只需要将实体对象继承Model即可。
package com.zm.mp.pojo;import com.baomidou.mybatisplus.annotation.IdType;import com.baomidou.mybatisplus.annotation.TableField;import com.baomidou.mybatisplus.annotation.TableId;import com.baomidou.mybatisplus.annotation.TableName;import com.baomidou.mybatisplus.extension.activerecord.Model;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;@Data@NoArgsConstructor@AllArgsConstructorpublic class User extends Model { private Long id; private String userName; private String password; private String name; private Integer age; private String email;}
注意: 使用AR模式除了实体对象继承Model外,还必须存在一个对应的原始mapper(比如这个项目中User实体 对应的UserMapper)并继承BaseMapper。
6.2、根据主键查询
在之前的案例中我们查询都是先注入对应的userMapper ,然后用userMapper去调用对应的selectById()方法。但在AR模式下我们直接用user对象调用对应的方法即可,因为我们已经继承了Model。
@RunWith(SpringRunner.class)@SpringBootTestpublic class UserMapperTest { @Autowired private UserMapper userMapper; @Test public void testAR() { User user = new User(); user.setId(2L); User user2 = user.selectById(); System.out.println(user2); }}
6.3、新增数据
@RunWith(SpringRunner.class)@SpringBootTestpublic class UserMapperTest { @Autowired private UserMapper userMapper; @Test public void testARInsert() { User user = new User(); user.setName("李四"); user.setAge(30); user.setEmail("lisi@qq.com"); boolean insert = user.insert(); System.out.println(insert); }}
日志结果:
[main] [com.zm.mp.mapper.UserMapper.insert]-[DEBUG] ==> Preparing: INSERT INTOuser ( name, age, email ) VALUES ( ?, ?, ? )[main] [com.zm.mp.mapper.UserMapper.insert]-[DEBUG] ==> Parameters: 李四(String),30(Integer), lisi@qq.com(String)[main] [com.zm.mp.mapper.UserMapper.insert]-[DEBUG] <== Updates: 1
6.4、更新操作
@RunWith(SpringRunner.class)@SpringBootTestpublic class UserMapperTest { @Autowired private UserMapper userMapper; @Test public void testARUpdate() { User user = new User(); user.setId(8L); user.setAge(35); boolean update = user.updateById(); System.out.println(update); }}
日志结果:
[main] [com.zm.mp.mapper.UserMapper.updateById]-[DEBUG] ==> Preparing: UPDATEuser SET age=? WHERE id=?[main] [com.zm.mp.mapper.UserMapper.updateById]-[DEBUG] ==> Parameters: 35(Integer),8(Long)[main] [com.zm.mp.mapper.UserMapper.updateById]-[DEBUG] <== Updates: 1
6.5、删除操作
@RunWith(SpringRunner.class)@SpringBootTestpublic class UserMapperTest { @Autowired private UserMapper userMapper; @Test public void testARDelete() { User user = new User(); user.setId(7L); boolean delete = user.deleteById(); System.out.println(delete); }}
6.6、根据条件查询
@RunWith(SpringRunner.class)@SpringBootTestpublic class UserMapperTest { @Autowired private UserMapper userMapper; @Test public void testARFindById() { User user = new User(); QueryWrapper userQueryWrapper = new QueryWrapper<>(); userQueryWrapper.ge("age", "20"); List users = user.selectList(userQueryWrapper); for (User user1 : users) { System.out.println(user1); } }}
7. 插件
7.1、mybatis的插件机制
MyBatis 允许你在已映射语句执⾏过程中的某⼀点进⾏拦截调⽤。默认情况下,MyBatis 允许使⽤插件来拦截的⽅法调⽤包括:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
我们看到了可以拦截Executor接⼝的部分⽅法,⽐如update,query,commit,rollback等⽅法,还有其他接⼝的⼀些⽅法等。
总体概括为:
- 拦截执⾏器的⽅法
- 拦截参数的处理
- 拦截结果集的处理
- 拦截Sql语法构建的处理
拦截器示例:
package com.zm.plugins;import org.apache.ibatis.executor.Executor;import org.apache.ibatis.mapping.MappedStatement;import org.apache.ibatis.plugin.*;import java.util.Properties;@Intercepts({@Signature( type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})public class MyInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { //拦截⽅法,具体业务逻辑编写的位置 return invocation.proceed(); } @Override public Object plugin(Object target) { //创建target对象的代理对象,⽬的是将当前拦截器加⼊到该对象中 return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { //属性设置 }}
注⼊到Spring容器:
@Beanpublic MyInterceptor myInterceptor(){ return new MyInterceptor();}
或者通过xml配置,mybatis-config.xml:
7.2、执⾏分析插件
在MP中提供了对SQL执⾏的分析的插件,可⽤作阻断全表更新、删除的操作。
注意:该插件仅适⽤于开发环境,不适⽤于⽣产环境。
SpringBoot配置:
@Beanpublic SqlExplainInterceptor sqlExplainInterceptor(){ SqlExplainInterceptor sqlExplainInterceptor=new SqlExplainInterceptor(); List sqlParserList=new ArrayList<>(); // 攻击 SQL 阻断解析器、加⼊解析链 sqlParserList.add(new BlockAttackSqlParser()); sqlExplainInterceptor.setSqlParserList(sqlParserList); return sqlExplainInterceptor;}
测试:
@Testpublic void testUpdate(){ User user=new User(); user.setAge(20); int result=this.userMapper.update(user,null); System.out.println("result = "+result);}
结果:
Caused by: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Prohibitionof table update operation at com.baomidou.mybatisplus.core.toolkit.ExceptionUtils.mpe(ExceptionUtils.java:49) at com.baomidou.mybatisplus.core.toolkit.Assert.isTrue(Assert.java:38) at com.baomidou.mybatisplus.core.toolkit.Assert.notNull(Assert.java:72) atcom.baomidou.mybatisplus.extension.parsers.BlockAttackSqlParser.processUpdate(BlockAttackSqlParser.java:45) atcom.baomidou.mybatisplus.core.parser.AbstractJsqlParser.processParser(AbstractJsqlParser.java:92) atcom.baomidou.mybatisplus.core.parser.AbstractJsqlParser.parser(AbstractJsqlParser.java:67) atcom.baomidou.mybatisplus.extension.handlers.AbstractSqlParserHandler.sqlParser(AbstractSqlParserHandler.java:76) atcom.baomidou.mybatisplus.extension.plugins.SqlExplainInterceptor.intercept(SqlExplainInterceptor.java:63) at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:61) at com.sun.proxy.$Proxy70.update(Unknown Source) atorg.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:197) ... 41 more
可以看到,当执⾏全表更新时,会抛出异常,这样有效防⽌了⼀些误操作。
7.3、性能分析插件
性能分析拦截器,⽤于输出每条 SQL 语句及其执⾏时间,可以设置最⼤执⾏时间,超过时间会抛出异常。
该插件只⽤于开发环境,不建议⽣产环境使⽤。
配置:
javaconfig⽅式:
@Beanpublic PerformanceInterceptor performanceInterceptor(){ PerformanceInterceptor performanceInterceptor=new PerformanceInterceptor(); // 设置sql语句的最大执行时间(单位毫秒) performanceInterceptor.setMaxTime(100); // 设置sql是否格式化显示 performanceInterceptor.setFormat(true); return performanceInterceptor;}
xml⽅式:
执⾏结果:
可以看到,执⾏时间为75ms。如果将maxTime设置为1,那么,该操作会抛出异常:
7.4、乐观锁插件
乐观锁是相对悲观锁而言的,乐观锁假设数据一般情况不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果冲突,则返回给用户异常信息,让用户决定如何去做。乐观锁适用于读多写少的场景,这样可以提高程序的吞吐量。
7.4.1、主要适⽤场景
意图:
当要更新⼀条记录的时候,希望这条记录没有被别⼈更新。
乐观锁实现⽅式:
- 取出记录时,获取当前version;
- 更新时,带上这个version;
- 执⾏更新时, set version = newVersion where version = oldVersion;
- 如果version不对,就更新失败。
7.4.2、插件配置
spring xml(xml方式):
spring boot(javaconfig方式):
@Configuration@MapperScan("com.zm.mapper")public class MybatisPlusConfig { @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor(); } }
7.4.3、注解实体字段
需要为实体字段添加@Version注解。
第⼀步,为表添加version字段,并且设置初始值为1:
ALTER TABLE `user`ADD COLUMN `version` int(10) NULL AFTER `email`;UPDATE `user` SET `version`='1';
第⼆步,为User实体对象添加version字段,并且添加@Version注解:
@Versionprivate Integer version;
7.4.4、测试更新
测试⽤例:
@Test public void testUpdate03(){ //第一步:查询id为6的用户 User user = userMapper.selectById(6L); //第二步:对该用户信息进行修改 user.setName("张三_update"); //第三步:执行更新操作 int i = userMapper.updateById(user); System.out.println(i); }
执⾏结果:
通过日志记录可以看到,更新的条件中有version条件,并且更新的version为2。
模拟一下并发过程 事务一修改数据未提交、事务二执行插入(乐观锁插件本质)。
@Test void testUpdate04(){ // 线程1 查询id为6的用户 User user1 = userMapper.selectById(6L); // 线程1 对用户信息进行修改 user1.setName("change By user1"); // 线程2 查询id为6的用户 User user2 = userMapper.selectById(6L); // 线程B 对用户信息进行修改 user2.setName("chang By user2"); // 执行更新操作 int i2 = userMapper.updateById(user2); int i1 = userMapper.updateById(user1); System.out.println("user2修改结果:"+i2 ); System.out.println("user1修改结果:"+i1 ); }
运行结果:事务1 更新失败,事务2更新成功
7.4.5、特别说明
- ⽀持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
- 整数类型下 newVersion = oldVersion + 1
- newVersion 会回写到 entity 中
- 仅⽀持 updateById(id) 与 update(entity, wrapper) ⽅法
- 在 update(entity, wrapper) ⽅法下, wrapper 不能复⽤!!!
8. Sql 注⼊器
我们已经知道,在MP中,通过AbstractSqlInjector将BaseMapper中的⽅法注⼊到了Mybatis容器,这样这些⽅法才可以正常执⾏。
那么,如果我们需要扩充BaseMapper中的⽅法,⼜该如何实现呢?
下⾯我们以扩展findAll⽅法为例进⾏学习。
8.1、编写MyBaseMapper继承BaseMapper
package com.zm.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import java.util.List;public interface MyBaseMapper extends BaseMapper { List findAll();}
其他的Mapper都可以继承该Mapper,这样实现了统⼀的扩展。
如:
package com.zm.mapper;import com.zm.pojo.User;public interface UserMapper extends MyBaseMapper { User findById(Long id);}
8.2、编写MySqlInjector(自定义sql注入器)
如果直接继承AbstractSqlInjector的话,原有的BaseMapper中的⽅法将失效,所以我们选择继承
DefaultSqlInjector进⾏扩展。
package com.zm.sqlInjector;import com.baomidou.mybatisplus.core.injector.AbstractMethod;import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;import java.util.List;public class MySqlInjector extends DefaultSqlInjector { @Override public List getMethodList() { List methodList = super.getMethodList(); // 再扩充⾃定义的⽅法 methodList.add(new FindAll()); return methodList; }}
8.3、编写FindAll
package com.zm.sqlInjector;import com.baomidou.mybatisplus.core.enums.SqlMethod;import com.baomidou.mybatisplus.core.injector.AbstractMethod;import com.baomidou.mybatisplus.core.metadata.TableInfo;import org.apache.ibatis.mapping.MappedStatement;import org.apache.ibatis.mapping.SqlSource;public class FindAll extends AbstractMethod { @Override public MappedStatement injectMappedStatement(Class> mapperClass, Class> modelClass, TableInfo tableInfo) { String sqlMethod = "findAll"; String sql = "select * from " + tableInfo.getTableName(); SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass); return this.addSelectMappedStatement(mapperClass, sqlMethod, sqlSource, modelClass, tableInfo); }}
8.4、注册到Spring容器
@Beanpublic MySqlInjector mySqlInjector(){ return new MySqlInjector();}
8.5、测试
@Testpublic void testFindAll(){ List users = this.userMapper.findAll(); for (User user : users) { System.out.println(user); }}
运行结果:
⾄此,我们实现了全局扩展SQL注⼊器。
9. ⾃动填充功能
有些时候我们可能会有这样的需求,插⼊或者更新数据时,希望有些字段可以⾃动填充数据,⽐如密码、version等。在MP中提供了这样的功能,可以实现⾃动填充。
9.1、添加@TableField注解
@TableField(fill = FieldFill.INSERT) //插⼊数据时进⾏填充private String version;
为version属性添加⾃动填充功能,在新增数据时有效。
FieldFill提供了多种模式选择:
public enum FieldFill { DEFAULT, INSERT, UPDATE, INSERT_UPDATE}
9.2、自定义实现类实现元对象处理接口。
package com.zm.handler;import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;import org.apache.ibatis.reflection.MetaObject;import org.springframework.stereotype.Component;@Component // 生成MyMetaObjectHandler 的实例对象存到IOC容器中public class MyMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { //字段为空,可以进⾏填充 setFieldValByName("version", 1, metaObject); } @Override public void updateFill(MetaObject metaObject) { }}
9.3、测试
@Testpublic void testInsert(){ User user=new User(); user.setName("老王"); user.setAge(18); user.setEmail("666@qq.com"); int result=this.userMapper.insert(user); System.out.println("result = "+result);}
结果:通过结果我们可以看到数据库中version字段的值添加上了,自动填充成功。
注意事项:
- 填充原理是直接给
entity
的属性设置值!!!- 注解则是指定该属性在对应情况下必有值,如果无值则入库会是
null
MetaObjectHandler
提供的默认方法的策略均为:如果属性有值则不覆盖,如果填充值为null
则不填充- 字段必须声明
TableField
注解,属性fill
选择对应策略,该声明告知Mybatis-Plus
需要预留注入SQL
字段- 填充处理器
MyMetaObjectHandler
在 Spring Boot 中需要声明@Component
或@Bean
注入- 要想根据注解
FieldFill.xxx
和字段名
以及字段类型
来区分必须使用父类的strictInsertFill
或者strictUpdateFill
方法- 不需要根据任何来区分可以使用父类的
fillStrategy
方法- update(T t,Wrapper updateWrapper)时t不能为空,否则自动填充失效
10. 逻辑删除
开发系统时,有时候在实现功能时,删除操作需要实现逻辑删除,所谓逻辑删除就是将数据标记为删除,⽽并⾮真正的物理删除(⾮DELETE操作),查询时需要携带状态条件,确保被标记的数据不被查询到。这样做的⽬的就是避免数据被真正的删除。
MP就提供了这样的功能,⽅便我们使⽤,接下来我们⼀起学习下。
10.1、修改表结构
为tb_user表增加deleted字段,⽤于表示数据是否被删除,1代表删除,0代表未删除。
ALTER TABLE `user`ADD COLUMN `deleted` int(1) NULL DEFAULT 0 COMMENT '1代表删除,0代表未删除' AFTER `version`;
同时,也修改User实体,增加deleted属性并且添加@TableLogic注解:
@TableLogicprivate Integer deleted;
10.2、配置
application.properties:
# 逻辑已删除值(默认为 1)mybatis-plus.global-config.db-config.logic-delete-value=1# 逻辑未删除值(默认为 0)mybatis-plus.global-config.db-config.logic-not-delete-value=0
10.3、测试
@Testpublic void testDeleteById(){ this.userMapper.deleteById(8L);}
执⾏的日志与结果:
测试查询:
@Test public void selectById() { User user = this.userMapper.selectById(8L); System.out.println("查询到的结果为:" + user); }
执⾏的日志与结果:
至此,已经实现了逻辑删除。
11. 代码⽣成器
AutoGenerator 是 MyBatis-Plus 的代码⽣成器,通过 AutoGenerator 可以快速⽣成 Entity、Mapper、MapperXML、Service、Controller 等各个模块的代码,极⼤的提升了开发效率。
11.1、创建一个新的springboot⼯程
pom.xml:
4.0.0 org.springframework.boot spring-boot-starter-parent 2.3.4.RELEASE com.zm zm-mp-generator 0.0.1-SNAPSHOT zm-mp-generator Demo project for Spring Boot 11 org.springframework.boot spring-boot-starter-test test com.baomidou mybatis-plus-boot-starter 3.1.1 com.baomidou mybatis-plus-generator 3.1.1 org.springframework.boot spring-boot-starter-freemarker mysql mysql-connector-java 5.1.47 org.slf4j slf4j-log4j12 org.springframework.boot spring-boot-starter-web org.projectlombok lombok true org.springframework.boot spring-boot-maven-plugin
11.2、代码
package com.zm.generator;import java.util.ArrayList;import java.util.List;import java.util.Scanner;import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;import com.baomidou.mybatisplus.core.toolkit.StringPool;import com.baomidou.mybatisplus.core.toolkit.StringUtils;import com.baomidou.mybatisplus.generator.AutoGenerator;import com.baomidou.mybatisplus.generator.InjectionConfig;import com.baomidou.mybatisplus.generator.config.DataSourceConfig;import com.baomidou.mybatisplus.generator.config.FileOutConfig;import com.baomidou.mybatisplus.generator.config.GlobalConfig;import com.baomidou.mybatisplus.generator.config.PackageConfig;import com.baomidou.mybatisplus.generator.config.StrategyConfig;import com.baomidou.mybatisplus.generator.config.TemplateConfig;import com.baomidou.mybatisplus.generator.config.po.TableInfo;import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;public class MysqlGenerator { public static String scanner(String tip) { Scanner scanner = new Scanner(System.in); StringBuilder help = new StringBuilder(); help.append("请输⼊" + tip + ":"); System.out.println(help.toString()); if (scanner.hasNext()) { String ipt = scanner.next(); if (StringUtils.isNotEmpty(ipt)) { return ipt; } } throw new MybatisPlusException("请输⼊正确的" + tip + "!"); } public static void main(String[] args) { // 代码⽣成器 AutoGenerator mpg = new AutoGenerator(); // 全局配置 GlobalConfig gc = new GlobalConfig(); String projectPath = System.getProperty("user.dir"); gc.setOutputDir(projectPath + "/src/main/java"); gc.setAuthor("zm"); gc.setOpen(false); mpg.setGlobalConfig(gc); // 数据源配置 DataSourceConfig dsc = new DataSourceConfig(); dsc.setUrl("jdbc:mysql://127.0.0.1:3306/mp? useUnicode = true & useSSL = false & characterEncoding = utf8"); // dsc.setSchemaName("public"); dsc.setDriverName("com.mysql.jdbc.Driver"); dsc.setUsername("root"); dsc.setPassword("root"); mpg.setDataSource(dsc); // 包配置 PackageConfig pc = new PackageConfig(); pc.setModuleName(scanner("模块名")); pc.setParent("com.zm.mp.generator"); mpg.setPackageInfo(pc); // ⾃定义配置 InjectionConfig cfg = new InjectionConfig() { @Override public void initMap() { // to do nothing } }; List focList = new ArrayList<>(); focList.add(new FileOutConfig("/templates/mapper.xml.ftl") { @Override public String outputFile(TableInfo tableInfo) { // ⾃定义输⼊⽂件名称 return projectPath + "/zm-mp-generator/src/main/resources/mapper/" + pc.getModuleName() + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML; } }); cfg.setFileOutConfigList(focList); mpg.setCfg(cfg); mpg.setTemplate(new TemplateConfig().setXml(null)); // 策略配置 StrategyConfig strategy = new StrategyConfig(); strategy.setNaming(NamingStrategy.underline_to_camel); strategy.setColumnNaming(NamingStrategy.underline_to_camel); //strategy.setSuperEntityClass("com.baomidou.mybatisplus.samples.generator.common.BaseEnt ity"); strategy.setEntityLombokModel(true); //strategy.setSuperControllerClass("com.baomidou.mybatisplus.samples.generator.common.BaseController"); strategy.setInclude(scanner("表名")); strategy.setSuperEntityColumns("id"); strategy.setControllerMappingHyphenStyle(true); strategy.setTablePrefix(pc.getModuleName() + "_"); mpg.setStrategy(strategy); // 选择 freemarker 引擎需要指定如下加,注意 pom 依赖必须有! mpg.setTemplateEngine(new FreemarkerTemplateEngine()); mpg.execute(); }}
通过测试发现,代码已经生成。结构如下:
12. MybatisX 快速开发插件
MybatisX 是⼀款基于 IDEA 的快速开发插件,为效率⽽⽣。
安装⽅法:打开 IDEA,进⼊ File -> Settings -> Plugins,输⼊ mybatisx 搜索并安装。
功能:
- Java 与 XML 调回跳转
- Mapper ⽅法⾃动⽣成 XML
来源地址:https://blog.csdn.net/bier_zm/article/details/125808590