1.前后端分离概念
2.数据库建表
新建数据库。
CREATE DATABASE gulischool;
建表
CREATE TABLE IF NOT EXISTS 'edu_teacher' (
'id' varchar(19) NOT NULL COMMENT '讲师ID',
'name' varchar(20) NOT NULL COMMENT '讲师姓名',
'intro' varchar(500) NOT NULL DEFAULT '' COMMENT '讲师简介',
'career' varchar(500) DEFAULT NULL COMMENT '讲师资历,一句话说明讲师',
'level' int(10) unsigned NOT NULL COMMENT '头衔 1高级讲师 2首席讲师',
'avatar' varchar(255) DEFAULT NULL COMMENT '讲师头像',
'sort' int(10) unsigned NOT NULL DEFAULT '0' COMMENT '排序',
'is_deleted' tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '逻辑删除 1(true)已删除, 0(false)未删除',
'gmt_create' datetime NOT NULL COMMENT '创建时间',
'gmt_modified' datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY ('id'),
UNIQUE KEY 'uk_name' ('name')
)ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COMMENT='讲师';
报错
1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near...
原因是语法错误,改为
CREATE TABLE IF NOT EXISTS edu_teacher (
id char(19) NOT NULL COMMENT '讲师ID',
name varchar(20) NOT NULL COMMENT '讲师姓名',
intro varchar(500) NOT NULL DEFAULT '' COMMENT '讲师简介',
career varchar(500) DEFAULT NULL COMMENT '讲师资历,一句话说明讲师',
level int(10) unsigned NOT NULL COMMENT '头衔 1高级讲师 2首席讲师',
avatar varchar(255) DEFAULT NULL COMMENT '讲师头像',
sort int(10) unsigned NOT NULL DEFAULT '0' COMMENT '排序',
is_deleted tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '逻辑删除 1(true)已删除, 0(false)未删除',
gmt_create datetime NOT NULL COMMENT '创建时间',
gmt_modified datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (id),
UNIQUE KEY uk_name (name)
)ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COMMENT='讲师'
注:表设计规范参考《阿里巴巴java设计规范》如下。
1.表中必备三个字段:id,gmt_create,gmt_modified(gmt为格林时间),id必为主键,类型为bigint unsigned,单表时自增,步长为1.
2.库名与应用名称尽量一致
3.表名、字段名使用小写字母或数字,禁止以数字开头
4.表名不使用复数名词
5.表的命名最好加上"业务名称_表的作用",如"edu_teacher"
6.非负值必须使用unsigned
7.表达与否使用is_xxx
8.单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。 说明:如果预计三年后的数 据量根本达不到这个级别,请不要在创建表时就分库分表
9.小数类型为 decimal,禁止使用 float 和 double。 说明:float 和 double 在存储的时候,存在精度损 失的问题,很可能在值的比较时,得到不 正确的结果。如果存储的数据范围超过 decimal 的范围,建议 将数据拆成整数和小数分开存储
10.唯一索引名为 uk_字段名;普通索引名则为 idx_字段名。
…
3.搭建项目工程
项目的工程结构如图。
具体建立过程如下。
1)建立父工程,菜单栏选择file->new->new project->springboot initial
注意:笔者下面的project SDK选错了,应该选择1.8版本,java版本是8!!!
在pom.xml中更改springboot版本为2.2.1.RELEASE
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
添加如下字段,表示父工程是一个pom类型,用于管理依赖版本和公共依赖。
<packaging>pom</packaging>
删除pom文件中的dependencies。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
替换pom文件中的properties,进行版本的控制。
<properties>
<java.version>1.8</java.version>
<guli.version>0.0.1-SNAPSHOT</guli.version>
<mybatis-plus.version>3.0.5</mybatis-plus.version>
<velocity.version>2.0</velocity.version>
<swagger.version>2.7.0</swagger.version>
<aliyun.oss.version>2.8.3</aliyun.oss.version>
<jodatime.version>2.10.1</jodatime.version>
<poi.version>3.17</poi.version>
<commons-fileupload.version>1.3.1</commons-fileupload.version>
<commons-io.version>2.6</commons-io.version>
<httpclient.version>4.5.1</httpclient.version>
<jwt.version>0.7.0</jwt.version>
<aliyun-java-sdk-core.version>4.3.3</aliyun-java-sdk-core.version>
<aliyun-sdk-oss.version>3.1.0</aliyun-sdk-oss.version>
<aliyun-java-sdk-vod.version>2.15.2</aliyun-java-sdk-vod.version>
<aliyun-java-vod-upload.version>1.4.11</aliyun-java-vod-upload.version>
<aliyun-sdk-vod-upload.version>1.4.11</aliyun-sdk-vod-upload.version>
<fastjson.version>1.2.28</fastjson.version>
<gson.version>2.8.2</gson.version>
<json.version>20170516</json.version>
<commons-dbutils.version>1.7</commons-dbutils.version>
<canal.client.version>1.1.0</canal.client.version>
<docker.image.prefix>zx</docker.image.prefix>
<cloud-alibaba.version>0.2.2.RELEASE</cloud-alibaba.version>
</properties>
配置dependencyManagement,锁定依赖的版本,这里采用${xxx} 直接引用变量来自于上面properties的版本控制,
<!--依赖管理-->
<dependencyManagement>
<dependencies>
<!--Spring Cloud-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--mybatis-plus 持久层-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- velocity 模板引擎, Mybatis Plus 代码生成器需要 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>${velocity.version}</version>
</dependency>
<!--swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
</dependency>
<!--swagger ui-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>
<!--aliyunOSS-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>${aliyun.oss.version}</version>
</dependency>
<!--日期时间工具-->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>${jodatime.version}</version>
</dependency>
<!--xls-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>${poi.version}</version>
</dependency>
<!--xlsx-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>${poi.version}</version>
</dependency>
<!--文件上传-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>${commons-fileupload.version}</version>
</dependency>
<!--commons-io-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
<!--httpclient-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${httpclient.version}</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>${gson.version}</version>
</dependency>
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jwt.version}</version>
</dependency>
<!--aliyun-->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>${aliyun-java-sdk-core.version}</version>
</dependency>
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>${aliyun-sdk-oss.version}</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-vod</artifactId>
<version>${aliyun-java-sdk-vod.version}</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-vod-upload</artifactId>
<version>${aliyun-java-vod-upload.version}</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-sdk-vod-upload</artifactId>
<version>${aliyun-sdk-vod-upload.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>${json.version}</version>
</dependency>
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>${commons-dbutils.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.otter</groupId>
<artifactId>canal.client</artifactId>
<version>${canal.client.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
删除src目录,因为父工程不需要写代码,具体代码会在子模块中实现。
4.创建子模块service
在项目中创建maven子模块service。
如第3步,artifactId 节点后添加以下字段(因为service下还会建立子模块)
<packaging>pom</packaging>
添加依赖。
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<!--hystrix依赖,主要是用 @HystrixCommand -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!--服务注册-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--服务调用-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- velocity 模板引擎, Mybatis Plus 代码生成器需要 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
</dependency>
<!--swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
</dependency>
<!--lombok用来简化实体类:需要安装lombok插件-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--xls-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
</dependency>
<!--httpclient-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<!--commons-io-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<!--gson-->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
将上述至服务调用的依赖(spring cloud相关,暂且用不到,会影响后面的项目启动)暂时注释。
将service下的src删除。在service下新建子子模块service_edu,注意location一定选择在service下。
5.讲师管理模块的模块配置
service_edu模块下src/main/resources目录新建application.properties。
进行如下配置。
# 服务端口
server.port=8001
# 服务名
spring.application.name=service-edu
# 环境设置:dev、test、prod
spring.profiles.active=dev
# mysql数据库连接
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/guli?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=00000
# mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
上面服务端口配置为8001,如不进行配置则默认为8080。mysql数据中password与username需要替换为个人数据库的对应账户与密码。
6.mp中的代码生成器
在service下的pom文件导入了velocity依赖,用于Mybatis Plus 代码生成器。
<!-- velocity 模板引擎, Mybatis Plus 代码生成器需要 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
</dependency>
在test\java下新建包com.wangzhou.eduservice ,在包下新建CodeGenerator.java ,拷贝以下代码。注:之所以建在test目录下是因为代码生成器不属于需要项目部署的内容,仅仅是进行辅助开发的类。
package com.wangzhou.eduservice;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import org.junit.Test;
/**
* @王舟
*/
public class CodeGenerator {
@Test
public void run() {
// 1、创建代码生成器
AutoGenerator mpg = new AutoGenerator();
// 2、全局配置
GlobalConfig gc = new GlobalConfig();
gc.setOutputDir("E:\\ideaworkspace\\guli_parent\\service\\service_edu\\" + "/src/main/java"); //输出目录
gc.setAuthor("wangzhou"); //作者名
gc.setOpen(false); //生成后是否打开资源管理器,即自动把生成的代码目录结构展开
gc.setFileOverride(false); //重新生成时文件是否覆盖
gc.setServiceName("%sService"); //去掉Service接口的首字母I
gc.setIdType(IdType.ID_WORKER_STR); //主键策略,ID_WORKER_STR(主键为字符串)&&ID_WORKER(主键为数)
gc.setDateType(DateType.ONLY_DATE);//定义生成的实体类中日期类型
gc.setSwagger2(true);//开启Swagger2模式
mpg.setGlobalConfig(gc);
// 3、数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/guli?serverTimezone=GMT%2B8");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("00000");
dsc.setDbType(DbType.MYSQL);
mpg.setDataSource(dsc);
// 4、包配置
PackageConfig pc = new PackageConfig();
//生成包:com.achang.eduservice
pc.setModuleName("eduservice"); //模块名
pc.setParent("com.wangzhou");
//生成包:com.achang.controller
pc.setController("controller");
pc.setEntity("entity");
pc.setService("service");
pc.setMapper("mapper");
mpg.setPackageInfo(pc);
// 5、策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setInclude("edu_teacher");//根据数据库哪张表生成,有多张表就加逗号继续填写
strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略
strategy.setTablePrefix(pc.getModuleName() + "_"); //生成实体时去掉表前缀
strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略
strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain = true) setter链式操作
strategy.setRestControllerStyle(true); //restful api风格控制器
strategy.setControllerMappingHyphenStyle(true); //url中驼峰转连字符
mpg.setStrategy(strategy);
// 6、执行
mpg.execute();
}
}
这里代码会爆红,将edu_service下的pom文件中artifactId改为service即可。
注意上面主键策略根据代码注释进行选择。第3项中,mp的代码生成器数据库的配置需要单独配置,需要根据自己的数据库进行配置,而不是直接使用项目中配置文件的配置。执行run()方法则可以生成代码了。
7.讲师列表
进入controller包下EduTeacherController,爆红,信息为@RequestMapping报Cannot resolve symbol 'RestController’错误,解决办法: 在错误处按alt+enter,选择add spring-boot-start-web to classpath。
EduTeacherController中有一个注解@RestController,其具体实现中有两个底层的注解@Controller和@ResponseBody,@Controller表示该类交给springboot管理,是控制层,@ResponseBody说明该类会返回一个json的数据。
在controller层自动注入service。
// 注入service
@Autowired
private EduTeacherService eduTeacherService;
service无需再注入mapper,因为mp在底层已经帮助我们进行了mapper的调用。
查询表中所有数据。
// 查询表中所有表数据
// Rest风格
@GetMapping("findAll")
public List<EduTeacher> findAllTeacher() {
List<EduTeacher> list = eduTeacherService.list(null);
return list;
}
在eduservice模块下新建模块启动类。
@SpringBootApplication
public class EduApplication {
public static void main(String[] args) {
SpringApplication.run(EduApplication.class, args);
}
}
由于mapper是接口,需要配置mapper的自动扫描。在eduservice下新建config包,包下新建EduConfig类,配置Mapper的自动扫描。
@Configuration
@MapperScan("com.wangzhou.eduservice.mapper")
public class EduConfig {
}
启动EduApplication。报错
java: java.lang.IllegalAccessError: class lombok.javac.apt.LombokProcessor (in unnamed module @0x590)
这是因pom文件中没有指定lombok版本或者版本太低。在maven仓库搜索Lombok最新版本,将其替换eduservice下的pom文件的对应依赖。
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>
重新编译重新编译可以顺利通过,通过时将打印如下日志信息。注意如果此时报Unsupported class file major version XX 是由于编译时的jdk版本与运行时的jdk版本不一致。笔者尝试解决未果,从头搭建项目,注意第3节中的提醒,保持编译、运行时jdk版本统一为jdk1.8。
2021-10-06 09:40:06.014 INFO 11352 --- [ main] com.wangzhou.eduservice.EduApplication : Started EduApplication in 4.808 seconds (JVM running for 6.533)
先在数据库中随意添加几条数据,访问http://localhost/8001/eduservice/edu-teacher/findAll。显示的数据是服务器端返回的json数据(@RestController 会将contoller交给springboot管理,并返回json数据)。显示数据如下。
[{"id":"1","name":"wz","intro":"coolBoy","career":"developer","level":100,"avatar":null,"sort":0,"isDeleted":0,"gmtCreate":"2021-12-01T01:56:30.000+0000","gmtModified":"2021-10-06T01:56:44.000+0000"},{"id":"2","name":"cc","intro":"beautifulgirl","career":"writter","level":18,"avatar":null,"sort":0,"isDeleted":0,"gmtCreate":"2021-10-06T01:57:49.000+0000","gmtModified":"2021-10-06T01:58:02.000+0000"}]
注意上面的时间信息的显示,似乎不太正确。正常的应该是2021-12-01 09:56:30 的格式。这是因为这个时间是带时区的显示,显示的是格林尼的标准时间。在application.properties中可以配置时区和时间格式。
#配置时间格式及时区
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
重启项目,访问url地址。撒花!
[{"id":"1","name":"wz","intro":"coolBoy","career":"developer","level":100,"avatar":null,"sort":0,"isDeleted":0,"gmtCreate":"2021-12-01 09:56:30","gmtModified":"2021-10-06 09:56:44"},{"id":"2","name":"cc","intro":"beautifulgirl","career":"writter","level":18,"avatar":null,"sort":0,"isDeleted":0,"gmtCreate":"2021-10-06 09:57:49","gmtModified":"2021-10-06 09:58:02"}]
下面我们开始crud的传统艺能,先搞个逻辑删除。
步骤如下:
(1)配置一个逻辑删除插件。
@Bean
public ISqlInjector sqlInjector() {
return new LogicSqlInjector();
}
(2)在实体类中对逻辑删除的标识属性isdeleted 添加注解@TableLogic 。
(3)在controller中编写逻辑删除方法。
@DeleteMapping("/deleteTeacherById/{id}")
public boolean removeTeacher(@PathVariable String id) {
eduTeacherService.removeById(id);
return false;
}
对以上代码简要解释如下。
8.swagger整合
由于使用浏览器只能够测试get类型的提交,我们对于delete方法的提交则需要借助一些工具来测试。如swagger、postman。
使用swagger的作用是:
1.可以进行接口测试。
2.生成一个接口测试的文档,可以从接口文档中读到接口测试的参数,测试的具体功能等。
下面在项目中整合swagger。为了使所有模块都能够使用swagger来进行接口测试,我们新建立一个模块common来进行swagger的整合。如下图,在guli_parent下新建maven模块common。
导入相关依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>provided </scope>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<scope>provided </scope>
</dependency>
<!--lombok用来简化实体类:需要安装lombok插件-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided </scope>
</dependency>
<!--swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<scope>provided </scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<scope>provided </scope>
</dependency>
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- spring2.X集成redis所需common-pool2
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.6.0</version>
</dependency>-->
由于该模块下还会有子模块。删除src包。在pom文件中artifactId 后配置:
<packaging>pom</packaging>
common下新建maven子模块service_base。按照如下目录结构创建SwaggerConfig配置类。如果爆红,alt+enter,按照提示信息添加依赖并导包即可。
复制下面代码,配置swagger插件,使用Predicates 过滤url中admin/.* 或/error.* 的路径,包含这些串的url不进行显示。
package com.wangzhou.servicebase;
import com.google.common.base.Predicates;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket webApiConfig(){
return new Docket(DocumentationType.SWAGGER_2)
.groupName("webApi")
.apiInfo(webApiInfo())
.select()
.paths(Predicates.not(PathSelectors.regex("/admin/.*")))
.paths(Predicates.not(PathSelectors.regex("/error.*")))
.build();
}
private ApiInfo webApiInfo(){
return new ApiInfoBuilder()
.title("网站-课程中心API文档")
.description("本文档描述了课程中心微服务接口定义")
.version("1.0")
.contact(new Contact("wangzhou", "http://wangzhou.com",
"wangzhou@qq.com"))
.build();
}
}
要如何在service_edu中来使用这个service_base呢?
(1)引入依赖文件
在service_edu的pom文件中引入service_base作为依赖(敲service_base即有提示,也可以从service_base的pom文件复制groupId等信息)
<dependency>
<groupId>com.wangzhou</groupId>
<artifactId>service_base</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
(2)更改文件扫描规则
在service_base中SwaggerConfig是配置类。service_edu在启动时会扫描该模块的文件,然而配置类不在项目service_edu中。我们可以在service_edu的启动类中增加注解@ComponentScan(basePackages = {"com.wangzhou"}) ,这样所有com.wangzhou包下的文件都可以被扫描到。
现在来测试下。
启动项目,访问http://localhost:8001/swagger-ui.html。出现了一个绿油油的网页。说明swagger已经整合成功了。不过,好像还有一点点小瑕疵。提示"Uable to infer base url".
在启动类添加注解@EnableSwagger2 ,重启项目,再次访问。漂漂亮亮,干干净净了。
小手点一点。点edu-teacher-controller -> findAll ->try it out.
测试结果出来了。
测试下逻辑删除功能。
查看数据库。成功了。
在controller中removeTeacher()添加注解,可以使生成的文档信息包含注释,方便调试,读者可自行测试。
@ApiOperation("删除讲师")
@DeleteMapping("/deleteTeacherById/{id}")
public boolean removeTeacher(@ApiParam(name = "id", value = "讲师id", required = true) @PathVariable String id) {
boolean flag= eduTeacherService.removeById(id);
return flag;
}
9.统一返回结果
由于项目中不同的模块、前后端一般都不是同一个人编写的,不同的接口返回的数据类型不一致,导致编程很不方便,我们采用json来作为统一返回数据格式。json类型的数据格式一般是两种:对象、数组,在实际中一般是两种格式混合使用。一般json数据的格式没有固定格式,只要能够描述清楚数据的具体信息与状态,但一般包含状态码、返回消息、数据等,我们将本项目的返回数据格式统一如下。
{
"success": 布尔, //响应是否成功
"code": 数字, //响应码
"message": 字符串, //返回消息
"data": HashMap //返回数据,放在键值对中
}
下面具体来实现统一返回数据。
(1)在common下新建maven子模块common_utils.
(2)如下图所示目录结构新建接口Resultcode。
在接口中存放状态码信息。
package com.wangzhou.commonutils;
public interface ResultCode {
//状态码:成功
public static Integer SUCCESS = 20000;
//状态码:失败
public static Integer ERROR = 20001;
}
在同一路径下,创建统一返回结果的类R。
// lombok的注解,自动生成getter,setter等
@Data
public class R {
// swagger的注解
@ApiModelProperty("是否成功")
private boolean success;
@ApiModelProperty("响应码")
private Integer code;
@ApiModelProperty("返回信息")
private String message;
@ApiModelProperty("返回数据")
private Map<String, Object> data = new HashMap<String, Object>();
//无参构造方法私有,其他类不可以创建该类的实例,只能使用其镜头方法
private R() {
}
public static R ok() {
R returnData = new R();
returnData.setSuccess(true);
returnData.setCode(ResultCode.SUCCESS);
returnData.setMessage("成功");
return returnData;
}
public static R error() {
R returnData = new R();
returnData.setSuccess(false);
returnData.setCode(ResultCode.ERROR);
returnData.setMessage("失败");
return returnData;
}
// 方便链式编程,即编程时可以:R.ok().success()
public R success(Boolean success) {
this.success = success;
return this;
}
public R code(Integer code) {
this.code = code;
return this;
}
public R message(String message) {
this.message = message;
return this;
}
public R data(String key, Object value) {
this.data.put(key, value);
return this;
}
public R data(Map<String, Object> data) {
this.setData(data);
return this;
}
}
下面在模块edu_service中使用统一的返回结果。
(1) 在service的pom文件引入依赖
<dependency>
<groupId>com.wangzhou</groupId>
<artifactId>common_utils</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
(2)将controller中的返回结果替换为R,注意导包R时不要导错,要导入自己项目的R,而不是baomidou的。
@ApiOperation("讲师列表")
@GetMapping("findAll")
public R findAllTeacher() {
List<EduTeacher> list = eduTeacherService.list(null);
return R.ok().data("items", list);
}
@ApiOperation("删除讲师")
@DeleteMapping("/deleteTeacherById/{id}")
public R removeTeacher(@ApiParam(name = "id", value = "讲师id", required = true) @PathVariable String id) {
boolean flag= eduTeacherService.removeById(id);
if(flag) {
return R.ok();
} else {
return R.error();
}
}
启动项目,访问http://localhost:8001/swagger-ui.html。
findAll的reponse body如下。成功了,removeTeacher请读者自测。
在测试removeTeacher时发现一个奇怪的现象:删除已经删除过的数据,并不存在的数据,甚至id格式错误的数据都会返回success。
截取日志信息如下。
Execute SQL:
UPDATE
edu_teacher
SET
is_deleted=1
WHERE
id='p'
AND is_deleted=0
根据源码,发现原来是mybatis plus的bug,此bug在3.3.0已经修复。参考博客https://blog.csdn.net/Evian_Tian/article/details/103919089
10.分页功能
下面实现分页功能。
(1)在EduConfig中配置分页插件
/**
* 分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
(2)在EduTeacherController中编写分页功能
@ApiOperation("分页查询")
@GetMapping("/pageList/{page}/{limit}")
public R pageList(@ApiParam(name = "page", value = "当前页码", required = true) @PathVariable Long page,
@ApiParam(name = "limit", value = "每页记录数", required = true) @PathVariable Long limit) {
Page<EduTeacher> teacherPage = new Page<>(page, limit);
// mp会把结果封装到eduTeacherService中
eduTeacherService.page(teacherPage, null);
Long total = teacherPage.getTotal();
List<EduTeacher> records = teacherPage.getRecords();
Map map = new HashMap<>();
map.put("total", total);
map.put("records", records);
return R.ok().data(map);
}
多加几个数据,请读者自行启动项目进行测试。
11.多条件组合查询
实现如下图功能。
实现步骤如下。
(1)将查询条件传入接口。
一般把条件值封装成为一个对象,然后将封装对象(vo对象)传递到接口中。我们先在entity下建包vo,vo目录下创建TeacherQuery.java用于封装需要传递的数据。
@ApiModel(value = "Teacher查询对象", description = "讲师查询对象封装")
@Data
public class TeacherQuery implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "教师名称,模糊查询")
private String name;
@ApiModelProperty(value = "头衔 1高级讲师 2首席讲师")
private Integer level;
@ApiModelProperty(value = "查询开始时间", example = "2019-01-01 10:10:10")
private String begin;//注意,这里使用的是String类型,前端传过来的数据无需进行类型转换
@ApiModelProperty(value = "查询结束时间", example = "2019-12-01 10:10:10")
private String end;
}
(2)编写多条件查询功能
@PostMapping("pageTeacherCondition/{page}/{limit}")
public R pageTeacherCondition(
@ApiParam(name = "page", value = "当前页码", required = true) @PathVariable Long page,
@ApiParam(name = "limit", value = "每页记录数", required = true) @PathVariable Long limit,
@RequestBody(required = fals) TeacherQuery teacherQuery) {
Page<EduTeacher> teacherPage = new Page<>();
QueryWrapper<EduTeacher> wrapper = new QueryWrapper<>();
String name = teacherQuery.getName();
Integer level = teacherQuery.getLevel();
String begin = teacherQuery.getBegin();
String end = teacherQuery.getEnd();
if(!StringUtils.isEmpty(name)) {
wrapper.like("name", name);
}
//判断是否传入教师头衔
if (!StringUtils.isEmpty(level.toString())){
//构造条件
wrapper.eq("level",level);
}
if (!StringUtils.isEmpty(begin)){
//构造条件,注意这里的参数名称与数据库对应,而不是属性
wrapper.ge("gmt_create",begin);//ge:大于等于
}
if (!StringUtils.isEmpty(begin)){
//构造条件
wrapper.le("gmt_modified",end);//le:小于等于
}
eduTeacherService.page(teacherPage, wrapper);
Long total = teacherPage.getTotal();
List<EduTeacher> records = teacherPage.getRecords();
Map map = new HashMap<>();
map.put("total", total);
map.put("records", records);
return R.ok().data(map);
}
在上面传递参数时使用了@RequestBody ,该注解表示用json传参,将json数据封装到对象中,在实际开发中经常使用这个格式来传参,不过使用该注解需要配合@PostMapping 使用才能得到查询结果,另外需要显示表示参数值可以为空,否则必须传参。另外@ResponseBody 用于返回json数据。
|