1. Mybatis-Plus概念 1.1 Mybatis-Plus介绍 官⽹: https://mybatis.plus/ 或 https://mp.baomidou.com/
Mybatis-Plus介绍
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 支持数据库 任何能使用 MyBatis
进行 CRUD, 并且支持标准 SQL 的数据库,具体支持情况如下,如果不在下列表查看分页部分教程 PR 您的支持。
MySQL,Oracle,DB2,H2,HSQL,SQLite,PostgreSQL,SQLServer,Phoenix,Gauss ,ClickHouse,Sybase,OceanBase,Firebird,Cubrid,Goldilocks,csiidb 达梦数据库,虚谷数据库,人大金仓数据库,南大通用(华库)数据库,南大通用数据库,神通数据库,瀚高数据库 1.4 框架结构
2. Mybatis-Plus快速⼊⻔ 2.1 安装 全新的 MyBatis-Plus 3.0 版本基于 JDK8,提供了 lambda 形式的调⽤,所以安装集成 MP3.0 要求如下:
Spring Boot添加依赖
<dependency > <groupId > com.baomidou</groupId > <artifactId > mybatis-plus-boot-starter</artifactId > <version > 3.4.0</version > </dependency >
Spring添加依赖
<dependency > <groupId > com.baomidou</groupId > <artifactId > mybatis-plus</artifactId > <version > 3.4.0</version > </dependency >
引入MyBatis-Plus依赖后,不要再次引入MyBatis和MyBatis-Spring依赖,以避免因版本差异导致的问题。
对于Mybatis整合MP有常常有三种⽤法,分别是Mybatis+MyBatis-Plus、Spring+Mybatis+MyBatis-Plus、Spring Boot+Mybatis+MyBatis-Plus。
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工程 <?xml version="1.0" encoding="UTF-8"?> <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion > 4.0.0</modelVersion > <groupId > com.lemon</groupId > <artifactId > mybatis-plus</artifactId > <packaging > pom</packaging > <version > 1.0-SNAPSHOT</version > <modules > <module > mybatis-plus-simple</module > </modules > <properties > <maven.compiler.source > 8</maven.compiler.source > <maven.compiler.target > 8</maven.compiler.target > </properties > <dependencies > <dependency > <groupId > com.baomidou</groupId > <artifactId > mybatis-plus</artifactId > <version > 3.4.0</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.47</version > </dependency > <dependency > <groupId > com.alibaba</groupId > <artifactId > druid</artifactId > <version > 1.0.11</version > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > <version > 1.18.4</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.12</version > </dependency > <dependency > <groupId > org.slf4j</groupId > <artifactId > slf4j-log4j12</artifactId > <version > 1.6.4</version > </dependency > </dependencies > <build > <plugins > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-compiler-plugin</artifactId > <configuration > <source > 1.8</source > <target > 1.8</target > </configuration > </plugin > </plugins > </build > </project >
2.4 Mybatis + MyBatis-Plus 下⾯演示,通过纯Mybatis与Mybatis-Plus整合。
1. 创建⼦Module pom.xml:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > <parent > <artifactId > mybatis-plus</artifactId > <groupId > com.lemon</groupId > <version > 1.0-SNAPSHOT</version > </parent > <modelVersion > 4.0.0</modelVersion > <artifactId > mybatis-plus-simple</artifactId > <properties > <maven.compiler.source > 8</maven.compiler.source > <maven.compiler.target > 8</maven.compiler.target > </properties > </project >
log4j.properties:
log4j.rootLogger =DEBUG,A1 log4j.appender.A1 =org.apache.log4j.ConsoleAppender log4j.appender.A1.layout =org.apache.log4j.PatternLayout log4j.appender.A1.layout.ConversionPattern =[%t] [%c]-[%p] %m%n
2. Mybatis实现查询User 编写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 > <properties resource ="jdbc.properties" > </properties > <environments default ="development" > <environment id ="development" > <transactionManager type ="JDBC" > </transactionManager > <dataSource type ="POOLED" > <property name ="driver" value ="${jdbc.driver}" /> <property name ="url" value ="${jdbc.url}" /> <property name ="username" value ="${jdbc.username}" /> <property name ="password" value ="${jdbc.password}" /> </dataSource > </environment > </environments > <mappers > <package name ="com.lemon.mapper" /> </mappers > </configuration >
编写User实体对象
@Data @NoArgsConstructor @AllArgsConstructor @TableName("user") public class User { private Long id; private String name; private Integer age; private String email; }
编写UserMapper接⼝
public interface UserMapper { List<User> findAll () ; }
编写UserMapper.xml⽂件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="com.lemon.mapper.UserMapper" > <insert id ="insert" parameterType ="com.lemon.pojo.User" > insert into .... </insert > <select id ="findAll" resultType ="com.lemon.pojo.User" > select * from user </select > </mapper >
编写TestMybatis测试⽤例
public class MPTest { @Test public void mybatisTest () throws Exception { InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml" ); SqlSessionFactory sqlSessionFactory = new MybatisSqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); List<User> userList = userMapper.findAll(); 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)
注:如果实体类名称和表名称不⼀致,可以在实体类上添加注解@TableName("指定数据库表名")
3. Mybatis+MyBatis-Plus实现查询User 将UserMapper继承BaseMapper,将拥有了BaseMapper中的所有⽅法
public interface UserMapper extends BaseMapper <User > {}
使⽤MP中的MybatisSqlSessionFactoryBuilder进程构建
@Test public void mybatisTest () throws Exception { InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml" ); SqlSessionFactory sqlSessionFactory = new MybatisSqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); List<User> 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)
注:如果实体类名称和表名称不⼀致,可以在实体类上添加注解@TableName(“指定数据库表名”) 简单说明: 由于使⽤了 MybatisSqlSessionFactoryBuilder
进⾏了构建,继承的BaseMapper中的⽅法就载⼊到了SqlSession
中,所以就可以直接使⽤相关的⽅法;
如图:
2.5 Spring + Mybatis + MyBatis-Plus 引⼊了Spring框架,数据源、构建等⼯作就交给了Spring管理。
1. 创建⼦Module <?xml version="1.0" encoding="UTF-8"?> <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > <parent > <artifactId > mybatis-plus</artifactId > <groupId > com.lemon</groupId > <version > 1.0-SNAPSHOT</version > </parent > <modelVersion > 4.0.0</modelVersion > <artifactId > mybatis-plus-spring</artifactId > <properties > <maven.compiler.source > 8</maven.compiler.source > <maven.compiler.target > 8</maven.compiler.target > <spring.version > 5.1.6.RELEASE</spring.version > </properties > <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-webmvc</artifactId > <version > ${spring.version}</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-jdbc</artifactId > <version > ${spring.version}</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-test</artifactId > <version > ${spring.version}</version > </dependency > </dependencies > </project >
2. 实现查询User 编写jdbc.properties
jdbc.driver =com.mysql.jdbc.Driver jdbc.url =jdbc:mysql://127.0.0.1:3306/mp?serverTimezone=GMT%2B8&useSSL=false jdbc.username =root jdbc.password =root
编写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" 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" > <context:property-placeholder location ="classpath:jdbc.properties" > </context:property-placeholder > <bean id ="dataSource" class ="com.alibaba.druid.pool.DruidDataSource" > <property name ="driverClassName" value ="${jdbc.driver}" /> <property name ="url" value ="${jdbc.url}" /> <property name ="username" value ="${jdbc.username}" /> <property name ="password" value ="${jdbc.password}" /> </bean > <bean id ="sqlSessionFactory" class ="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean" > <property name ="dataSource" ref ="dataSource" > </property > <property name ="configLocation" value ="classpath:sqlMapConfig.xml" /> </bean > <bean class ="org.mybatis.spring.mapper.MapperScannerConfigurer" > <property name ="basePackage" value ="com.lemon.mapper" /> </bean > </beans >
编写User对象以及UserMapper接⼝
@Data @NoArgsConstructor @AllArgsConstructor @TableName("user") public class User { private Long id; private String name; private Integer age; private String email; }
public interface UserMapper extends BaseMapper <User > { List<User> findAll () ; }
编写测试⽤例@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:applicationContext.xml") public class TestSpringMP { @Autowired private UserMapper userMapper; @Test public void test () { List<User> userList = userMapper.selectList(null ); for (User user : userList) { System.out.println(user); } } }
2.6 SpringBoot + Mybatis + MyBatis-Plus 使⽤SpringBoot将进⼀步的简化MyBatis-Plus的整合,需要注意的是,由于使⽤SpringBoot需要继承parent,所以需要重新创建⼯程,并不是创建⼦Module。
1. 创建⼯程并导入依赖 <?xml version="1.0" encoding="UTF-8"?> <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion > 4.0.0</modelVersion > <parent > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-parent</artifactId > <version > 2.3.4</version > <relativePath /> </parent > <groupId > com.lemon</groupId > <artifactId > mybatis-plus-springboot</artifactId > <version > 0.0.1-SNAPSHOT</version > <name > mybatis-plus-springboot</name > <description > mybatis-plus-springboot</description > <properties > <java.version > 1.8</java.version > </properties > <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter</artifactId > <exclusions > <exclusion > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-logging</artifactId > </exclusion > </exclusions > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-test</artifactId > <scope > test</scope > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > <optional > true</optional > </dependency > <dependency > <groupId > com.baomidou</groupId > <artifactId > mybatis-plus-boot-starter</artifactId > <version > 3.1.1</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.47</version > </dependency > <dependency > <groupId > org.slf4j</groupId > <artifactId > slf4j-log4j12</artifactId > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > RELEASE</version > <scope > test</scope > </dependency > </dependencies > <build > <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > </plugin > </plugins > </build > </project >
2. 编写application.properties spring.datasource.driver-class-name =com.mysql.jdbc.Driver spring.datasource.url =jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL=false spring.datasource.username =root spring.datasource.password =123456
3. 编写pojo @Data @NoArgsConstructor @AllArgsConstructor @TableName("user") public class User { private Long id; private String name; private Integer age; private String email; }
4. 编写mapper public interface UserMapper extends BaseMapper <User > {}
5. 编写启动类 @MapperScan("com.lemon.mapper") @SpringBootApplication public class MybatisPlusSpringbootApplication { public static void main (String[] args) { SpringApplication.run(MybatisPlusSpringbootApplication.class, args); } }
6. 编写测试⽤例 @RunWith(SpringRunner.class) @SpringBootTest class MybatisPlusSpringbootApplicationTests { @Autowired private UserMapper userMapper; @Test void testSelect () { List<User> 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 插⼊操作 1. ⽅法定义 2. 测试⽤例 @Test public void testInsert () { User user = new User(); user.setAge(18 ); user.setName("kobe" ); user.setEmail("123@qq.com" ); int result = userMapper.insert(user); System.out.println(result); System.out.println("id值为" + user.getId()); }
测试结果:
可以看到,数据已经写⼊到了数据库,但是,id的值不正确,我们期望的是数据库⾃增⻓,实际是MyBatis-Plus⽣成了id的值写⼊到了数据库。
如何设置id的⽣成策略呢?
MyBatis-Plus⽀持的id策略:
@Getter public 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对象:
@Data @NoArgsConstructor @AllArgsConstructor @TableName("user") public class User { @TableId(type = IdType.AUTO) private Long id; private String name; private Integer age; private String email; }
3. @TableField 在MP中通过@TableField注解可以指定字段的⼀些属性,常常解决的问题有2个:
对象中的属性名和字段名不⼀致的问题(⾮驼峰) 对象中的属性字段在表中不存在的问题 @Data @NoArgsConstructor @AllArgsConstructor @TableName("user") public class User { @TableId(type = IdType.AUTO) private Long id; private String name; private Integer age; @TableField(value = "email") private String mail; }
其他⽤法,如⼤字段不加⼊查询字段:
@Data @NoArgsConstructor @AllArgsConstructor @TableName("user") public class User { @TableId(type = IdType.AUTO) private Long id; @TableField(select = false) ) private String name; private Integer age; @TableField(value = "email") private String mail; }
效果:
还有数据表中不存在的字段以及版本号
@Data @NoArgsConstructor @AllArgsConstructor @TableName("user") public class User { @TableId(type = IdType.AUTO) private Long id; @TableField(select = true) private String name; private Integer age; @TableField(value = "email") private String mail; @TableField(exist = false) private String address; @Version @TableField(fill = FieldFill.INSERT) private Integer version; }
3.2 更新操作 在MyBatis-Plus中,更新操作有2种,⼀种是根据id更新,另⼀种是根据条件更新。
1. 根据id更新 ⽅法定义:
int updateById (@Param(Constants.ENTITY) T entity) ;
测试:
@Test public void testUpdateById () { User user = new User(); user.setId(6L ); user.setAge(24 ); int i = userMapper.updateById(user); System.out.println(i); }
结果:
2. 根据条件更新 ⽅法定义:
int update (@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper) ;
测试⽤例:
@Test public void testUpate () { User user = new User(); user.setAge(35 ); QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("name" , "kobe" ); int i = userMapper.update(user, queryWrapper); System.out.println(i); }
或者,通过UpdateWrapper进⾏更新:
@Test public void testUpate2 () { UpdateWrapper<User> updateWrapper = new UpdateWrapper<>(); updateWrapper.eq("id" , 6 ).set("age" , 40 ); int i = userMapper.update(null , updateWrapper); System.out.println(i); }
测试结果:
3.3 删除操作
1. deleteById ⽅法定义:
int deleteById (Serializable id) ;
测试⽤例:
@Test public void testDeleteById () { int i = userMapper.deleteById(6L ); System.out.println(i); }
测试结果:
2. deleteByMap ⽅法定义:
int deleteByMap (@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap) ;
测试⽤例:
@Test public void testDeleteByMap () { HashMap<String, Object> map = new HashMap<>(); map.put("name" , "kobe" ); map.put("age" , 18 ); int i = userMapper.deleteByMap(map); System.out.println(i); }
测试结果:
3. delete ⽅法定义:
int delete (@Param(Constants.WRAPPER) Wrapper<T> wrapper) ;
测试⽤例:
@Test public void testDelete () { QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("name" ,"kobe1" ).eq("age" ,18 ); int i = userMapper.delete(queryWrapper); System.out.println(i); }
或者如下:(两个是等效的)
@Test public void testDelete () { User user = new User(); user.setName("kobe2" ); user.setAge(18 ); QueryWrapper<User> queryWrapper = new QueryWrapper<>(user); int i = userMapper.delete(queryWrapper); System.out.println(i); }
测试结果:
deleteBatchIds ⽅法定义:
int deleteBatchIds (@Param(Constants.COLLECTION) Collection<? extends Serializable> idList) ;
测试⽤例:
@Test public void testDeleteBatchIds () { int i = userMapper.deleteBatchIds(Arrays.asList(6l , 7l , 8l , 9l )); System.out.println(i); }
测试结果:
3.4 查询操作 MyBatis-Plus提供了多种查询操作,包括根据id查询、批量查询、查询单条数据、查询列表、分⻚查询等操作。
1. selectById ⽅法定义:
T selectById (Serializable id) ;
测试⽤例:
@Test public void testSelectById () { User user = userMapper.selectById(2L ); System.out.println(user); }
测试结果:
result = User(id=2 , name=Jack, age=20 , email=test2@baomidou .com)
2. selectBatchIds ⽅法定义:
List<T> selectBatchIds (@Param(Constants.COLLECTION) Collection<? extends Serializable> idList) ;
测试⽤例:
@Test public void testSelectBatchIds () { List<User> users = userMapper.selectBatchIds(Arrays.asList(1L , 2l , 3l , 4l , 5l )); for (User user : users) { System.out.println(user); } }
测试结果:
3. selectOne ⽅法定义:
T selectOne (@Param(Constants.WRAPPER) Wrapper<T> queryWrapper) ;
测试⽤例:
@Test public void testSelectOne () { QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("name" ,"Jack" ); User user = userMapper.selectOne(queryWrapper); System.out.println(user); }
测试结果:
User(id=2 , name=Jack, age=20 , email=test2@baomidou .com)
4. selectCount ⽅法定义:
Integer selectCount (@Param(Constants.WRAPPER) Wrapper<T> queryWrapper) ;
测试⽤例:
@Test public void testSelectCount () { QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.gt("age" ,18 ); Integer count = userMapper.selectCount(queryWrapper); System.out.println(count); }
测试结果:
5. selectList ⽅法定义:
List<T> selectList (@Param(Constants.WRAPPER) Wrapper<T> queryWrapper) ;
测试用例:
@Test public void testSelectList () { QueryWrapper<User> wrapper = new QueryWrapper<User>(); wrapper.gt("age" , 18 ); List<User> users = this .userMapper.selectList(wrapper); for (User user : users) { System.out.println("user = " + user); } }
测试结果:
6. selectPage ⽅法定义:
IPage<T> selectPage (IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper) ;
配置分⻚插件:
@Configuration public class MybatisPlusConfig { @Bean public PaginationInterceptor paginationInterceptor () { return new PaginationInterceptor(); } }
测试⽤例:
@Test public void testSelectPage () { QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.gt("age" , 18 ); Page<User> page = new Page<>(1 , 2 ); IPage<User> userIPage = userMapper.selectPage(page, queryWrapper); System.out.println("总条数" + userIPage.getTotal()); System.out.println("总页数" + userIPage.getPages()); System.out.println("分页数据" + userIPage.getRecords()); }
测试结果:
3.5 SQL注⼊的原理 前⾯我们已经知道,MP在启动后会将BaseMapper中的⼀系列的⽅法注册到meppedStatements中,那么究竟是如何注⼊的呢?流程⼜是怎么样的?下⾯我们将⼀起来分析下。
在MP中,ISqlInjector负责SQL的注⼊⼯作,它是⼀个接⼝,AbstractSqlInjector是它的实现类。
在AbstractSqlInjector中,主要是由inspectInject()⽅法进⾏注⼊的,如下:
public abstract class AbstractSqlInjector implements ISqlInjector { @Override public void inspectInject (MapperBuilderAssistant builderAssistant, Class<?> mapperClass) { Class<?> modelClass = extractModelClass(mapperClass); if (modelClass != null ) { String className = mapperClass.toString(); Set<String> mapperRegistryCache = GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration()); if (!mapperRegistryCache.contains(className)) { List<AbstractMethod> 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的配置,详情:使用配置 | MyBatis-Plus (baomidou.com)
下⾯我们对常⽤的配置做讲解。
4.1 基本配置 1. configLocation MyBatis 配置文件位置,如果您有单独的 MyBatis 配置,请将其路径配置到 configLocation
中.MyBatis Configuration 的具体内容请参考MyBatis 官方文档(opens new window)
Spring Boot:
mybatis-plus.config-location = classpath:mybatis-config.xml
Spring MVC:
<bean id ="sqlSessionFactory" class ="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean" > <property name ="configLocation" value ="classpath:mybatis-config.xml" /> </bean >
2. mapperLocations 类型:String[]
默认值:["classpath*:/mapper/**/*.xml"]
MyBatis Mapper 所对应的 XML 文件位置,如果您在 Mapper 中有自定义方法(XML 中有自定义实现),需要进行该配置,告诉 Mapper 所对应的 XML 文件位置
注意 Maven 多模块项目的扫描路径需以 classpath*:
开头 (即加载多个 jar 包下的 XML 文件)
Spring Boot:
mybatis-plus.mapper-locations = classpath*:mybatis/*.xml
Spring MVC:
<bean id ="sqlSessionFactory" class ="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean" > <property name ="mapperLocations" value ="classpath*:mybatis/*.xml" /> </bean >
3. typeAliasesPackage MyBaits 别名包扫描路径,通过该属性可以给包中的类注册别名,注册后在 Mapper 对应的 XML 文件中可以直接使用类名,而不用使用全限定的类名(即 XML 中调用的时候不用包含包名)
Spring Boot:
mybatis-plus.type-aliases-package = com.lemon.pojo
Spring MVC:
<bean id ="sqlSessionFactory" class ="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean" > <property name ="typeAliasesPackage" value ="com.baomidou.mybatisplus.samples.quickstart.entity" /></bean >
4.2 进阶配置 本部分(Configuration)的配置大都为 MyBatis 原生支持的配置,这意味着您可以通过 MyBatis XML 配置文件的形式进行配置。
1. mapUnderscoreToCamelCase 是否开启自动驼峰命名规则(camel case)
映射,即从经典数据库列名 A_COLUMN
(下划线命名) 到经典 Java 属性名 aColumn
(驼峰命名) 的类似映射。
注意 此属性在 MyBatis 中原默认值为 false,在 MyBatis-Plus 中,此属性也将用于生成最终的 SQL 的 select body 如果您的数据库命名符合规则无需使用 @TableField
注解指定数据库字段名
示例(SpringBoot)
mybatis-plus.configuration.map-underscore-to-camel-case =false
2. cacheEnabled 开启 Mybatis 二级缓存,默认为 true。
SpringBoot:
mybatis-plus.configuration.cache-enabled =false
4.3 DB 策略配置 1. idType 类型:com.baomidou.mybatisplus.annotation.IdType
默认值:ASSIGN_ID
全局默认主键类型
SpringBoot:
// 设置全局主键的生成策略-自增 mybatis-plus.global-config.db-config.id-type =auto
SpringMVC:
<bean id ="sqlSessionFactory" class ="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean" > <property name ="dataSource" ref ="dataSource" /> <property name ="globalConfig" > <bean class ="com.baomidou.mybatisplus.core.config.GlobalConfig" > <property name ="dbConfig" > <bean class ="com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig" > <property name ="idType" value ="AUTO" /> </bean > </property > </bean > </property > </bean >
2. tablePrefix 表名前缀
SpringBoot:
mybatis-plus.global-config.db-config.table-prefix =tb_
SpringMVC:
<bean id ="sqlSessionFactory" class ="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean" > <property name ="dataSource" ref ="dataSource" /> <property name ="globalConfig" > <bean class ="com.baomidou.mybatisplus.core.config.GlobalConfig" > <property name ="dbConfig" > <bean class ="com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig" > <property name ="idType" value ="AUTO" /> <property name ="tablePrefix" value ="tb_" /> </bean > </property > </bean > </property > </bean >
5. 条件构造器 在MyBatis-Plus中,Wrapper接⼝的实现类关系如下:
可以看到,AbstractWrapper
和AbstractChainWrapper
是重点实现,接下来我们重点学习AbstractWrapper
以及其⼦类。
说明: QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper) 的⽗类⽤于⽣成 sql 的 where 条件, entity 属性也⽤于⽣成 sql 的 where 条件。
注意: entity ⽣成的 where 条件与 使⽤各个 api ⽣成的 where 条件没有任何关联⾏为
5.1 allEq allEq(Map<R, V> params) allEq(Map<R, V> params, boolean null2IsNull) allEq(boolean condition, Map<R, V> params, boolean null2IsNull)
全部eq (或个别isNull ) 个别参数说明: params
: key
为数据库字段名,value
为字段值null2IsNull
: 为true
则在map
的value
为null
时调用 isNull 方法,为false
时则忽略value
为null
的 例1: allEq({id:1,name:"老王",age:null})
—>id = 1 and name = '老王' and age is null
例2: allEq({id:1,name:"老王",age:null}, false)
—>id = 1 and name = '老王'
allEq(BiPredicate<R, V> filter, Map<R, V> params) allEq(BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull) allEq(boolean condition, BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull)
个别参数说明:
filter
: 过滤函数,是否允许字段传入比对条件中params
与 null2IsNull
: 同上
例1: allEq((k,v) -> k.indexOf("a") >= 0, {id:1,name:"老王",age:null})
—>name = '老王' and age is null
例2: allEq((k,v) -> k.indexOf("a") >= 0, {id:1,name:"老王",age:null}, false)
—>name = '老王'
测试用例:
@Test public void testAllEq () { QueryWrapper<User> queryWrapper = new QueryWrapper<>(); Map<String, Object> map = new HashMap<>(); map.put("name" , "jack" ); map.put("age" , null ); queryWrapper.allEq((k,v) -> k.equals("name" ),map); List<User> users = userMapper.selectList(queryWrapper); for (User user : users) { System.out.println(user); } }
5.2 基本比较操作 1. eq eq(R column, Object val) eq(boolean condition, R column, Object val)
等于 = 例: eq("name", "老王")
—>name = '老王'
2. ne ne(R column, Object val) ne(boolean condition, R column, Object val)
不等于 <> 例: ne("name", "老王")
—>name <> '老王'
3. gt gt(R column, Object val) gt(boolean condition, R column, Object val)
大于 > 例: gt("age", 18)
—>age > 18
4. ge ge(R column, Object val) ge(boolean condition, R column, Object val)
大于等于 >= 例: ge("age", 18)
—>age >= 18
5. lt lt(R column, Object val) lt(boolean condition, R column, Object val)
小于 < 例: lt("age", 18)
—>age < 18
6. le le(R column, Object val) le(boolean condition, R column, Object val)
小于等于 <= 例: le("age", 18)
—>age <= 18
7. between between(R column, Object val1, Object val2) between(boolean condition, R column, Object val1, Object val2)
BETWEEN 值1 AND 值2 例: between("age", 18, 30)
—>age between 18 and 30
8. notBetween notBetween(R column, Object val1, Object val2) notBetween(boolean condition, R column, Object val1, Object val2)
NOT BETWEEN 值1 AND 值2 例: notBetween("age", 18, 30)
—>age not between 18 and 30
9. in in(R column, Collection<?> value) in(boolean condition, R column, Collection<?> value)
字段 IN (value.get(0), value.get(1), …) 例: in("age",{1,2,3})
—>age in (1,2,3)
in(R column, Object... values) in(boolean condition, R column, Object... values)
字段 IN (v0, v1, …) 例: in("age", 1, 2, 3)
—>age in (1,2,3)
10. notIn notIn(R column, Collection<?> value) notIn(boolean condition, R column, Collection<?> value)
字段 NOT IN (value.get(0), value.get(1), …) 例: notIn("age",{1,2,3})
—>age not in (1,2,3)
notIn(R column, Object... values) notIn(boolean condition, R column, Object... values)
字段 NOT IN (v0, v1, …) 例: notIn("age", 1, 2, 3)
—>age not in (1,2,3)
5.3 模糊查询 1. like like(R column, Object val) like(boolean condition, R column, Object val)
LIKE ‘%值%’ 例: like("name", "王")
—>name like '%王%'
2. notLike notLike(R column, Object val) notLike(boolean condition, R column, Object val)
NOT LIKE ‘%值%’ 例: notLike("name", "王")
—>name not like '%王%'
3. likeLeft likeLeft(R column, Object val) likeLeft(boolean condition, R column, Object val)
LIKE ‘%值’ 例: likeLeft("name", "王")
—>name like '%王'
4. likeRight likeRight(R column, Object val) likeRight(boolean condition, R column, Object val)
LIKE ‘值%’ 例: likeRight("name", "王")
—>name like '王%'
测试用例:
@Test public void testWrapperLike () { QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.like("name" ,"m" ); List<User> users = userMapper.selectList(queryWrapper); for (User user : users) { System.out.println(user); } }
5.4 排序 1. orderBy orderBy(boolean condition, boolean isAsc, R... columns)
排序:ORDER BY 字段, … 例: orderBy(true, true, "id", "name")
—>order by id ASC,name ASC
2. orderByAsc orderByAsc(R... columns) orderByAsc(boolean condition, R... columns)
排序:ORDER BY 字段, … ASC 例: orderByAsc("id", "name")
—>order by id ASC,name ASC
3. orderByDesc orderByDesc(R... columns) orderByDesc(boolean condition, R... columns)
排序:ORDER BY 字段, … DESC 例: orderByDesc("id", "name")
—>order by id DESC,name DESC
测试用例:
@Test public void testWrapper2 () { QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.orderByDesc("age" ); List<User> users = userMapper.selectList(queryWrapper); for (User user : users) { System.out.println(user); } }
5.5 逻辑查询 1. or or() or(boolean condition)
拼接 OR注意事项: 主动调用or
表示紧接着下一个方法 不是用and
连接!(不调用or
则默认为使用and
连接)
例: eq("id",1).or().eq("name","老王")
—>id = 1 or name = '老王'
or(Consumer<Param> consumer) or(boolean condition, Consumer<Param> consumer)
OR 嵌套 例: or(i -> i.eq("name", "李白").ne("status", "活着"))
—>or (name = '李白' and status <> '活着')
2. and and(Consumer<Param> consumer) and(boolean condition, Consumer<Param> consumer)
AND 嵌套 例: and(i -> i.eq("name", "李白").ne("status", "活着"))
—>and (name = '李白' and status <> '活着')
测试用例:
@Test public void testWrapper2 () { QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("name" ,"jack" ).or().eq("age" ,28 ); List<User> users = userMapper.selectList(queryWrapper); for (User user : users) { System.out.println(user); } }
5.7 select select(String... sqlSelect) select(Predicate<TableFieldInfo> predicate) select(Class<T> entityClass, Predicate<TableFieldInfo> predicate)
设置查询字段说明: 以上方法分为两类. 第二类方法为:过滤查询字段(主键除外),入参不包含 class 的调用前需要wrapper
内的entity
属性有值! 这两类方法重复调用以最后一次为准
例: select("id", "name", "age")
例: select(i -> i.getProperty().startsWith("test"))
6. ActiveRecord 模式 ActiveRecord(简称AR)⼀直⼴受动态语⾔( PHP 、 Ruby 等)的喜爱,⽽ Java 作为准静态语⾔,对于 ActiveRecord 往往只能感叹其优雅,所以我们也在 AR 道路上进⾏了⼀定的探索,希望⼤家能够喜欢。
什么是ActiveRecord? ActiveRecord也属于ORM(对象关系映射)层,由Rails最早提出,遵循标准的ORM模型:表映射到记录,记录映射到对象,字段映射到对象属性。配合遵循的命名和配置惯例,能够很⼤程度的快速实现模型的操作,⽽且简洁易懂。
ActiveRecord的主要思想是:
每⼀个数据库表对应创建⼀个类,类的每⼀个对象实例对应于数据库中表的⼀⾏记录;通常表的每个字段在类中都有相应的Field; ActiveRecord同时负责把⾃⼰持久化,在ActiveRecord中封装了对数据库的访问,即CURD; ActiveRecord是⼀种领域模型(Domain Model),封装了部分业务逻辑; 6.1 操作步骤 说明:
实体类只需继承 Model 类即可进行强大的 CRUD 操作 需要项目中已注入对应实体的BaseMapper 1. 继承 Model class User extends Model <User > { }
2. 调用CRUD
方法 User user = new User(); user.insert(); user.selectAll(); user.updateById(); user.deleteById();
@Data @NoArgsConstructor @AllArgsConstructor @TableName("user") public class User extends Model <User > { @TableId(type = IdType.AUTO) private Long id; @TableField(select = true) private String name; private Integer age; @TableField(value = "email") private String mail; @TableField(exist = false) private String address; @Version @TableField(fill = FieldFill.INSERT) private Integer version; }
6.2 根据主键查询 @Test public void testARSelectById () { User user = new User(); user.setId(1L ); User user1 = user.selectById(); System.out.println(user1); }
6.3 新增数据 @Test public void testARInsert () { User user = new User(); user.setName("墨竹" ); user.setAge(18 ); user.setMail("mozhu@lemon.com" ); boolean insert = user.insert(); System.out.println(insert); }
测试结果:
6.4 更新操作 @Test public void testARUpate () { User user = new User(); User user1 = user.selectById(1L ); user.setId(6L ); user.setName("青梅" ); user.setVersion(user1.getVersion()); boolean insert = user.updateById(); System.out.println(insert); }
结果:
6.5 删除操作 @Test public void testARDelete () { User user = new User(); boolean b = user.deleteById(6L ); System.out.println(b); }
6.6 根据条件查询 @Test public void testARFindByWrapper () { QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.ge("age" ,"20" ); User user = new User(); List<User> users = user.selectList(queryWrapper); 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语法构建的处理 拦截器示例
@Intercepts({//注意看这个大花括号,也就这说这里可以定义多个@Signature对多个地方拦截,都用这个拦截器 @Signature(type = StatementHandler.class, //这是指拦截哪个接口 method = "prepare",//这个接口内的哪个方法名,不要拼错了 args = {Connection.class, Integer.class}),// 这是拦截的方法的入参,按顺序写到这,不要多也不要少,如果方法重载,可是要通过方法名和入参来确定唯一的 }) public class MyPlugin implements Interceptor { private final Logger logger = LoggerFactory.getLogger(this .getClass()); @Override public Object intercept (Invocation invocation) throws Throwable { System.out.println("对方法进行了增强...." ); return invocation.proceed(); } @Override public Object plugin (Object target) { System.out.println("将要包装的目标对象:" + target); return Plugin.wrap(target, this ); } @Override public void setProperties (Properties properties) { System.out.println("插件配置的初始化参数:" + properties); } }
注⼊到Spring容器:
@Bean public MyInterceptor myInterceptor () { return new MyInterceptor(); }
或者通过xml配置,mybatis-config.xml:
<plugins > <plugin interceptor ="com.lemon.plugin.MyPlugin" > <property name ="name" value ="syj" /> </plugin > </plugins >
7.2 性能分析插件 在MP中提供了对SQL执⾏的分析的插件,可⽤作阻断全表更新、删除的操作,注意:该插件仅适⽤于开发环境,不适⽤于⽣产环境。
SpringBoot配置Bean:
@Bean public PerformanceInterceptor performanceInterceptor () { PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor(); performanceInterceptor.setMaxTime(100 ); performanceInterceptor.setFormat(true ); return performanceInterceptor; }
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.extension.plugins.PerformanceInterceptor " > <property name ="maxTime" value ="100" /> <property name ="format" value ="true" /> </plugin > </plugins > </configuration >
执⾏结果:
可以看到,执⾏时间为35ms。如果将maxTime设置为1,那么,该操作会抛出异常。
Caused by: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: The SQL execution time is too large, please optimize ! at com.baomidou.mybatisplus.core.toolkit.ExceptionUtils.mpe(ExceptionUtils.java:4 9) at com.baomidou.mybatisplus.core.toolkit.Assert.isTrue(Assert.java:38) ................
7.3 乐观锁插件 1. 主要适⽤场景 意图:
当要更新⼀条记录的时候,希望这条记录没有被别⼈更新。
乐观锁实现⽅式:
取出记录时,获取当前version 更新时,带上这个version 执⾏更新时, set version = newVersion where version = oldVersion
如果version不对,就更新失败 2. 插件配置 spring xml:
<bean class ="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor" />
spring boot:
@Bean public OptimisticLockerInterceptor optimisticLockerInterceptor () { return new OptimisticLockerInterceptor(); }
3. 注解实体字段 需要为实体字段添加@Version注解。
第⼀步,为表添加version字段,并且设置初始值为1;
ALTER TABLE `tb_user`ADD COLUMN `version` int (10 ) NULL AFTER `email`;UPDATE `tb_user` SET `version`= '1' ;
第⼆步,为User实体对象添加version字段,并且添加@Version注解:
@Version private Integer version;
4. 测试 测试用例:
@Test public void testUpdateById () { User user = new User(); user.setId(6L ); user.setAge(24 ); user.setVersion(1 ); int i = userMapper.updateById(user); System.out.println(i); }
测试结果:
可以看到,更新的条件中有version条件,并且更新的version为2。
如果再次执⾏,更新则不成功。这样就避免了多⼈同时更新时导致数据的不⼀致。
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 public interface MyBaseMapper <T > extends BaseMapper <T > { public List<T> findAll () ; }
其他的Mapper都可以继承该Mapper,这样实现了统⼀的扩展。
如:
public interface UserMapper extends MyBaseMapper <User > { User findById (Long id) ; }
8.2 编写MySqlInjector 如果直接继承AbstractSqlInjector的话,原有的BaseMapper中的⽅法将失效,所以我们选择继承DefaultSqlInjector进⾏扩展。
public class MySqlInjector extends DefaultSqlInjector { @Override public List<AbstractMethod> getMethodList () { List<AbstractMethod> methodList = super .getMethodList(); methodList.add(new FindAll()); return methodList; } }
8.3 编写FindAll public class FindAll extends AbstractMethod { @Override public MappedStatement injectMappedStatement (Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) { String sql = "select * from " + tableInfo.getTableName(); SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass); return this .addSelectMappedStatement(mapperClass, "findAll" , sqlSource, modelClass, tableInfo); } }
8.4 注册到Spring容器 @Bean public MySqlInjector mySqlInjector () { return new MySqlInjector(); }
8.5 测试 测试用例:
@Test public void testFindAll () { List<User> all = userMapper.findAll(); for (User user : all) { System.out.println(user); } }
测试结果:
⾄此,我们实现了全局扩展SQL注⼊器。
9. ⾃动填充功能 有些时候我们可能会有这样的需求,插⼊或者更新数据时,希望有些字段可以⾃动填充数据,⽐如密码、version等。在MP中提供了这样的功能,可以实现⾃动填充。
9.1 添加@TableField注解 @TableField(fill = FieldFill.INSERT) private String version;
为email添加⾃动填充功能,在新增数据时有效。
FieldFill提供了多种模式选择:
@Component public class MyMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill (MetaObject metaObject) { Object version = getFieldValByName("version" , metaObject); if (version == null ) { setFieldValByName("version" , 1 , metaObject); } } @Override public void updateFill (MetaObject metaObject) { } }
9.3 测试 @Test public void testInsert () { User user = new User(); user.setAge(18 ); user.setName("kobe" ); user.setMail("123@qq.com" ); int result = userMapper.insert(user); System.out.println(result); System.out.println("id值为" + user.getId()); }
测试结果:
注意事项:
填充原理是直接给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就提供了这样的功能,⽅便我们使⽤,接下来我们⼀起学习下。
说明:
只对自动注入的 sql 起效:
插入: 不作限制 查找: 追加 where 条件过滤掉已删除数据,且使用 wrapper.entity 生成的 where 条件会忽略该字段 更新: 追加 where 条件防止更新到已删除数据,且使用 wrapper.entity 生成的 where 条件会忽略该字段 删除: 转变为 更新 例如:
删除: update user set deleted=1 where id = 1 and deleted=0
查找: select id,name,deleted from user where deleted=0
字段类型支持说明:
支持所有数据类型(推荐使用 Integer
,Boolean
,LocalDateTime
) 如果数据库字段使用datetime
,逻辑未删除值和已删除值支持配置为字符串null
,另一个值支持配置为函数来获取值如now()
附录:
逻辑删除是为了方便数据恢复和保护数据本身价值等等的一种方案,但实际就是删除。 如果你需要频繁查出来看就不应使用逻辑删除,而是以一个状态去表示。 10.1 修改表结构 为tb_user表增加deleted字段,⽤于表示数据是否被删除,1代表删除,0代表未删除。
ALTER TABLE `tb_user`ADD COLUMN `deleted` int (1 ) NULL DEFAULT 0 COMMENT '1代表删除,0代表未删除' AFTER `version`;
同时,也修改User实体,增加deleted属性并且添加@TableLogic注解:
@TableLogic private Integer deleted;
10.2 配置 application.properties:
mybatis-plus.global-config.db-config.logic-delete-value =1 mybatis-plus.global-config.db-config.logic-not-delete-value =0
10.3 测试 @Test public void testDeleteById () { int i = userMapper.deleteById(6L ); System.out.println(i); }
测试结果:
测试查询:
@Test public void testSelectById () { User user = userMapper.selectById(6 ); System.out.println(user); }
执⾏的SQL:
可⻅,已经实现了逻辑删除。
11. 代码生成器 AutoGenerator 是 MyBatis-Plus 的代码⽣成器,通过 AutoGenerator 可以快速⽣成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极⼤的提升了开发效率。
11.1 创建⼯程 pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion > 4.0.0</modelVersion > <parent > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-parent</artifactId > <version > 2.6.3</version > <relativePath /> </parent > <groupId > com.lemon</groupId > <artifactId > mybatis-plus-generator</artifactId > <version > 0.0.1-SNAPSHOT</version > <name > mybatis-plus-generator</name > <description > mybatis-plus-generator</description > <properties > <java.version > 1.8</java.version > </properties > <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-test</artifactId > <scope > test</scope > </dependency > <dependency > <groupId > com.baomidou</groupId > <artifactId > mybatis-plus-boot-starter</artifactId > <version > 3.1.1</version > </dependency > <dependency > <groupId > com.baomidou</groupId > <artifactId > mybatis-plus-generator</artifactId > <version > 3.1.1</version > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-freemarker</artifactId > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.47</version > </dependency > <dependency > <groupId > org.slf4j</groupId > <artifactId > slf4j-log4j12</artifactId > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > </dependency > </dependencies > <build > <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > </plugin > </plugins > </build > </project >
11.2 生成代码 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(); final String projectPath = System.getProperty("user.dir" ); gc.setOutputDir(projectPath + "/src/main/java" ); gc.setAuthor("zimu" ); 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.setDriverName("com.mysql.jdbc.Driver" ); dsc.setUsername("root" ); dsc.setPassword("123456" ); mpg.setDataSource(dsc); final PackageConfig pc = new PackageConfig(); pc.setModuleName(scanner("模块名" )); pc.setParent("com.lemon.mp.generator" ); mpg.setPackageInfo(pc); InjectionConfig cfg = new InjectionConfig() { @Override public void initMap () { } }; List<FileOutConfig> focList = new ArrayList<FileOutConfig>(); focList.add(new FileOutConfig("/templates/mapper.xml.ftl" ) { @Override public String outputFile (TableInfo tableInfo) { return projectPath + "/lemon-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.setEntityLombokModel(true ); strategy.setInclude(scanner("表名" )); strategy.setSuperEntityColumns("id" ); strategy.setControllerMappingHyphenStyle(true ); strategy.setTablePrefix(pc.getModuleName() + "_" ); mpg.setStrategy(strategy); mpg.setTemplateEngine(new FreemarkerTemplateEngine()); mpg.execute(); } }
11.3 测试
代码已经生成:
12. MybatisX 快速开发插件 MybatisX 是⼀款基于 IDEA 的快速开发插件,为效率⽽⽣。
安装⽅法:打开 IDEA,进⼊ File -> Settings -> Plugins -> Browse Repositories,输⼊ mybatisx 搜索并安装。
功能:
Java 与 XML 调回跳转 Mapper ⽅法⾃动⽣成 XML