文章目录
一、MybatisPlus简介
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis的基础上只做增强不做改变,为简化开发、提高效率而生。
框架结构
二、MP-CRUD快速使用
数据库表结构
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for t_user
-- ----------------------------
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
`id` bigint(0) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`t_name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '姓名',
`age` int(0) NULL DEFAULT NULL COMMENT '年龄',
`email` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of t_user
-- ----------------------------
INSERT INTO `t_user` VALUES (1, 'Jone', 18, 'test1@baomidou.com');
INSERT INTO `t_user` VALUES (2, 'Jack', 20, 'test2@baomidou.com');
INSERT INTO `t_user` VALUES (3, 'Tom', 28, 'test3@baomidou.com');
INSERT INTO `t_user` VALUES (4, 'Sandy', 20, 'test4@baomidou.com');
INSERT INTO `t_user` VALUES (5, 'Billie', 24, 'test5@baomidou.com');
INSERT INTO `t_user` VALUES (7, 'tatat', 22, '614039502@qq.com');
INSERT INTO `t_user` VALUES (8, 'pang', 22, '614039502@qq.com');
SET FOREIGN_KEY_CHECKS = 1;
实体类
package cn.guet.quick_start.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 lombok.AllArgsConstructor;
import lombok.Data;
/**
* @author pangjian
* @ClassName User
* @Description
* @date 2021/5/20 13:44
*/
@Data
@AllArgsConstructor
@TableName("t_user")
public class User {
@TableId(value = "id",type = IdType.AUTO)
private Integer id;
@TableField("t_name")
private String name;
private Integer age;
private String email;
/*
@TableField(exist = false)
private String salary;
*/
}
注解使用
- @TableId(value = “id”,type = IdType.AUTO)
如果你的实体类有一个id属性,mp会默认它为主键,如果你没有设置id值,它会帮你生成一个全局唯一的id值,因为主键一定是唯一的,但数据库主键已经设计了自增策略,我不想它帮我生成,如果数据库设置了id自增,改变type就可以实现id自增,原来默认是分布式全局唯一ID,现在变成了主键自增策略
- @TableField(“t_name”)
我们数据库表的字段名没有name,mp默认是把成员变量名当作表字段名去执行sql语句,我们可以使用该注解去映射数据库字段名
- @TableField(exist = false)
把该属性设置为不是数据库字段
- @TableName(“t_user”)
mp会默认将pojo类名当作表名,如果类名和表名不一致可以使用注解,我们可以使用该注解去映射数据库表名
mybatisplus-实现数据库访问(Mapper层)
前提条件
package cn.guet.quick_start.Mapper;
import cn.guet.quick_start.pojo.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* @author pangjian
* @Interface UserMapper
* @Description 继承BaseMapper<T>,泛型填写自己要操作的实体类,就可以不用写sql语句直接调用内置通用的Mapper,实现对单表的大部分CURD
* @date 2021/5/20 13:46
*/
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
测试
插入
@Test
void insert() {
// id不用设值,直接用数据库的主键自增策略
User user = new User(null,"nihao",22,"614039502@qq.com");
userMapper.insert(user);
// 插入后可以立即返回主键
System.out.println("插入记录的主键:======"+user.getId());
}
删除
@Test
void delete() {
// 按id=9的条件去删除该记录
userMapper.deleteById(9);
}
更新
@Test
void update() {
User user = new User(8,"pmc",21,"614039502@qq.com");
// 更新id为8的用户信息
userMapper.updateById(user);
System.out.println(user);
}
查询
@Test
void queryListByMap() {
Map<String,Object> map = new HashMap<>();
map.put("age",20);
map.put("email","614039502@qq.com");
// 查询age为20并且id是2的user信息,map的构造的查询条件只能是等于
List<User> users1 = userMapper.selectByMap(map);
System.out.println("查看1列表信息:======="+users1);
//查询所有
List<User> users = userMapper.selectByMap(null);
System.out.println("查看2列表信息:======="+users);
}
mybatisplus-实现业务访问(Service层)
前提条件
package cn.guet.quick_start.service;
import cn.guet.quick_start.pojo.User;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author pangjian
* @Interface UserService
* @Description 业务层接口继承IService<T>接口,泛型写自己要操作的实体类,
* @date 2021/5/20 14:57
*/
public interface UserService extends IService<User> {
}
package cn.guet.quick_start.service.impl;
import cn.guet.quick_start.Mapper.UserMapper;
import cn.guet.quick_start.pojo.User;
import cn.guet.quick_start.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* @author pangjian
* @ClassName UserServiceImpl
* @Description 如果单单实现了UserService,要实现很多接口,mp为了不麻烦,会让我们去继承一个基类,2个泛型:要操作的Mapper接口和实体类
* @date 2021/5/20 14:58
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
测试
记录没有插入,有则更新的业务
@Test
void saveOrUpdate() {
User user = new User(8,"pang",22,"614039502@qq.com");
// 根据id去查询,没有则插入,有则更新
userService.saveOrUpdate(user);
}
三、MP-分页查询
前提条件
package cn.guet.quick_start.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author pangjian
* @ClassName MPconfig
* @Description 配置类,采用官方文档的最新版配置方式
* @date 2021/5/20 15:25
*/
@Configuration
public class MPconfig {
// 分页插件,要指定数据库类型
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
测试
/**
* @Description:要配置分页插件
* @return void
* @date 2021/5/20 15:24
*/
@Test
void page() {
// 第一页显示4条
IPage<User> iPage = new Page<>(1,4);
// 返回一个IPage对象,一定要指定泛型
IPage<User> page = userService.page(iPage);
//当前分页的所有的数据,根据Ipage对象定好的规则
List<User> records = page.getRecords();
System.out.println("==========="+records);
// 当前分页的总页数
System.out.println("总页数"+page.getPages());
// 设置每页显示的条数
// page.setSize(2);
// 当前页
System.out.println("当前页"+page.getCurrent());
// 每页显示的条数
System.out.println("每一页显示的条数"+page.getSize());
}
项目常用的自定义分页实现
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="cn.guet.quick_start.Mapper.UserMapper">
<select id="selectByAge" resultType="cn.guet.quick_start.pojo.User">
SELECT * FROM T_USER WHERE AGE=#{age}
</select>
</mapper>
Mapper编写
@Mapper
public interface UserMapper extends BaseMapper<User> {
IPage<User> selectByAge(IPage page, Integer age);
}
Service编写
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public IPage<User> selectByAge(Integer age) {
// 第一页显示两条数据,简单就写死了,如果项目,这两个值通过方法传参设置的
IPage<User> iPage = new Page<>(1,2);
iPage = userMapper.selectByAge(iPage,age);
return iPage;
}
}
测试
@Test
public void pageTest(){
IPage<User> iPage = userService.selectByAge(20);
System.out.println("分页数据==========="+iPage.getRecords());
System.out.println("总页数====="+iPage.getPages());
}
四、MP-条件构造器Wrapper
用于条件不是对等条件的查询条件语句,比如模糊查询等
测试
@Test
public void list(){
// 继承自 AbstractWrapper ,自身的内部属性 entity 也用于生成 where 条件及 LambdaQueryWrapper, 可以通过 new QueryWrapper().lambda() 方法获取
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 相当于执行了 SELECT t_name,age FROM t_user WHERE (email = ?)
queryWrapper
.select("t_name","age")
.eq("email","614039502@qq.com");
System.out.println("======="+userService.list(queryWrapper));
}
- 目前有一个不懂的地方就是查询字段为t_name,pojo实体类类用了注解@TableField(“t_name”)去映射了t_name,但查询结果注入属性为空,可能要做一些ResultMap映射
官方文档详细使用说明
五、MP-全局id生成策略
目前我们的主键生成策略是只应用于了User,假如我们要全局配置
mybatis-plus:
global-config:
db-config:
id-type: auto
就不需要每个类的id都要加注解去说明了
六、MP-代码生成器
依赖包
<!--代码生成器依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
<!--模板引擎-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
测试
package cn.guet.generator;
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.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
/**
* @author pangjian
* @ClassName GeneratorApp
* @Description TODO
* @date 2021/5/21 13:43
*/
public class GeneratorApp {
/**
* <p>
* 读取控制台内容
* </p>
*/
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.isNotBlank(ipt)) {
return ipt;
}
}
throw new MybatisPlusException("请输入正确的" + tip + "!");
}
public static void main(String[] args) {
/**
* @Description:构造代码生成器对象
* @date 2021/5/21 14:41
*/
AutoGenerator mpg = new AutoGenerator();
/**
* @Description:全局配置
* @date 2021/5/21 14:05
*/
GlobalConfig gc = new GlobalConfig();
// 获得当前项目路径,我们不希望生成在父工程里面,而是在子模块里面,需要加一级 /generator
String projectPath = System.getProperty("user.dir")+"/generator";
System.out.println(projectPath); // D:\springboot-study\mybatis-plus
// 设置生成路径,包是从另外的配置去设置,这里只是全局配置
gc.setOutputDir(projectPath + "/src/main/java");
// 每个类里面生成作者
gc.setAuthor("pangjian");
// 代码生成后是否要打开所在文件夹,一般我们设置为不需要
gc.setOpen(false);
// 实体属性 Swagger2 注解,要导入Swagger2的相关依赖,它根据数据库表注释去生成的
gc.setSwagger2(true);
// 会在mapper.xml生成一个基础的<ResultMap> 映射所有字段
gc.setBaseResultMap(true);
// 同文件的生成覆盖,就不会执行两次,生成两个内容相同的文件了
gc.setFileOverride(true);
// 设置生成的实体类名:直接用表名
gc.setEntityName("%s");
// 设置生成的Mapper类名:直接用表名加Mapper来命名,%s就是表名,后面拼接上Mapper
gc.setMapperName("%sMapper");
// 设置mapper.xml文件名
gc.setXmlName("%sMapper");
// 设置业务接口名称
gc.setServiceName("%sService");
// 设置业务实现类名称
gc.setServiceImplName("%sServiceImpl");
// 全局配置设置到代码生成器对象里面去
mpg.setGlobalConfig(gc);
/**
* @Description:数据源配置
* @date 2021/5/21 14:05
*/
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/mp?serverTimezone=UTC&userUnicode=true&characterEncoding=utf-8&userSSL=false");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
mpg.setDataSource(dsc);
/**
* @Description:包配置,控制生成的代码放在哪个包下面,全局配置只指定到了/src/main/java下
* @date 2021/5/21 14:08
*/
PackageConfig pc = new PackageConfig();
// 设置模块名,也就是/src/main/java+域名最后一级的包名(也是模块名,它的目录下就是springboot的入口启动器)
pc.setModuleName(scanner("模块名"));
// 设置项目建设时候的域名,也就是/src/main/java,接下来的包路径
pc.setParent("cn.guet");
mpg.setPackageInfo(pc);
/**
* @Description:自定义配置
* @date 2021/5/21 14:32
*/
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
// to do nothing
}
};
// 我们用什么模板就要配置什么模板路径
String templatePath = "/templates/mapper.xml.vm";
// 自定义输出配置
List<FileOutConfig> focList = new ArrayList<>();
// 自定义配置会被优先输出
focList.add(new FileOutConfig(templatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!,控制生成xml文件的路径
return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
+ "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
}
});
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
/**
* @Description:配置模板
* @date 2021/5/21 14:38
*/
TemplateConfig templateConfig = new TemplateConfig();
// 配置自定义输出模板
//指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
// templateConfig.setEntity("templates/entity2.java");
// templateConfig.setService();
// templateConfig.setController();
// 把默认生成的xml文件路径给关掉,只在自定义配置路径下生成
templateConfig.setXml(null);
mpg.setTemplate(templateConfig);
/**
* @Description:策略配置
* @date 2021/5/21 14:13
*/
StrategyConfig strategy = new StrategyConfig();
// 表名的生成策略:下划线转驼峰命名t_user->tUser
strategy.setNaming(NamingStrategy.underline_to_camel);
// 列名的生成策略:下划线转驼峰命名last_name->lastName
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
// 没有就注释掉
// strategy.setSuperEntityClass("你自己的父类实体,没有就不用设置!");
// 实体类是否支持lombok
strategy.setEntityLombokModel(true);
// controller类是否用@RestController注解
strategy.setRestControllerStyle(true);
// 公共父类,Controller没有父类注释掉
// strategy.setSuperControllerClass("你自己的父类控制器,没有就不用设置!");
// 写于父类中的公共字段
// strategy.setSuperEntityColumns("id");
strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
// 驼峰转连字符,比如表名pms_product -->controller类 @RequestMapping("/pmc/pms_product"),false的话是pmsProduct
strategy.setControllerMappingHyphenStyle(false);
// 过滤掉表前缀,生成的所有类就不会带前缀
strategy.setTablePrefix("t_");
mpg.setStrategy(strategy);
// 用默认的不用设置
// mpg.setTemplateEngine(new FreemarkerTemplateEngine());
/**
* @Description:代码生成器对象执行自动生成代码
* @date 2021/5/21 14:42
*/
mpg.execute();
}
}
结果: