1、相关概念
1.1、特性
- ⽆侵⼊:只做增强不做改变,引⼊它不会对现有⼯程产⽣影响,如丝般顺滑
- 损耗⼩:启动即会⾃动注⼊基本 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.2、架构
2、快速入门
2.1、安装
MyBatis-Plus 3.0 版本基于 JDK8,提供了 lambda 形式的调⽤,所以安装集成 MP3.0 要求如下:
- JDK 8+
- Maven or Gradle
SpringBoot项目:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.0</version>
</dependency>
Spring MVC项目:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>3.4.0</version>
</dependency>
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、创建工程
<dependencies>
<dependency>
<groupid>com.baomidou</groupid>
<artifactid>mybatis-plus</artifactid>
<version>3.1.1</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
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
2.4、Mybatis + MP
即纯Mybatis与Mybatis-Plus整合
2.4.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>blnp-mybatis-plus</artifactid>
<groupid>com.blnp.net.mp</groupid>
<version>1.0-SNAPSHOT</version>
</parent>
<modelversion>4.0.0</modelversion>
<artifactid>blnp-mybatis-plus-simple</artifactid>
</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.4.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: 运⾏环境-->
<environments default="development">
<environment id="development">
<!--当前的事务事务管理器是JDBC-->
<transactionManager type="JDBC"></transactionManager>
<!--数据源信息 POOLED:使⽤mybatis的连接池-->
<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>
<mapper resource="mapper/UserMapper.xml"></mapper>
</mappers>
</configuration>
第⼆步,编写User实体对象:(这⾥使⽤lombok进⾏了进化bean操作)
@Data // getter setter @toString
@NoArgsConstructor
@AllArgsConstructor
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.blnp.net.mapper.UserMapper">
<!-- 查询所有 -->
<select id="findAll" resultType="com.blnp.net.pojo.User">
select * from user
</select>
</mapper>
第五步,编写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 < User > all = mapper.findAll();
for(User user: all) {
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("指定数据库表名")
2.4.3、Mybatis + MP 实现查询User
第⼀步,将UserMapper继承BaseMapper,将拥有了BaseMapper中的所有⽅法:
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.blnp.net.pojo.User;
public interface UserMapper extends BaseMapper<User> {
}
第⼆步,使⽤MP中的MybatisSqlSessionFactoryBuilder进程构建:
@Test
public 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 < User > all = mapper.selectList(null);
for(User user: all) {
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、Mybatis + Spring + MP
引⼊了Spring框架,数据源、构建等⼯作就交给了Spring管理。
2.5.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>blnp-mybatis-plus</artifactId>
<groupId>com.blnp.mp</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>blnp-mybatis-plus-spring</artifactId>
<properties>
<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.5.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">
<!--引⼊properties-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--dataSource-->
<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>
<!--这⾥使⽤MP提供的sqlSessionFactory,完成spring与mp的整合-->
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--扫描mapper接⼝,使⽤的依然是mybatis原⽣的扫描器-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.blnp.mapper"/>
</bean>
</beans>
第三步,编写User对象以及UserMapper接⼝:
@Data
@NoArgsConstructor
@AllArgsConstructor
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 test2() throws IOException {
List < User > users = this.userMapper.selectList(null);
for(User user: users) {
System.out.println(user);
}
}
2.6、Mybatis + SpringBoot + MP
2.6.1、导入依赖
<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>
</dependencies>
<build>
<plugins>
<plugin>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-maven-plugin</artifactid>
</plugin>
</plugins>
</build>
2.6.2、编写配置文件
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
编写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=tr
ue&useSSL=false
spring.datasource.username=root
spring.datasource.password=root
2.6.3、创建对象
编写pojo
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
}
编写mapper
public interface UserMapper extends BaseMapper < User > {}
编写启动类
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.blnp.mp.mapper") //设置mapper接⼝的扫描包
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
2.6.4、编写测试用例
import com.blnp.mp.mapper.UserMapper;
import com.blnp.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)
@SpringBootTest
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
public 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、通用CURD
3.1、插入操作
测试方法:
package com.blnp.net.test;
import com.blnp.net.dao.IUserMapper;
import com.blnp.net.entity.UserEntity;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.List;
/**
* <p></p>
*
* @author lyb 2045165565@qq.com
* @version 1.0
* @since 2024/8/26 20:22
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class TestSpringMP {
@Autowired
private IUserMapper userMapper;
@Test
public void test(){
List<UserEntity> users = userMapper.selectList(null);
for (UserEntity user : users) {
System.out.println(user);
}
}
@Test
public void testInsert() {
UserEntity user = new UserEntity();
user.setAge(18);
user.setEmail("test@blnp.cn");
user.setName("子慕");
//返回的result是受影响的⾏数,并不是⾃增后的id
int result = userMapper.insert(user);
System.out.println(result);
System.out.println(user.getId());
}
}
执行结果如下所示:
可以看到,数据已经写⼊到了数据库,但是,id的值不正确,我们期望的是数据库⾃增⻓,实际是MP⽣成了id的值写⼊到了数据库。因此我们需要对ID配置生成策略!mybatis-plus支持的ID策略有以下几种:
package com.baomidou.mybatisplus.annotation;
import lombok.Getter;
/**
* 生成ID类型枚举类
*
* @author hubin
* @since 2015-11-10
*/
@Getter
public enum IdType {
/**
* 数据库ID自增
*/
AUTO(0),
/**
* 该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
*/
NONE(1),
/**
* 用户输入ID
* <p>该类型可以通过自己注册自动填充插件进行填充</p>
*/
INPUT(2),
/* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */
/**
* 分配ID (主键类型为number或string),
* 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(雪花算法)
*
* @since 3.3.0
*/
ASSIGN_ID(3),
/**
* 分配UUID (主键类型为 string)
* 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(UUID.replace("-",""))
*/
ASSIGN_UUID(4),
/**
* @deprecated 3.3.0 please use {@link #ASSIGN_ID}
*/
@Deprecated
ID_WORKER(3),
/**
* @deprecated 3.3.0 please use {@link #ASSIGN_ID}
*/
@Deprecated
ID_WORKER_STR(3),
/**
* @deprecated 3.3.0 please use {@link #ASSIGN_UUID}
*/
@Deprecated
UUID(4);
private final int key;
IdType(int key) {
this.key = key;
}
}
实体类修改
@TableId(type = IdType.AUTO)
private Long id;
关于@TableField 注解的用途:
在MP中通过@TableField注解可以指定字段的⼀些属性,常常解决的问题有2个:
- 对象中的属性名和字段名不⼀致的问题(⾮驼峰)
- 对象中的属性字段在表中不存在的问题
package com.blnp.net.entity;
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 lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* <p></p>
*
* @author lyb 2045165565@qq.com
* @version 1.0
* @since 2024/8/26 19:38
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("user")
public class UserEntity {
@TableId(type = IdType.AUTO)
private Long id;
/**
* 表示该大字段不加入查询
**/
@TableField(select = false)
private String name;
/**
* 表示该字段在数据库表中不存在
**/
@TableField(exist = false)
private Integer age;
/**
* 解决字段名不一致问题
**/
@TableField(value = "mail")
private String email;
}
注意事项:
这里可能有的人配置ID策略后,测试新增时程序抛出以下错误:
org.springframework.dao.DataIntegrityViolationException:
### Error updating database. Cause: java.sql.SQLException: #HY000
### The error may exist in com/blnp/net/dao/IUserMapper.java (best guess)
### The error may involve com.blnp.net.dao.IUserMapper.insert-Inline
### The error occurred while setting parameters
### SQL: INSERT INTO user ( name, age, email ) VALUES ( ?, ?, ? )
### Cause: java.sql.SQLException: #HY000
; #HY000; nested exception is java.sql.SQLException: #HY000
问题原因其实是insert语句没有设置ID值,导致ID字段为空。此时可以确认下表结构ID字段是否有设置自动增长,没有则勾选上即可。
3.2、更新操作
在MP中,更新操作有2种,⼀种是根据id更新,另⼀种是根据条件更新。
根据ID更新:
@Test
public void testUpdateById() {
UserEntity user = new UserEntity();
user.setName("西瓜");
user.setId(6L); //主键
user.setAge(21); //更新的字段
//根据id更新,更新不为null的字段
this.userMapper.updateById(user);
}
根据条件更新:
@Test
public void testUpdate() {
UserEntity user = new UserEntity();
user.setAge(22); //更新的字段
//更新的条件
QueryWrapper<UserEntity> wrapper = new QueryWrapper<>();
wrapper.eq("id", 6);
//执⾏更新操作
int result = this.userMapper.update(user, wrapper);
System.out.println("result = " + result);
}
或者,通过UpdateWrapper进⾏更新:
@Test
public void testUpdateWrapper() {
//更新的条件以及字段
UpdateWrapper<UserEntity> wrapper = new UpdateWrapper<>();
wrapper.eq("id", 6).set("age", 23);
//执⾏更新操作
int result = this.userMapper.update(null, wrapper);
System.out.println("result = " + result);
}
3.3、删除操作
@Test
public void testDeleteById() {
//执行删除操作
int result = this.userMapper.deleteById(6L);
System.out.println("result = " + result);
}
@Test
public void testDeleteByMap() {
Map<String, Object> columnMap = new HashMap<>();
columnMap.put("age",21);
columnMap.put("name","⼦慕");
//将columnMap中的元素设置为删除的条件,多个之间为and关系
int result = this.userMapper.deleteByMap(columnMap);
System.out.println("result = " + result);
}
@Test
public void testDeleteByMapWrapper() {
UserEntity user = new UserEntity();
user.setAge(20);
user.setName("慕");
//将实体对象进⾏包装,包装为操作条件
QueryWrapper<UserEntity> wrapper = new QueryWrapper<>(user);
int result = this.userMapper.delete(wrapper);
System.out.println("result = " + result);
}
@Test
public void testDeleteByBatch() {
//根据id集合批量删除
int result = this.userMapper.deleteBatchIds(Arrays.asList(1L,10L,20L));
System.out.println("result = " + result);
}
3.4、查询操作
@Test
public void testSelectById() {
//根据id查询数据
UserEntity user = this.userMapper.selectById(2L);
System.out.println("result = " + user);
}
@Test
public void testSelectBatchIds() {
//根据id集合批量查询
List<UserEntity> users = this.userMapper.selectBatchIds(Arrays.asList(2L, 3L, 10L));
for (UserEntity user : users) {
System.out.println(user);
}
}
@Test
public void testSelectOne() {
QueryWrapper<UserEntity> wrapper = new QueryWrapper<UserEntity>();
wrapper.eq("name", "jack");
//根据条件查询⼀条数据,如果结果超过⼀条会报错
UserEntity user = this.userMapper.selectOne(wrapper);
System.out.println(user);
}
@Test
public void testSelectCount() {
QueryWrapper<UserEntity> wrapper = new QueryWrapper<UserEntity>();
wrapper.gt("age", 23); //年龄⼤于23岁
//根据条件查询数据条数
Integer count = this.userMapper.selectCount(wrapper);
System.out.println("count = " + count);
}
@Test
public void testSelectList() {
QueryWrapper<UserEntity> wrapper = new QueryWrapper<UserEntity>();
wrapper.gt("age", 23); //年龄⼤于23岁
//根据条件查询数据
List<UserEntity> users = this.userMapper.selectList(wrapper);
for (UserEntity user : users) {
System.out.println("user = " + user);
}
}
@Test
public void testSelectPage() {
QueryWrapper<UserEntity> wrapper = new QueryWrapper<UserEntity>();
wrapper.gt("age", 20); //年龄⼤于20岁
Page<UserEntity> page = new Page<>(1,1);
//根据条件查询数据
IPage<UserEntity> iPage = this.userMapper.selectPage(page, wrapper);
System.out.println("数据总条数:" + iPage.getTotal());
System.out.println("总⻚数:" + iPage.getPages());
List<UserEntity> users = iPage.getRecords();
for (UserEntity user : users) {
System.out.println("user = " + user);
}
}
3.5、SQL注入的原理
在MP中,ISqlInjector负责SQL的注⼊⼯作,它是⼀个接⼝,AbstractSqlInjector是它的实现类,实现关系如下:
在AbstractSqlInjector中,主要是由inspectInject()⽅法进⾏注⼊的,如下:在实现⽅法中, methodList.forEach(m -> m.inject(builderAssistant, mapperClass,modelClass, tableInfo)); 是关键,循环遍历⽅法,进⾏注⼊。最终调⽤抽象⽅法injectMappedStatement进⾏真正的注⼊:
@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(mapperClass);
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);
}
}
}
以SelectByID为例,查看源代码如下:
package com.baomidou.mybatisplus.core.injector.methods;
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;
import org.apache.ibatis.scripting.defaults.RawSqlSource;
/**
* 根据ID 查询一条数据
*
* @author hubin
* @since 2018-04-06
*/
public class SelectById extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
SqlMethod sqlMethod = SqlMethod.SELECT_BY_ID;
SqlSource sqlSource = new RawSqlSource(configuration, String.format(sqlMethod.getSql(),
sqlSelectColumns(tableInfo, false),
tableInfo.getTableName(), tableInfo.getKeyColumn(), tableInfo.getKeyProperty(),
tableInfo.getLogicDeleteSql(true, true)), Object.class);
return this.addSelectMappedStatementForTable(mapperClass, getMethod(sqlMethod), sqlSource, tableInfo);
}
}
可以看到,⽣成了SqlSource对象,再将SQL通过addSelectMappedStatement⽅法添加到meppedStatements中。
4、具体配置
4.1、基本配置
4.1.1、configLocation
MyBatis 配置⽂件位置,如果有单独的 MyBatis 配置,请将其路径配置到 configLocation 中。MyBatis Configuration 的具体内容请参考MyBatis 官⽅⽂档
Springboot:
mybatis-plus.config-location = classpath:mybatis-config.xml
SpringMVC:
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
4.1.2、mapperLocations
MyBatis Mapper 所对应的 XML ⽂件位置,如果您在 Mapper 中有⾃定义⽅法(XML 中有⾃定义实现),需要进⾏该配置,告诉 Mapper 所对应的 XML ⽂件位置。
Springboot:
mybatis-plus.mapper-locations = classpath*:mybatis/*.xml
SpringMVC:
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="mapperLocations" value="classpath*:mybatis/*.xml"/>
</bean>
注意事项:Maven 多模块项⽬的扫描路径需以 classpath*: 开头(即加载多个 jar 包下的 XML ⽂件)。
4.1.3、typeAliasesPackage
MyBaits 别名包扫描路径,通过该属性可以给包中的类注册别名,注册后在 Mapper 对应的 XML ⽂件中可以直接使⽤类名,⽽不⽤使⽤全限定的类名(即 XML 中调⽤的时候不⽤包含包名)。
Springboot:
mybatis-plus.type-aliases-package = com.blnp.net.mp.pojo
SpringMVC:
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="typeAliasesPackage" value="com.baomidou.mybatisplus.samples.quickstart.entity"/>
</bean>
4.2、进阶配置
4.2.1、mapUnderscoreToCamelCase
- 类型:Boolean
- 默认值:true
是否开启⾃动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN(下划线命名) 到经典 Java 属性名 aColumn(驼峰命名) 的类似映射。
此属性在 MyBatis 中原默认值为 false,在 MyBatis-Plus 中,此属性也将⽤于⽣成最终的 SQL 的 select body。如果您的数据库命名符合规则⽆需使⽤ @TableField 注解指定数据库字段名。
#关闭⾃动驼峰映射,该参数不能和mybatis-plus.config-location同时存在
mybatis-plus.configuration.map-underscore-to-camel-case=false
4.2.2、cacheEnabled
- 类型:Boolean
- 默认值:true
全局地开启或关闭配置⽂件中的所有映射器已经配置的任何缓存,默认为 true。
mybatis-plus.configuration.cache-enabled=false
4.3、DB策略配置
4.3.1、idType
- 类型:com.baomidou.mybatisplus.annotation.IdType
- 默认值:ID_WORKER
全局默认主键类型,设置后,即可省略实体对象中的@TableId(type = IdType.AUTO)配置。
SpringBoot:
mybatis-plus.global-config.db-config.id-type=auto
SpringMVC:
<!--这⾥使⽤MP提供的sqlSessionFactory,完成了Spring与MP的整合-->
<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>
4.3.2、tablePrefix
- 类型:String
- 默认值:null
表名前缀,全局配置后可省略@TableName()配置。
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、条件构造器
5.1、allEq
相关API
allEq(Map<R, V> params)
allEq(Map<R, V> params, boolean null2IsNull)
allEq(boolean condition, Map<R, V> params, boolean null2IsNull)
- params:key 为数据库字段名, value 为字段值
- null2IsNull : 为true 则在map 的value 为null 时调⽤ isNull ⽅法,为false 时则忽略value 为null 的
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 testWrapper() {
QueryWrapper<UserEntity> wrapper = new QueryWrapper<>();
//设置条件
Map<String,Object> params = new HashMap<>();
params.put("name", "jack");
params.put("age", "20");
// wrapper.allEq(params);
// SELECT * FROM user WHERE password IS NULL AND name = ? AND age = ?
// wrapper.allEq(params,false);
// SELECT * FROM user WHERE name = ? AND age = ?
// wrapper.allEq((k, v) -> (k.equals("name") || k.equals("age")),params);
// SELECT * FROM user WHERE name = ? AND age = ?
List<UserEntity> users = this.userMapper.selectList(wrapper);
for (UserEntity 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)
@Test
public void testWrapperQuery() {
QueryWrapper<UserEntity> wrapper = new QueryWrapper<>();
//SELECT id,name,age,email FROM tb_user WHERE password = ? AND age >= ? AND name IN (?,?,?)
wrapper.eq("email", "test2@baomidou.com")
.ge("age", 20)
.in("name", "jack", "jone", "tom");
List<UserEntity> users = this.userMapper.selectList(wrapper);
for (UserEntity 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 '王%'
@Test
public void testWrapperLike() {
QueryWrapper<UserEntity> wrapper = new QueryWrapper<>();
//SELECT id,user_name,password,name,age,email FROM tb_user WHERE name LIKE ?
//Parameters: %⼦%(String)
wrapper.like("name", "⼦");
List<UserEntity> users = this.userMapper.selectList(wrapper);
for (UserEntity 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
@Test
public void testWrapperOrder() {
QueryWrapper<UserEntity> wrapper = new QueryWrapper<>();
//SELECT id,user_name,password,name,age,email FROM tb_user ORDER BY age DESC
wrapper.orderByDesc("age");
List<UserEntity> users = this.userMapper.selectList(wrapper);
for (UserEntity 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 <> '活着')
@Test
public void testWrapperAnd() {
QueryWrapper<UserEntity> wrapper = new QueryWrapper<>();
//SELECT id,user_name,password,name,age,email FROM tb_user WHERE name= ? OR age = ?
wrapper.eq("name","jack").or().eq("age", 24);
List<UserEntity> users = this.userMapper.selectList(wrapper);
for (UserEntity user : users) {
System.out.println(user);
}
}
5.6、Select
在MP查询中,默认查询所有的字段,如果有需要也可以通过select⽅法进⾏指定字段。
@Test
public void testWrapperSelect() {
QueryWrapper<UserEntity> wrapper = new QueryWrapper<>();
//SELECT id,name,age FROM tb_user WHERE name = ? OR age = ?
wrapper.eq("name", "jack")
.or()
.eq("age", 24)
.select("id", "name", "age");
List<UserEntity> users = this.userMapper.selectList(wrapper);
for (UserEntity user : users) {
System.out.println(user);
}
}
6、ActiveRecord
6.1、开启AR功能
在MP中,开启AR⾮常简单,只需要将实体对象继承Model即可。
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("user")
public class UserEntity extends Model<UserEntity> {
@TableId(type = IdType.AUTO)
private Long id;
/**
* 表示该大字段不加入查询
**/
private String name;
/**
* 表示该字段在数据库表中不存在
**/
private Integer age;
/**
* 解决字段名不一致问题
**/
private String email;
}
6.2、根据主键查询
@Test
public void testAR() {
UserEntity user = new UserEntity();
user.setId(2L);
UserEntity user2 = user.selectById();
System.out.println(user2);
}
6.3、新增数据
@Test
public void testARInsert() {
UserEntity user = new UserEntity();
user.setName("应颠");
user.setAge(30);
user.setEmail("yingdian@blnp.cn");
boolean insert = user.insert();
System.out.println(insert);
}
6.4、更新操作
@Test
public void testARUpdate() {
UserEntity user = new UserEntity();
user.setId(8L);
user.setAge(35);
boolean update = user.updateById();
System.out.println(update);
}
6.5、删除操作
@Test
public void testARDelete() {
UserEntity user = new UserEntity();
user.setId(7L);
boolean delete = user.deleteById();
System.out.println(delete);
}
6.6、根据条件查询
@Test
public void testARFindById() {
UserEntity user = new UserEntity();
QueryWrapper<UserEntity> userQueryWrapper = new QueryWrapper<>();
userQueryWrapper.le("age","20");
List<UserEntity> users = user.selectList(userQueryWrapper);
for (UserEntity user1 : users) {
System.out.println(user1);
}
}