一、模块开发
Mybatis-Plus快速入门_小蜗牛耶的博客-CSDN博客_mybatis-plus
回顾可看我写的这篇文章
下文用boot代替springboot
mp代替mybatisplus
按照本文目录结构即可快速开发一套完整的CRUD接口,包括后面的Swagger测试
快速开发套路
这一套东西就是写接口对吧,所以==写接口也就是写业务类,写业务类有个小口诀==
- 建表写sql
- 定义实体类
- dao与mapper
- service和impl
- controller
那么我们用==boot写微服务模块==也有个小套路
- 建module
- 改pom
- 写yml
- 主启动
- 业务类
业务类对标我们上面的顺序
建module
建立新项目的时候可以用spring initializr初始化boot项目,也可以用maven的方式创建。这里我采用maven的方式来创建
改pom
pom出现的问题,跟网络环境有很大关系。
pom如果不断出错,可以更换网络或者删掉本地仓库中的包,重下
<?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.2.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.caq</groupId> <artifactId>mybatisplusdemo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>mybatisplusdemo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <scope>provided</scope> <version>2.7.0</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.7.0</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.0.5</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>
yml
这里我采用mysql8.0以上的驱动,配置url到时候?后面的值必须要加
驱动的名称和mysql5的也不一样记得区分
#端口号 server: port: 8003 #服务名 spring: application: name: service-edu #返回json的全局时间格式 jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8 datasource: username: root password: root url: jdbc:mysql://localhost:3306/mybatisplus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8 driver-class-name: com.mysql.cj.jdbc.Driver profiles: active: dev # mybatis日志 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
主启动
package com.caq.mybatisplusdemo; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MybatisplusdemoApplication { public static void main(String[] args) { SpringApplication.run(MybatisplusdemoApplication.class, args); } }
二、业务类开发
业务类(手动)
下面就开始写业务类了,业务类也遵守我们的步骤来写
建表写sql
CREATE TABLE user01 ( id int(20) NOT NULL COMMENT '主键ID' , name01 VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名', age01 INT(11) NULL DEFAULT NULL COMMENT '年龄', email01 VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱', PRIMARY KEY (id) ); DELETE FROM user01; INSERT INTO user01 (id, name01, age01, email01) 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');
实体类、dao、service
这里我们采用mp的插件,一键完成创建
==安装插件==
==idea中连接数据库==
==右键你要一键生成实体类,dao,service的表格generator即可==
==生成说明==
==service接口说明==
==那么我们是不是要实现这么多方法呢?==
当然不用,mp给我们定义好了一个IService的实现类,我们只需要实现类继承它并实现接口即可
controller
调用service,service调用mapper(dao)
开发controller,service,dao的过程就叫开发接口
为了前后端更好的沟通,我们可以定义一个统一的返回类
统一返回类
状态码定义
package com.caq.commonutils; public interface ResultCode { //状态码:成功 public static Integer SUCCESS = 20000; //状态码:失败 public static Integer ERROR = 20001; }
统一返回类型R
package com.caq.commonutils; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.util.HashMap; import java.util.Map; /** * 结果类 */ @Data public class R<T> { @ApiModelProperty(value = "是否成功") private Boolean success; @ApiModelProperty(value = "返回码") private Integer code; @ApiModelProperty(value = "返回消息") private String message; @ApiModelProperty(value = "返回数据") private Map<String, Object> data = new HashMap<>(); //私有的构造器 private R() {} //成功的静态方法 public static R ok() { R r = new R(); r.setSuccess(true); r.setCode(ResultCode.SUCCESS); r.setMessage("您的操作成功啦(*^▽^*)"); return r; } //失败的静态方法 public static R error() { R r = new R(); r.setSuccess(false); r.setCode(ResultCode.ERROR); r.setMessage("您的操作失败啦(⊙︿⊙)"); return r; } //the follow methods all return this,链式编程 public R success(Boolean success){ this.setSuccess(success); return this; } public R code(Integer code){ this.setCode(code); return this; } public R message(String message){ this.setMessage(message); return this; } public R data(String key, Object value){ this.data.put(key, value); return this; } public R data(Map<String, Object> map){ this.setData(map); return this; } }
业务类(自动)
好,我们记住了这些固定的步骤之后其实还有个更简单的方式哦!
那就是mp里面的代码生成器!
下面直接放代码,因为它是固定的,我们只需要会更改即可
public class CodeGenerator { @Test public void genCode() { // 1、创建代码生成器 AutoGenerator mpg = new AutoGenerator(); // 2、全局配置 GlobalConfig gc = new GlobalConfig(); String projectPath = System.getProperty("user.dir"); gc.setOutputDir(projectPath + "/src/main/java"); gc.setAuthor("Pyy"); gc.setOpen(false); //生成后是否打开资源管理器 gc.setServiceName("%sService"); //去掉Service接口的首字母I gc.setIdType(IdType.AUTO); //主键策略 gc.setSwagger2(true);//开启Swagger2模式 mpg.setGlobalConfig(gc); // 3、数据源配置 DataSourceConfig dsc = new DataSourceConfig(); dsc.setUrl("jdbc:mysql://localhost:3306/srb_core?serverTimezone=GMT%2B8&characterEncoding=utf-8"); dsc.setDriverName("com.mysql.cj.jdbc.Driver"); dsc.setUsername("root"); dsc.setPassword("root"); dsc.setDbType(DbType.MYSQL); mpg.setDataSource(dsc); // 4、包配置 PackageConfig pc = new PackageConfig(); pc.setParent("com.caq"); pc.setEntity("entity"); //此对象与数据库表结构一一对应,通过 DAO 层向上传输数据源对象。 mpg.setPackageInfo(pc); // 5、策略配置 StrategyConfig strategy = new StrategyConfig(); strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略 strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略 strategy.setEntityLombokModel(true); // lombok strategy.setLogicDeleteFieldName("is_deleted");//逻辑删除字段名 strategy.setEntityBooleanColumnRemoveIsPrefix(true);//去掉布尔值的is_前缀(确保tinyint(1)) strategy.setRestControllerStyle(true); //restful api风格控制器 mpg.setStrategy(strategy); // 6、执行 mpg.execute(); } }
MP插件
业务类搞好后,为了让接口的功能更完善,我们最后加上mp自带的插件
baomidou.gitee.io/mybatis-plu…
mp的2.x文档更详细一点
下面分别介绍主要的插件和一些常见知识点
主键生成策略
下面是我对实体类字段进行的设置,
这样设置的话我的主键在每次创建新用户的时候都会自动填充为分布式全局唯一ID 字符串类型
private static final long serialVersionUID = 1L; @ApiModelProperty(value = "讲师ID") /** * 分布式应用时,我们需要生成分布式ID,可以选择使用@TableId(type=IdType.ID_WORKER),数据库中的主键为: * IdType包括以下几类: * AUTO : 数据库主键自增 * INPUT: 用户自行输入 * ID_WORKER: 分布式全局唯一ID, 长整型 * UUID: 32位UUID字符串 * NONE: 无状态 * ID_WORKER_STR: 分布式全局唯一ID 字符串类型 */ @TableId(value = "id", type = IdType.ID_WORKER_STR) private String id;
自动填充详解
自动填充一般应用在数据库创建时间或修改时间字段
[自动填充功能官网](自动填充功能 | MyBatis-Plus (baomidou.com))
配置类
package com.caq.servicebase.handle; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import org.apache.ibatis.reflection.MetaObject; import org.springframework.stereotype.Component; import java.util.Date; @Component public class MyMetaObjectHandler implements MetaObjectHandler { //插入的时候填充创建和修改字段 @Override public void insertFill(MetaObject metaObject) { //属性名称,不是字段名称 this.setFieldValByName("gmtCreate",new Date(),metaObject); this.setFieldValByName("gmtModified",new Date(),metaObject); } //修改的时候填充修改字段 @Override public void updateFill(MetaObject metaObject) { this.setFieldValByName("gmtModified",new Date(),metaObject); } }
定义字段
@ApiModelProperty(value = "创建时间") @TableField(fill= FieldFill.INSERT) private Date gmtCreate; @ApiModelProperty(value = "更新时间") @TableField(fill=FieldFill.INSERT_UPDATE) private Date gmtModified;
测试自动填充
乐观锁
锁是针对数据冲突的解决方案
==悲观锁==
正如其名,它指的是对数据被外界修改持保守(悲观),因此在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制
==乐观锁==
相对悲观锁而言,乐观锁假设认为数据一般情况下不会有冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现了冲突,则让返回用户错误的信息,让用户决定如何去做。乐观锁的实现方式一般是记录数据版本
乐观锁的实现方式
- 取出记录,获取当前Version
- 更新时,带上这个version
- 执行更新时,set version = newVersion where version = oldVersion
- 如果version不对,就更新失败
乐观锁配置需要两步
配置插件
//乐观锁插件 @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor(); }
在实体类的字段上加上@Version
注解
@Version private Integer version;
逻辑删除
只对自动注入的 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()
附录:
- 逻辑删除是为了方便数据恢复和保护数据本身价值等等的一种方案,但实际就是删除。
- 如果你需要频繁查出来看就不应使用逻辑删除,而是以一个状态去表示。
配置
@Bean public ISqlInjector sqlInjector() { return new LogicSqlInjector(); }
实体类字段上加上@TableLogic
注解
@TableLogic private Integer deleted;
分页
- 自定义查询语句分页(自己写sql/mapper)
- spring 注入 mybatis 配置分页插件
配置类
//分页插件 @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); }
性能分析
性能分析拦截器,用于输出每条 SQL 语句及其执行时间
注意!该插件只用于开发环境,不建议生产环境使用。。。
配置类
通用CRUD
介绍完了mp的组件,要想使用只需要把它们写进一个配置类中让boot扫描即可
配置类
注入mp插件,完善接口功能
CRUD接口
package com.caq.mybatisplusdemo.controller; @Api("crud测试") @RestController @RequestMapping("/testMP/user") public class crudDemo { @Autowired UserService userService; @ApiOperation("增加用户") @PostMapping("save") public R saveUser(@RequestBody User user){ boolean save = userService.save(user); if (save){ return R.ok(); }else { return R.error(); } } @ApiOperation("查看所有用户") @GetMapping("list") public R listUser(){ List<User> list = userService.list(null); return R.ok().data("items",list); } @ApiOperation("查看某个用户") @GetMapping("getByIdUser") public R getByIdUser(@PathVariable String id){ User user = userService.getById(id); return R.ok().data("user",user); } @ApiOperation("按ID删除user") @DeleteMapping("delete") public R removeUser(@ApiParam(name = "id",value = "讲师ID",required = true)@PathVariable String id){ boolean delete = userService.removeById(id); if (delete){ return R.ok(); }else { return R.error(); } } @ApiOperation("按ID更改user") @PostMapping public R updateUser(@RequestBody User user){ boolean update = userService.updateById(user); if (update){ return R.ok(); }else { return R.error(); } } }