- 前端部分,使用主流的前端框架Vue,使用Es6的开发规范,采用模块化的开发模式。
- 后端部分,使用目前流行的SpringBoot+SpringCloud进行微服务架构,使用Feign、Gateway、Hystrix,以及阿里巴巴的Nacos等组件搭建了项目的基础环境
总结一下学习到的(复习的)知识:
创建项目,依赖管理,mybatis plus使用(主键生成,自动填充…),Swagger,统一返回结果对象,分页功能,条件查询,统一异常处理,统一日志…
创建项目:
最外层父项目:
packaging为pom形式
在artifactId后面设置
在父项目中可以设置<properties>
和<dependencyManagement>
控制(依赖)版本问题
中间层项目(子模块):
packaging同样为pom形式,只需要引入依赖(不需要标注版本 -> 版本在最外层已定义)
配置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=123456
#返回json的全局时间格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
## 设置日志级别
#logging.level.root=WARN
##mybatis日志
#mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
项目中使用MP(Mybatis Plus)
- 设计数据库表(要有id, gmt_create, gmt_modified)
- 在test文件夹中创建自定义generator类,例如:
/** * FileName: demo * Author: Haozhancc * Date: 2022/4/20 9:43 */ public class CodeGenerator { @Test public void run() { // 1、创建代码生成器 AutoGenerator mpg = new AutoGenerator(); // 2、全局配置 GlobalConfig gc = new GlobalConfig(); String projectPath = System.getProperty("user.dir"); //最好用绝对路径 gc.setOutputDir("C:\\Users\\52637\\Desktop\\项目\\guli_parent\\service\\service_edu" + "/src/main/java"); gc.setAuthor("haozhancc"); gc.setOpen(false); //生成后是否打开资源管理器 gc.setFileOverride(false); //重新生成时文件是否覆盖 gc.setServiceName("%sService"); //去掉Service接口的首字母I //这一步根据主键的形式设置,如果是int类型就ID_WORKER,String就ID_WORKER_STR gc.setIdType(IdType.ID_WORKER_STR); //主键策略 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("123456"); dsc.setDbType(DbType.MYSQL); mpg.setDataSource(dsc); // 4、包配置 PackageConfig pc = new PackageConfig(); pc.setParent("com.haozhancc"); pc.setModuleName("eduservice"); //模块名 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(); } }
Mybatis Plus主键生成策略
- 主键增长
- UUID
- redis
- mp默认的snowflake算法
自动填充
- 在private Date gmt_created上添加@TableField(fill = FieldFill.INSERT)注解
- 配合自己写一个Handler(别忘了扫描组件)
/** * FileName: MyMetaObjectHandler * Author: Haozhancc * Date: 2022/4/20 14:25 */ @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); } }
删除功能
逻辑删除需要配置插件(数据库也要有对应字段) [注入bean]
配合注解==@TableLogic==
如果路径传入值例如:
@DeleteMapping(“{id}”)
需要在方法上对应使用@PathVariable来获取
接口测试工具 (Swagger)
swagger是一个规范和完整的框架,用于生成,描述,调用和可视化RESTful风格的web服务.
项目中如何整合Swagger
创建公共模块 整合 Swagger(所有模块均可以使用)
- 引入依赖
<!--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>
- 在公共模块中编写SwaggerConfig配置Swagger
/** * FileName: SwaggerConfig * Author: Haozhancc * Date: 2022/4/20 10:27 */ @Configuration //配置类 @EnableSwagger2 //swagger注解 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("标题") .description("描述") .version("1.0") .contact(new Contact("Helen", "http://xxx.com", "XXXXXXXX@qq.com")) .build(); } }
- 在其他需要Swagger的地方引入公共模块依赖
- 在启动类上加入
@ComponentScan(basePackaged = {com.xxx})
注解扫描这个配置类
结束! 访问http://localhost:8001/swagger-ui.html
统一返回值
同样放到通用包
/**
* 统一返回结果
* FileName: R
* Author: Haozhancc
* Date: 2022/4/20 10:55
*/
@Data
public class R {
@ApiModelProperty(value = "是否成功")
private Boolean success;
@ApiModelProperty(value = "返回码")
private Integer code;
@ApiModelProperty(value = "返回消息")
private String message;
@ApiModelProperty(value = "返回数据")
private Map<String, Object> data = new HashMap<String, Object>();
//私有构造方法
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;
}
public R success(Boolean success){
this.setSuccess(success);
return this;
}
public R message(String message){
this.setMessage(message);
return this;
}
public R code(Integer code){
this.setCode(code);
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;
}
}
配合
/**
* FileName: ResultCode
* Author: Haozhancc
* Date: 2022/4/20 10:51
*/
public interface ResultCode {
public static Integer SUCCESS = 20000; //成功
public static Integer ERROR = 20001; //失败
}
接口可以换成枚举类…
调用时 例如return R.ok().data("xxx",list);
分页功能
配置插件:
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
具体代码编写:
//3.分页查询讲师方法
@GetMapping("pageTeacher/{current}/{limit}")
public R pageListTeacher(@PathVariable long current,
@PathVariable long limit) {
//创建page对象
Page<EduTeacher> pageTeacher = new Page<>(current, limit);
//调用方法实现分页
//调用方法的时候,底层封装,把分页所有数据封装到pageTeacher对象里面
teacherService.page(pageTeacher, null);
long total = pageTeacher.getTotal(); //总记录数
List<EduTeacher> records = pageTeacher.getRecords();
return R.ok().data("total", total).data("rows", records);
}
条件查询(分页)
在wrapper中传的是表中的字段 而 自动填充传的是类的属性
//4.条件查询待分页的方法
@PostMapping("pageTeacherCondition/{current}/{limit}")
public R pageTeacherCondition(@PathVariable long current,
@PathVariable long limit,
@RequestBody(required = false) TeacherQuery teacherQuery) {
//创建page对象
Page<EduTeacher> pageTeacher = new Page<>(current, limit);
//构建条件
QueryWrapper<EduTeacher> wrapper = new QueryWrapper<>();
// 多条件组合查询 wrapper.
// 动态SQL
String name = teacherQuery.getName();
Integer level = teacherQuery.getLevel();
String begin = teacherQuery.getBegin();
String end = teacherQuery.getEnd();
if (!StringUtils.isNullOrEmpty(name)) {
//构建条件
wrapper.like("name", name);
}
if (null != level) {
wrapper.eq("level", level);
}
if (!StringUtils.isNullOrEmpty(begin)) {
wrapper.ge("gmt_create", begin);
}
if (!StringUtils.isNullOrEmpty(end)) {
wrapper.le("gmt_create", end);
}
wrapper.orderByDesc("gmt_create");
//调用方法实现条件查询分页
teacherService.page(pageTeacher, wrapper);
long total = pageTeacher.getTotal();
List<EduTeacher> records = pageTeacher.getRecords(); //数据list集合
return R.ok().data("total", total).data("rows",records);
}
使用@RequestBody不能使用@GetMapping,只能使用@PostMapping
如果可以没有:@RequestBody(required = false)
@ResponseBody是返回json数据, @RequestBody是接收json数据
统一异常处理
/**
* FileName: GlobalExceptionHandler
* Author: Haozhancc
* Date: 2022/4/20 15:06
*/
@ControllerAdvice
public class GlobalExceptionHandler {
//全局异常
@ExceptionHandler(Exception.class)
@ResponseBody
public R error(Exception e) {
e.printStackTrace();
return R.error().message("执行了全局异常处理");
}
//特定异常
@ExceptionHandler(ArithmeticException.class)
@ResponseBody
public R error(ArithmeticException e) {
e.printStackTrace();
return R.error().message("执行了ArithmeticException异常处理");
}
//自定义异常
@ExceptionHandler(GuliException.class)
@ResponseBody
public R error(GuliException e) {
e.printStackTrace();
return R.error().code(e.getCode()).message(e.getMsg());
}
}
自定义异常 继承 RuntimeException:
/**
* FileName: GuliException
* Author: Haozhancc
* Date: 2022/4/20 15:26
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class GuliException extends RuntimeException{
private Integer code;
private String msg;
}