mybatis官网
Mybatis-Plus是一个Mybatis框架的增强插件,核心理念
是为简化开发而生
根据官方描述,MP只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑.并且只需简单配置,即可快速进行 CRUD 操作,从而节省大量时间.代码生成,分页,性能分析等功能一应俱全。
ide工具:idea
springboot版本:2.3.0
mybatis-plus:3.3.2
jdk:12
数据库:MySQL(版本8.0+)
org.springframework.boot
spring-boot-starter-thymeleaf
org.springframework.boot
spring-boot-starter-web
mysql
mysql-connector-java
runtime
com.alibaba
druid-spring-boot-starter
1.1.21
org.projectlombok
lombok
true
com.baomidou
mybatis-plus-boot-starter
3.3.2
com.baomidou
mybatis-plus-generator
3.3.2
org.freemarker
freemarker
2.3.30
com.github.xiaoymin
knife4j-spring
1.9.6
com.github.xiaoymin
knife4j-spring-ui
1.9.6
org.springframework.boot
spring-boot-starter-test
test
org.junit.vintage
junit-vintage-engine
# 配置端口号
server:
port: 8080
# 数据源配置
spring:
datasource:
url: jdbc:mysql://localhost:3306/mp_test?useUnicode=true&characterEncoding=utf-8
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 自己的数据库连接密码
type: com.alibaba.druid.pool.DruidDataSource
#模板引擎设置
thymeleaf:
mode: HTML
encoding: UTF-8
servlet:
content-type: text/html
# 开发时关闭缓存,页面实时刷新
cache: false
# mybatis- plus配置
mybatis-plus:
# xml扫描,多个目录用逗号或者分号隔开隔开
mapper-locations: classpath:mapper/*.xml
# 以下配置均有默认值,可以不设置
global-config:
db-config:
#主键类型 AUTO:"数据库ID自增" INPUT:"用户输入ID",ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID";
id-type: auto
configuration:
# 是否开启自动驼峰命名规则映射:从数据库列名到Java属性驼峰命名的类似映射
map-underscore-to-camel-case: true
# 返回map时true:当查询数据为空时字段返回为null,false:不加这个查询数据为空时,字段将被隐藏
call-setters-on-nulls: true
# 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
create schema mp_test;
use mp_test;
CREATE TABLE `mp_user` (
`id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',
`name` varchar(32) DEFAULT NULL COMMENT '姓名',
`age` int(11) DEFAULT NULL COMMENT '年龄',
`skill` varchar(32) DEFAULT NULL COMMENT '技能',
`evaluate` varchar(64) DEFAULT NULL COMMENT '评价',
`fraction` bigint(11) DEFAULT NULL COMMENT '分数',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8mb4 COMMENT='用户信息表';
INSERT INTO `mp_user` VALUES (1, '小明', 20, '画画', '该学生在画画方面有一定天赋', 89);
INSERT INTO `mp_user` VALUES (2, '小兰', 19, '游戏', '近期该学生由于游戏的原因导致分数降低了', 64);
INSERT INTO `mp_user` VALUES (3, '张张', 18, '英语', '近期该学生参加英语比赛获得二等奖', 90);
INSERT INTO `mp_user` VALUES (4, '大黄', 20, '体育', '该学生近期由于参加篮球比赛,导致脚伤', 76);
INSERT INTO `mp_user` VALUES (5, '大白', 17, '绘画', '该学生参加美术大赛获得三等奖', 77);
INSERT INTO `mp_user` VALUES (7, '小龙', 18, 'JAVA', '该学生是一个在改BUG的码农', 59);
INSERT INTO `mp_user` VALUES (9, 'Sans', 18, '睡觉', 'Sans是一个爱睡觉,并且身材较矮骨骼巨大的骷髅小胖子', 60);
INSERT INTO `mp_user` VALUES (10, 'papyrus', 18, 'JAVA', '个性张扬的骷髅,给人自信、有魅力的骷髅小瘦子', 58);
INSERT INTO `mp_user` VALUES (11, '小红', 12, '画肖像', '小红是一个很红的女孩', 61);
INSERT INTO `mp_user` VALUES (12, 'Tom', 7, '理发', '全世界的理发师统称为tom老师', 61);
INSERT INTO `mp_user` VALUES (13, '小黑', 3, NULL, NULL, 61);
INSERT INTO `mp_user` VALUES (14, '楚子航', 24, '君焰', '杀胚一个', 99);
INSERT INTO `mp_user` VALUES (15, '路明非', 6, '自爆', '废材一个', 60);
在测试包新建CodeGenerator.java类
代码如下
package com.wlf.demo;
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 com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
/**
* @author wan [email protected]
* @version 1.0
* @className CodeGenerator
* @date 2020/6/5 9:35
**/
public class CodeGenerator {
/**
*
* 读取控制台内容
*
*/
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("无良芳");
gc.setOpen(false);
//这里可以设置为false,是一个api工具类
gc.setSwagger2(true); //实体属性 Swagger2 注解
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/mp_test?serverTimezone=GMT%2B8");
// dsc.setSchemaName("public");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("你的数据库密码");
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setModuleName(scanner("模块名"));
//这里需要填上自己的项目包路径。
pc.setParent("com.wlf.demo");
mpg.setPackageInfo(pc);
// 自定义配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
// to do nothing
}
};
// 如果模板引擎是 freemarker
String templatePath = "/templates/mapper.xml.ftl";
// 如果模板引擎是 velocity
// String templatePath = "/templates/mapper.xml.vm";
// 自定义输出配置
List focList = new ArrayList<>();
// 自定义配置会被优先输出
focList.add(new FileOutConfig(templatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
// return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
// + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
return projectPath + "/src/main/resources/mapper/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
}
});
/*
cfg.setFileCreate(new IFileCreate() {
@Override
public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) {
// 判断自定义文件夹是否需要创建
checkDir("调用默认方法创建的目录,自定义目录用");
if (fileType == FileType.MAPPER) {
// 已经生成 mapper 文件判断存在,不想重新生成返回 false
return !new File(filePath).exists();
}
// 允许生成模板文件
return true;
}
});
*/
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
// 配置模板
TemplateConfig templateConfig = new TemplateConfig();
// 配置自定义输出模板
//指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
// templateConfig.setEntity("templates/entity2.java");
// templateConfig.setService();
// templateConfig.setController();
templateConfig.setXml(null);
mpg.setTemplate(templateConfig);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
// strategy.setSuperEntityClass("你自己的父类实体,没有就不用设置!");
strategy.setEntityLombokModel(true);
strategy.setRestControllerStyle(true);
// 公共父类
// strategy.setSuperControllerClass("你自己的父类控制器,没有就不用设置!");
// 写于父类中的公共字段
// strategy.setSuperEntityColumns("id");
strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
strategy.setControllerMappingHyphenStyle(true);
// strategy.setTablePrefix(pc.getModuleName() + "_");
//去除表名的前缀
strategy.setTablePrefix("mp_");
mpg.setStrategy(strategy);
mpg.setTemplateEngine(new FreemarkerTemplateEngine());
mpg.execute();
}
}
直接运行此类,提示输入模块名就是新的包名,作者在这里输入的是
mp,然后提示你输入表名,按规定输入表名回车,即可生成代码。
项目结构图
这里我说一下项目结构,和以前的ssm项目结构差不多,
config 一些配置类。
controller 控制层,接受处理请求
entity 实体类,没啥好说的。
mapper dao层,数据库连接层
service 服务层 处理一些复杂的服务,一般来说,不建议在控制层里写过多的代码。控制层只用来发送和接受数据,对数据的处理业务统统放在service层。
项目下新建config包,在此包下新建MybatisPlusConfig.java类
详细代码如下
@Configuration
public class MybatisPlusConfig {
/**
* 分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor(){
return new PaginationInterceptor();
}
}
@SpringBootApplication
@MapperScan("com.wlf.demo.mp.mapper") //扫描dao
public class SpringbootMybatisPlusApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootMybatisPlusApplication.class, args);
}
}
到这里环境搭建已经完成。
到这里我们可以看一看生成的代码service的实现类里面没有任何代码,MyBatis-Plus官方封装了许多基本CRUD的方法,可以直接使用大量节约时间,MP共通方法详见IService,ServiceImpl,BaseMapper源码,写入操作在ServiceImpl中已有事务绑定,这里我们列举一下常用的方法演示。
原本是打算写一个前端页面更直观的进行测试,算了算了,用插件测试测试得了
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private IUserService iUserService;
/**
* 根据用户id获取用户信息
* @param id 用户id
* @return User 用户实体
*/
@GetMapping("/getUser")
public User getUser(@RequestParam("id") Integer id){
return iUserService.getById(id);
}
/**
* 查询全部信息
* @return 返回用户实体类集合
*/
@GetMapping("/getList")
public List getList(){
return iUserService.list();
}
/**
* 分页查询全部数据
* 这里只做演示,不写前端
* @return IPage 分页数据
*/
@GetMapping("/getUserListPage")
public IPage getUserListPage(){
IPage page = new Page<>();
//当前页
page.setCurrent(4);
//每页条数
page.setSize(2);
return iUserService.page(page);
}
/**
* 根据指定字段查询用户信息集合
* @Return Collection 用户实体集合
*/
@GetMapping("/getListMap")
public Collection getListMap(){
Map map = new HashMap<>();
//kay是字段名 value是字段值
map.put("age",20);
return iUserService.listByMap(map);
}
/**
* 新增用户信息
*/
@GetMapping("/saveUser")
public void saveUser(){
User userInfoEntity = new User();
userInfoEntity.setName("陈墨瞳");
userInfoEntity.setSkill("侧写");
userInfoEntity.setAge(18);
userInfoEntity.setFraction(99L);
userInfoEntity.setEvaluate("无言灵,长得漂亮");
iUserService.save(userInfoEntity);
}
/**
* 批量新增用户信息
*/
@RequestMapping("/saveUserList")
public void saveUserList(){
//创建对象
User sans = new User();
sans.setName("芬格尔");
sans.setSkill("睡觉");
sans.setAge(18);
sans.setFraction(60L);
sans.setEvaluate("芬格尔是一个爱睡觉,并且身手十分了的");
User papyrus = new User();
papyrus.setName("零");
papyrus.setSkill("镜瞳");
papyrus.setAge(18);
papyrus.setFraction(99L);
papyrus.setEvaluate("高冷女王");
//批量保存
List list =new ArrayList<>();
list.add(sans);
list.add(papyrus);
iUserService.saveBatch(list);
}
/**
* 更新用户信息
*/
@RequestMapping("/updateUser")
public void updateUser(){
//根据实体中的ID去更新,其他字段如果值为null则不会更新该字段,参考yml配置文件
User userInfoEntity = new User();
userInfoEntity.setId(1L);
userInfoEntity.setAge(19);
iUserService.updateById(userInfoEntity);
}
/**
* 新增或者更新用户信息
*/
@RequestMapping("/saveOrUpdateUser")
public void saveOrUpdate(){
//传入的实体类userInfoEntity中ID为null就会新增(ID自增)
//实体类ID值存在,如果数据库存在ID就会更新,如果不存在就会新增
User userInfoEntity = new User();
userInfoEntity.setId(1L);
userInfoEntity.setAge(20);
iUserService.saveOrUpdate(userInfoEntity);
}
/**
* 根据ID删除用户信息
*/
@RequestMapping("/deleteUser")
public void deleteInfo(String id){
iUserService.removeById(id);
}
/**
* 根据ID批量删除用户信息
*/
@RequestMapping("/deleteUserList")
public void deleteInfoList(){
List userIdlist = new ArrayList<>();
userIdlist.add("12");
userIdlist.add("13");
iUserService.removeByIds(userIdlist);
}
/**
* 根据指定字段删除用户信息
*/
@RequestMapping("/deleteUserMap")
public void deleteInfoMap(){
//kay是字段名 value是字段值
Map map = new HashMap<>();
map.put("skill","删除");
map.put("fraction",10L);
iUserService.removeByMap(map);
}
}
这里推荐一个idea的插件,用来调试api十分方便
安装好后就可以愉快的测试api接口了。
当查询条件复杂的时候,我们可以使用MP的条件构造器,请参考下面的QueryWrapper条件参数说明。
查询方式 | 方法说明 | 实例 |
---|---|---|
eq | 等价于等于 | eq(“name”, “老王”)—>name = ‘老王’ |
allEq | 全部eq(或个别isNull) | allEq({id:1,name:“老王”,age:null}, false)—>id = 1 and name = ‘老王’ |
ne | 不等于<> | ne(“name”, “老王”)—>name <> ‘老王’ |
gt | 大于 > | gt(“age”, 18)—>age > 18 |
ge | 大于等于 >= | ge(“age”, 18)—>age >= 18 |
lt | 小于 < | lt(“age”, 18)—>age < 18 |
le | 小于等于 <= | le(“age”, 18)—>age <= 18 |
between | BETWEEN 值1 AND 值2 | between(“age”, 18, 30)—>age between 18 and 30 |
notBetween | NOT BETWEEN 值1 AND 值2 | notBetween(“age”, 18, 30)—>age not between 18 and 30 |
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 ‘王%’ |
isNull | 字段 IS NULL | isNull(“name”)—>name is null |
isNotNull | 字段 IS NOT NULL | isNotNull(“name”)—>name is not null |
in | 字段 IN (value.get(0), value.get(1), …) 字段 IN (v0, v1, …) |
in(“age”,{1,2,3})—>age in (1,2,3) in(“age”, 1, 2, 3)—>age in (1,2,3) |
notIn | 字段 NOT IN (value.get(0), value.get(1), …) 字段 NOT IN (v0, v1, …) |
notIn(“age”,{1,2,3})—>age not in (1,2,3) notIn(“age”, 1, 2, 3)—>age not in (1,2,3) |
inSql | 字段 IN ( sql语句 ) | inSql(“age”, “1,2,3,4,5,6”)—>age in (1,2,3,4,5,6) inSql(“id”, “select id from table where id < 3”)—>id in (select id from table where id < 3) |
notInSql | 字段 NOT IN ( sql语句 ) | notInSql(“age”, “1,2,3,4,5,6”)—>age not in (1,2,3,4,5,6) notInSql(“id”, “select id from table where id < 3”)—>id not in (select id from table where id < 3) |
groupBy | 分组:GROUP BY 字段, … | groupBy(“id”, “name”)—>group by id,name |
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 |
orderBy | 排序:ORDER BY 字段, … | orderBy(true, true, “id”, “name”)—>order by id ASC,name ASC |
having | HAVING ( sql语句 ) | having(“sum(age) > 10”)—>having sum(age) > 10 |
having(“sum(age) > {0}”, 11)—>having sum(age) > 11 |
||
or | 拼接 OR | eq(“id”,1).or().eq(“name”,“老王”)—>id = 1 or name = ‘老王’ |
or | OR 嵌套 | or(i -> i.eq(“name”, “李白”).ne(“status”, “活着”))—>or (name = ‘李白’ and status <> ‘活着’) |
and | AND 嵌套 | and(i -> i.eq(“name”, “李白”).ne(“status”, “活着”))—>and (name = ‘李白’ and status <> ‘活着’) |
nested | 正常嵌套 不带 AND 或者 OR | nested(i -> i.eq(“name”, “李白”).ne(“status”, “活着”))—>(name = ‘李白’ and status <> ‘活着’) |
下面演示了一些常见的实例,有一说一,就是一个字,香!!!
在controller包下新建一个UserPlusController类,代码如下,
@RestController
@RequestMapping("/userPlus")
public class UserPlusController {
@Resource
private IUserService iUserService;
@GetMapping("/getUserListPage")
public Map getUserListPage(){
Map map = new HashMap<>();
//查询成绩高于80的用户
QueryWrapper wrapper = new QueryWrapper<>();
wrapper.lambda().gt(User::getFraction,80);
List list = iUserService.list(wrapper);
map.put("userfraction",list);
//查询年龄大于5岁,小于等于15岁的同学
QueryWrapper wrapper1 = new QueryWrapper<>();
wrapper1.lambda().gt(User::getAge,5);
wrapper1.lambda().le(User::getAge,15);
List list1 = iUserService.list(wrapper1);
map.put("userAge5~15",list1);
//模糊查询技能字段带有画的数据,并且按照年龄降序
QueryWrapper wrapper2 = new QueryWrapper<>();
wrapper2.lambda().like(User::getSkill,"画");
wrapper2.lambda().orderByDesc(User::getAge);
List list2 = iUserService.list(wrapper2);
map.put("UserAgeSkill", list2);
//模糊查询名字带有"小"或者年龄大于18的学生
QueryWrapper wrapper3 = new QueryWrapper<>();
wrapper3.lambda().like(User::getName,"小");
wrapper3.lambda().or().gt(User::getAge,18);
List list3 = iUserService.list(wrapper3);
map.put("userOr",list3);
//查询评价不为null的学生,并分页
IPage page = new Page<>();
page.setCurrent(1);
page.setSize(5);
QueryWrapper wrapper4 = new QueryWrapper<>();
wrapper4.lambda().isNotNull(User::getEvaluate);
page = iUserService.page(page,wrapper4);
map.put("userPage",page);
return map;
}
}
你认为mp的条件构造器这么香了还需要自己写sql语句嘛,好吧,有些时候确实需要。介绍一下自定义sql语句,没啥难的,和以前没有mp一样,自己添加dao层接口,在mapper.xml里面写sql语句,就这还没完,mp对于自定义sql语句还有优化。香就完事了。没啥好说的看一张图吧。
回归正题。需求查询分数大于80的学生信息,并分页。
第一步,编写mapper.xml语句,看看这简洁分页查询语句,直接看语句你甚至都感觉不到这个语句竟然可以做到分页查询。这和普通的查询没什么区别嘛。
第二步,编写dao层代码,我直接贴代码
/**
* 查询大于该分数的学生,并分页
* @param page 分页参数
* @param fraction 分数
* @return 分页数据
*/
IPage selectUserByFraction(IPage page,Long fraction);
这里再给大家推荐一个插件。
装上之后dao层代码会有提示点击直接跳转到mapper文件中,也可以直接生成mapper标签。香!!!
如图点击那个小鸟会有神奇的事情发生。
第三步,给service接口添加方法
/**
* 查询大于该分数的学生,并分页
* @param page 分页参数
* @param fraction 分数
* @return 分页数据
*/
IPage selectUserByFraction(IPage page, Long fraction);
再实现方法。
@Override
public IPage selectUserByFraction(IPage page, Long fraction) {
return this.baseMapper.selectUserByFraction(page,fraction);
}
最后我们controller测试一下,新建UserCustomController类
@RestController
@RequestMapping("/userCustom")
public class UserCustomController {
@Resource
private IUserService iUserService;
/**
* 查询分数大于80
* @return 分页数据
*/
@GetMapping("/getUserListSql")
public IPage getUserListSql() {
IPage page = new Page<>();
page.setCurrent(1);
page.setSize(2);
return iUserService.selectUserByFraction(page, 80L);
}
}
到这里文章也基本结束了。好的。
原本想弄个前端界面的,嗨,谁说前端简单我锤谁。
有什么问题可以评论区告诉我呦
项目源码点击这里