jdk:1.8
mybatis-plus:3.5.2
springboot:2.7.1
PostgreSQL:14.4
mybatis-plus官网
不多说生涩的理论,我的感受是:
虽然复杂的SQL还是要手写,但是,都这样了还要啥自行车?
新建springboot项目,添加依赖
<!--数据库驱动-->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.11</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--代码生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.2</version>
</dependency>
<!--自动代码生成到时候需要配置开启swagger注解,所以导入swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<!--模板引擎,规定必须设置-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
配置数据源
代码生成是根据数据库生成相应的实体,服务等,所以我们要先建一张表:
/*
Navicat Premium Data Transfer
Source Server : localhost_5432
Source Server Type : PostgreSQL
Source Server Version : 140004
Source Host : localhost:5432
Source Catalog : db_ds_ss
Source Schema : test
Target Server Type : PostgreSQL
Target Server Version : 140004
File Encoding : 65001
Date: 01/07/2022 11:39:21
*/
-- ----------------------------
-- Table structure for ds_user
-- ----------------------------
DROP TABLE IF EXISTS "test"."ds_user";
CREATE TABLE "test"."ds_user" (
"ds_user_name" varchar(255) COLLATE "pg_catalog"."default" NOT NULL,
"ds_user_password" varchar(255) COLLATE "pg_catalog"."default" NOT NULL,
"ds_user_email" varchar(255) COLLATE "pg_catalog"."default",
"ds_user_tel" varchar(255) COLLATE "pg_catalog"."default",
"ds_user_role" int2 NOT NULL,
"create_time" timestamp(0) DEFAULT now(),
"modify_time" timestamp(0) DEFAULT now(),
"deleted" int2 NOT NULL DEFAULT 0,
"ds_user_id" serial4 NOT NULL
)
;
COMMENT ON COLUMN "test"."ds_user"."ds_user_name" IS '用户名 ';
COMMENT ON COLUMN "test"."ds_user"."ds_user_password" IS '密码';
COMMENT ON COLUMN "test"."ds_user"."ds_user_email" IS '邮箱';
COMMENT ON COLUMN "test"."ds_user"."ds_user_tel" IS '手机';
COMMENT ON COLUMN "test"."ds_user"."ds_user_role" IS '用户权限:如果为0则为普通用户,1则为管理员 ';
COMMENT ON COLUMN "test"."ds_user"."create_time" IS '创建时间 ';
COMMENT ON COLUMN "test"."ds_user"."modify_time" IS '修改时间 ';
COMMENT ON COLUMN "test"."ds_user"."deleted" IS '删除标志 ,逻辑删除,0表示未删除,1表示删除';
COMMENT ON COLUMN "test"."ds_user"."ds_user_id" IS 'id ,自增';
-- ----------------------------
-- Uniques structure for table ds_user
-- ----------------------------
ALTER TABLE "test"."ds_user" ADD CONSTRAINT "ds_user_ds_user_id_key" UNIQUE ("ds_user_name");
-- ----------------------------
-- Primary Key structure for table ds_user
-- ----------------------------
ALTER TABLE "test"."ds_user" ADD CONSTRAINT "ds_user_pk" PRIMARY KEY ("ds_user_id");
阿里的开发手册规定:id, gmt_create, gmt_modified必须要有。(创建、修改时间自动插入实现见下文)
另外,逻辑删除也可以加上,也就是用一个表示判断数据是否被删除了,而不是真正的移除这一条数据,删除的sql也从delete变成了update xxx set delete=1 where xxx
。
yml中配置数据源:
spring:
datasource:
druid:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://localhost:5432/db_ds_ss?currentSchema=test
username: postgres
password: 123456
initial-size: 5
max-active: 20
min-idle: 5
max-wait: 60000
mybatis-plus:
configuration:
#日志输出
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
#逻辑删除
logic-delete-value: 1
logic-not-delete-value: 0
写代码
搞个测试就可以了,方便
@Test
void autocode() {
// 最开始是配置数据源,postgresql还得指定架构,这里我就指定了用test架构
FastAutoGenerator.create(new DataSourceConfig.Builder("jdbc:postgresql://localhost:5432/db_ds_ss?currentSchema=test", "postgres", "123456").schema("test"))
// 全局配置
.globalConfig(builder -> {
builder.author("yangsf") // 设置作者
.enableSwagger() // 开启 swagger 模式(根据数据库的注释等生成swagger的注解,真的牛)
.fileOverride() // 覆盖已生成文件
.outputDir(System.getProperty("user.dir") + "/src/main/java") // 指定输出目录
.disableOpenDir() // 生成后不打开资源管理器
.dateType(DateType.ONLY_DATE) // 时间类型
.commentDate("yyyy-MM-dd hh:mm:ss"); // 时间格式
})
// 包配置,也就是生成哪些文件夹
.packageConfig(builder -> {
builder.parent("com.yangsf") // 设置父包名 也就是生成在输出目录的 com/yangsf下
.moduleName("mybatisplus") // 设置父包模块名 也就是代码放在com/yangsf/mybatisplus下
.pathInfo(Collections.singletonMap(OutputFile.xml, System.getProperty("user.dir")+"/src/main/resources/mapper")) // 设置mapperXml生成路径
.service("service") // 接下来的几个都是包名,也就是对应的代码的存放目录
.entity("entity")
.mapper("mapper")
.controller("controller")
.xml("mapper")
.other("utils");
})
// 策略配置
.strategyConfig(builder -> {
builder.addInclude("ds_user") // 这里填数据库表名,可以填多个,用’,‘隔开
.addTablePrefix("ds_") // 忽略前缀
// mapper(dao)层的配置
.mapperBuilder()
.formatMapperFileName("%sMapper") // 生成的文件名都叫xxxmapper(根据表名)
.enableMapperAnnotation() //开启@mapper注解
.superClass(BaseMapper.class) // 继承BaseMapper类
.formatXmlFileName("%sMapper") // 生成对应的xml都叫xxxmapper
// service层配置
.serviceBuilder()
.formatServiceFileName("%sService")
.formatServiceImplFileName("%sServiceImpl")
// 实体类配置
.entityBuilder()
.idType(IdType.AUTO) // 主键的策略,我选的是自增
.enableLombok() //开启 Lombok
.disableSerialVersionUID() //不实现 Serializable 接口,不生产 SerialVersionUID
.logicDeleteColumnName("deleted") //逻辑删除字段名
.naming(NamingStrategy.underline_to_camel) //数据库表映射到实体的命名策略:下划线转驼峰命
.columnNaming(NamingStrategy.underline_to_camel) //数据库表字段映射到实体的命名策略:下划线转驼峰命
.addTableFills(
new Column("create_time", FieldFill.INSERT),
new Column("modify_time", FieldFill.INSERT_UPDATE)
) //添加表字段填充,"create_time"字段自动填充为插入时间,"modify_time"字段自动填充为插入修改时间,到时候需要编写具体的代码实现
.enableTableFieldAnnotation() // 开启生成字段注解
// controller层配置
.controllerBuilder()
.formatFileName("%sController") //格式化 Controller 类文件名称,%s进行匹配表名,如 UserController
.enableRestStyle(); //开启生成 @RestController 控制器
})
// 模板引擎配置 ,默认是Velocity
.templateEngine(new FreemarkerTemplateEngine())
.execute(); // 执行
}
运行后,就会发现:
直接爽。
代码生成了,怎么用?直接调方法就完事。
也就是插入。
这里要注意的是,插入时间也就是creat_time字段,自己每次手动写插入时间是很low比的行为,我们直接统一解决,让他自己插入。
顺便把修改时间一起搞了。
首先,要有一个注解@TableField,因我我们生成代码的时候配置了这个注解,所以自己就有了:
CTRL+鼠标左键可以点进FieldFill查看其中的枚举的解释。
我们需要一个策略来填充这两个字段,新建一个handler包,新建一个MyMetaObjectHandler类实现MetaObjectHandler接口:
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill ....");
this.setFieldValByName("createTime", new Date(), metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill ....");
this.setFieldValByName("modifyTime", new Date(), metaObject);
}
}
我们之后就不用手动输入这两个时间了。
测试:
@Autowired
UserService userService;
@Test
void contextLoads() {
// 保存一个
User user = new User();
user.setDsUserName("root");
user.setDsUserPassword("123456");
user.setDsUserRole(1);
userService.save(user);
// 保存多个
User user1 = new User();
user1.setDsUserName("admin");
user1.setDsUserPassword("123456");
user1.setDsUserRole(1);
User user2 = new User();
user2.setDsUserName("xiaoming");
user2.setDsUserPassword("123456");
user2.setDsUserRole(1);
ArrayList<User> users = new ArrayList<>();
users.add(user1);
users.add(user2);
userService.saveBatch(users);
}
数据库中果然增加了,并且插入时间也自动填充上了。
先来个简单的查询所有,为了方便我们去实体类中加一个toString方法,然后就可以直接开始查询刚刚插入的数据了:
@Test
void selectTest() {
// 查询所有
userService.list().forEach(System.out::println);
}
日志输出和结果都在这里,注意看标红的地方,这就是利用逻辑删除标志来判断记录是否已被删除。
如果要条件查询,就需要学习Wapper,mybatis-plus的条件构造器,不过我们在这里先不慌学,在之后专门一章来说Wrrapper。
先来个根据id修改:
@Test
void updateTest() {
User user = new User();
user.setDsUserId(9);
user.setDsUserName("yangsf");r
userService.updateById(user);
}
这里将id为9的记录的ds_user_name字段修改为了“yangsf”,并且自动填充了修改时间,复杂的修改需要使用Wrapper(见下文)。
删除如果配置了逻辑删除,那就是逻辑删除(修改删除标志的值),如果没有配置,就是普通删除(删除记录)。
看个简单的,根据id删除:
@Test
void deleteTest() {
User user = new User();
user.setDsUserId(9);
userService.removeById(user);
}
这里将id为9的记录逻辑删除了,来看看sql:
实际上只是更改了一个值,并不是物理删除。
条件构造器,生成sql里where后面的东西,一般分两种wrapper,查询wrapper和更新wrapper(它们俩的父wrapper是AbstractWrapper),能构造的条件很多,不用记,需要的时候去官网看即可,这里说一下用法
举两个例子,一个查一个改,主要是学会怎么看官方文档。
查询权限为1且名为root且id大于3的用户的创建时间
第一步:查看官方文档构造条件相等的方法
第二步:查看条件构造大于的方法
第三步:查看只查询某字段的方法
于是找到eq,gt和select
然后看示例,例如eq:
即可写出下面的代码:
@Test
void wrapperOne() {
// 查询权限为1且名为root且id大于3的用户的创建时间
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("ds_user_role", 1)
.eq("ds_user_name", "root")
.gt("ds_user_id", 3)
.select("create_time");
userService.list(wrapper).forEach(System.out::println);
}
生成了这样一个sql:
修改名为admin且tel为空的字段的权限为0
第一步:依然是查看条件构造的方法
第二部:查看修改的方法
条件构造不必在说
于是找到set:
即可写出如下代码:
@Test
void wrapperTow() {
// 修改名为admin且tel为空的字段的权限为0
UpdateWrapper<User> wrapper = new UpdateWrapper<>();
wrapper.eq("ds_user_name", "admin")
.isNull("ds_user_tel")
.set("ds_user_role", 0);
userService.update(wrapper);
}
生成的sql:
内置了多个插件
这里只说分页,分页是真的爽。
不用分页插件分页,用limit的话,还要需要计算一下是从多少条开始,使用分页插件就再也不用计算了。
首先添加配置类:
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.POSTGRE_SQL));
return interceptor;
}
}
然后就能直接用了:
@Test
void pageTest01() {
// new 一个Page对象即可,第一个参数是页码,第二个参数是页面大小,比如第二页就是2,5
Page<User> userPage = new Page<>(1, 2);
// 还可以传个wrapper进去
userService.page(userPage, null);
// 输出查询结果
userPage.getRecords().forEach(System.out::println);
// 总共的记录数
System.out.println(userPage.getTotal());
// 总共的页数
System.out.println(userPage.getPages());
}
总结:一个字,爽!