1, Instructor query function
1. Write query controller code content
@RestController @RequestMapping("/eduservice/teacher") public class EduTeacherController { @Autowired private EduTeacherService eduTeacherService; // Instructor query all data list // Use restful style @GetMapping("findAll") public List<EduTeacher> findAll(){ List<EduTeacher> list = eduTeacherService.list(null); return list; } }
2. Create configuration classes, configure mapper scanning and other
@Configuration @MapperScan("com.kuang.eduservice.mapper") public class EduConfig { }
3. Configure SQL execution performance analysis plug-in
/** * SQL Execute performance analysis plug-in * It is not recommended to use the development environment online. maxTime refers to the maximum execution time of sql */ @Bean @Profile({"dev","test"})// Set dev test environment on public PerformanceInterceptor performanceInterceptor() { PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor(); performanceInterceptor.setMaxTime(1000);//ms, beyond the ms set here, sql will not be executed performanceInterceptor.setFormat(true); return performanceInterceptor; }
4. Create main startup class
@SpringBootApplication public class EduApplication { public static void main(String[] args) { SpringApplication.run(EduApplication.class, args); } }
5. Test start
visit: http://localhost:8001/eduservice/teacher/findAll
6. Unify the returned json time format
By default, json time format has time zone and is world standard time, which is eight hours away from our time
In application Set in YML:
spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8
2, Instructor logical deletion function
1. Configure logical deletion plug-in
//Logical delete component @Bean public ISqlInjector sqlInjector(){ return new LogicSqlInjector(); }
2. Add a comment on the logical deletion attribute
@TableLogic private Boolean isDeleted;
3. Write Controller deletion method
//Logical deletion method @DeleteMapping("removeById/{id}") public boolean removeById(@PathVariable String id){ boolean flag = eduTeacherService.removeById(id); return flag; }
4. How to test
Test with some tools
- swagger testing (emphasis): generate online interface documents to facilitate interface testing.
- postman (understand)
3, Configure Swagger2
1. Create a common module - in Guli_ Create common under Parent
2. Introduce related dependencies
<dependencies> <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 Used to simplify entity classes: need to install lombok plug-in unit--> <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 integrate redis what is needed common-pool2 <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.6.0</version> </dependency>--> </dependencies>
3. Create configuration class for swagger
Create a sub module service under common_ Base, and create the configuration class of swagger in this module
@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("website-Course Center API file") .description("This document describes the definition of course center microservice interface") .version("1.0") .contact(new Contact("lxy", "www.baidu.com", "2422737092@qq.com")) .build(); } }
4. Specific use of Swagger2
- In service_edu import service_base dependency
- Finally, in the service_ Add annotation on edu startup class and set package scanning rules
- Access test: http://localhost:8001/swagger-ui.html
5. Define interface description and parameter description
- Defined on class: @ Api
- Defined on method: @ ApiOperation
- Defined on parameters: @ ApiParam
@Api(description = "Instructor management") @RestController @RequestMapping("/eduservice/teacher") public class EduTeacherController { @Autowired private EduTeacherService eduTeacherService; // Query all data in instructor list // Use restful style @ApiOperation(value = "List of all instructors") @GetMapping("findAll") public List<EduTeacher> findAll(){ List<EduTeacher> list = eduTeacherService.list(null); return list; } //Logical deletion method @ApiOperation(value = "Delete instructor logically") @DeleteMapping("{id}") public boolean removeById( @ApiParam(name = "id", value = "lecturer ID",required = true) @PathVariable String id){ boolean flag = eduTeacherService.removeById(id); return flag; } }
4, Unified return result object
1. Unified return data format
In the project, we will encapsulate the response into json return. Generally, we will unify the data format of all interfaces to make the data operation of the front end (IOS, Android, web) more consistent and easy.
Generally, the unified return data format has no fixed format, as long as it can clearly describe the returned data status and the specific data to be returned. However, it usually includes status code, return message and data
For example, the basic data format required by our system is as follows:
- Data carried successfully
{ "success": true, "code": 20000, "message": "success", "data": { "items": [ { "id": "1", "name": "Lau Andy", "intro": "Graduated from the Mathematics Department of Normal University, he loves education and has taught mathematical thinking for more than 6 years" } ] } }
- Data carried successfully (paging)
{ "success": true, "code": 20000, "message": "success", "data": { "total": 17, "rows": [ { "id": "1", "name": "Lau Andy", "intro": "Graduated from the Mathematics Department of Normal University, he loves education and has taught mathematical thinking for more than 6 years" } ] } }
- No data returned successfully
{ "success": true, "code": 20000, "message": "success", "data": {} }
- No return data failed
{ "success": false, "code": 20001, "message": "fail", "data": {} }
- Therefore, we unify the results
{ "success": Boolean, //Is the response successful "code": number, //Response code "message": character string, //Return message "data": HashMap //Return data in key value pairs }
2. Unified result return class
-
Create sub module common under common module_ utils
-
Create interface definition return code - create package com rg. Commonutils, create interface ResultCode
public interface ResultCode { public static Integer SUCCESS = 20000;//success public static Integer ERROR = 20001;//fail }
- Create result class - create R class
@Data public class R { @ApiModelProperty(value = "Is it successful") private Boolean success; @ApiModelProperty(value = "Return code") private Integer code; @ApiModelProperty(value = "Return message") private String message; @ApiModelProperty(value = "Return data") private Map <String, Object> data = new HashMap <String, Object>(); private R(){} //Privatization of construction methods Purpose: it is forbidden to use the new method arbitrarily, and only the method we define can be used public static R ok(){//Define success method R r = new R(); r.setSuccess(true); r.setCode(ResultCode.SUCCESS); r.setMessage("success"); return r;//For chain programming } public static R error(){//Define failed method R r = new R(); r.setSuccess(false); r.setCode(ResultCode.ERROR); r.setMessage("fail"); return r;//For chain programming } //Related methods of custom attributes (with chain programming function) 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; } }
3. Unified use of returned results
-
Add dependency - common in the service module_ utils
-
Modify the returned results in the Controller
//1. Query all data of lecturer and use Rest style @ApiOperation("List of all instructors") @GetMapping("findAll") public List <EduTeacher> findAll() { List <EduTeacher> eduTeacherList = eduTeacherService.list(null); return eduTeacherList; } //2. Delete instructor logically through id @ApiOperation("according to ID Delete instructor") @DeleteMapping("removeById/{id}") public R removeById(@ApiParam(name = "id", value = "lecturer ID", required = true) @PathVariable String id) { boolean flag = eduTeacherService.removeById(id); if (flag) { return R.ok(); } else { return R.error(); } }
5, Instructor paging query
1. Configuring paging plug-in in EduConfig
/** * Paging plug-in */ @Bean public PaginationInterceptor paginationInterceptor(){ return new PaginationInterceptor(); }
2. Add paging method in EduTeacherController
//3. Query lecturers by page @ApiOperation("Paging query instructor") @GetMapping("pageList/{current}/{limit}") public R pageList(@ApiParam(name = "current", value = "Current page number", required = true) @PathVariable("current") Integer current, @ApiParam(name = "limit", value = "Records per page", required = true) @PathVariable("limit") Integer limit) { Page <EduTeacher> page = new Page <>(current, limit); //Paging query eduTeacherService.page(page, null);//Finally, these attributes can be obtained through page or iPage, because the paged data will be re encapsulated into these objects // Map <String, Object> map = new HashMap <>(); // map.put("total",page.getTotal()); // map.put("rows", page.getRecords()); // return R.ok().data(map); return R.ok().data("total", page.getTotal()).data("rows", page.getRecords()); }
6, Instructor condition query with pagination
Requirements: query according to lecturer name, lecturer Title level and lecturer residence time
1. Create query object
Create com rg. eduservice. entiy. VO package, create TeacherQuery class query object
@ApiModel(value = "Teacher Query object",description = "Instructor query object encapsulation") @Data public class TeacherQuery implements Serializable { private static final long serialVersionUID = 1L; @ApiModelProperty(value = "Teacher name,Fuzzy query") private String name; @ApiModelProperty(value = "Title 1 senior lecturer 2 chief lecturer") private Integer level; @ApiModelProperty(value = "Query start time", example = "2019-01-01 10:10:10") private String begin; @ApiModelProperty(value = "Query end time", example = "2019-12-01 10:10:10") private String end; }
2. Write controller
//4. Query lecturers according to conditions @ApiOperation("Query lecturers by criteria") @PostMapping("pageQuery/{current}/{limit}") //@RequestBody: pass data through json and encapsulate the data into objects json data can only be encapsulated in the request body, so POST requests are needed public R pageQuery(@ApiParam(name = "current", value = "Current page number", required = true) @PathVariable("current") Integer current, @ApiParam(name = "limit", value = "Records per page", required = true) @PathVariable("limit") Integer limit, @ApiParam(name = "teacherQuery",value = "query criteria",required = false) @RequestBody(required = false) TeacherQuery teacherQuery){//required = false, indicating that the condition is optional Page <EduTeacher> page = new Page <>(current, limit); //Construction conditions QueryWrapper <EduTeacher> wrapper = new QueryWrapper <>(); String name = teacherQuery.getName(); Integer level = teacherQuery.getLevel(); String begin = teacherQuery.getBegin(); String end = teacherQuery.getEnd(); //We have learned dynamic sql in mybatis. Here we can use MP to simplify operations if(!StringUtils.isEmpty(name)){ wrapper.like("name", name); } if(!StringUtils.isEmpty(level)){ wrapper.eq("level", level); } if(!StringUtils.isEmpty(begin)){ wrapper.ge("gmt_create", begin); } if(!StringUtils.isEmpty(end)){ wrapper.le("gmt_modified", end); } eduTeacherService.page(page, wrapper);//Paging query return R.ok().data("total", page.getTotal()).data("rows", page.getRecords()); }
7, Auto fill package
1. Create autofill class in service base
@Component public class MyMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { //The property name is set here, not the field name this.setFieldValByName("gmtCreate", new Date(), metaObject); this.setFieldValByName("gmtModified", new Date(), metaObject); } @Override public void updateFill(MetaObject metaObject) { this.setFieldValByName("gmtModified", new Date(), metaObject); } }
2. Add auto fill annotation to entity class
8, Instructor add function
1. Add auto fill annotation to entity class
2. New method in eduteachercontroller
//5. Add lecturer @ApiOperation("Add lecturer") @PostMapping("addTeacher") public R addTeacher(@ApiParam(value = "eduTeacher",name = "Instructor information") @RequestBody EduTeacher eduTeacher){ boolean flag = eduTeacherService.save(eduTeacher); if (flag){ return R.ok(); }else{ return R.error(); } }
3. Test in swagger
9, Instructor modification function
1. New method in eduteachercontroller
//6. Query according to instructor Id -- > Data echo function @GetMapping("getById/{id}") @ApiOperation("according to id Query lecturer") public R getById(@ApiParam(name = "id",value = "lecturer ID",required = true) @PathVariable String id){ EduTeacher eduTeacher = eduTeacherService.getById(id); return R.ok().data("teacher", eduTeacher); } //7. Instructor modification function @ApiOperation("Modify instructor") @PutMapping("updateById") public R updateById(@ApiParam(name = "eduTeacher",value = "Instructor information",required = true) @RequestBody EduTeacher eduTeacher){ boolean flag = eduTeacherService.updateById(eduTeacher); if(flag){ return R.ok(); }else{ return R.error(); } }
10, Unified exception handling
1. Manufacturing abnormality
int a = 10/0;
2. Unified exception handling
- Create a unified exception handling class globalexceptionhandler in service base java:
@ControllerAdvice public class GlobalExceptionHandler { //Unified exception handling @ExceptionHandler(Exception.class) @ResponseBody public R error(Exception e){ e.printStackTrace(); return R.error(); } }
- Swagger test
3. Handling specific exceptions
- Added in GlobalExceptionHandler
//Handling specific exceptions @ExceptionHandler(ArithmeticException.class) @ResponseBody public R error(ArithmeticException e){ e.printStackTrace(); return R.error().message("Custom exception executed"); }
- Swagger
4. Custom exception
- Create custom exception class
@Data @AllArgsConstructor @NoArgsConstructor public class GuLiException extends RuntimeException{ private Integer code;//Status code private String msg;//Abnormal information }
- Add exception handling method
@ExceptionHandler(GuliException.class) @ResponseBody public R error(GuliException e){ e.printStackTrace(); return R.error().message(e.getMsg()).code(e.getCode()); }
- GuliException is thrown at the required position in the business
try { int i = 10 / 0; } catch (Exception e) { throw new GuLiException(200001, "Custom exception occurred"); }
- Swagger test
11, Unified log processing
1. Configure log level
The behavior of the Logger is hierarchical. As shown in the following table:
It is divided into: OFF, FATAL, ERROR, WARN, INFO, DEBUG and ALL
By default, the log level printed by spring boot from the console is only INFO or above. You can configure the log level
# Set log level logging.level.root=WARN
2. Logback log
spring boot uses Logback internally as the framework for log implementation.
Logback is very similar to log4j. If you are familiar with log4j, you will be handy with logback soon.
Some advantages of logback over log4j: https://blog.csdn.net/caisini_vc/article/details/48551287
1. Configure logback log
Delete application Log configuration in properties
# mybatis log #mybatis-plus: # configuration: # log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #logging: # level: debug
Install idea color log plug-in: grep console
Create logback spring in resources xml
<?xml version="1.0" encoding="UTF-8"?> <configuration scan="true" scanPeriod="10 seconds"> <!-- The log level is from low to high TRACE < DEBUG < INFO < WARN < ERROR < FATAL,If set to WARN,Lower than WARN No information will be output --> <!-- scan:When this property is set to true If the configuration file changes, it will be reloaded. The default value is true --> <!-- scanPeriod:Set the time interval for monitoring whether the configuration file is modified. If no time unit is given, the default unit is milliseconds. When scan by true This property takes effect when. The default interval is 1 minute. --> <!-- debug:When this property is set to true When, it will be printed out logback Internal log information, real-time viewing logback Operation status. The default value is false. --> <contextName>logback</contextName> <!-- name The value of is the name of the variable, value The value defined by the variable. Values defined by are inserted into the logger In context. After the variable is defined, you can“ ${}"To use variables. --> <property name="log.path" value="D:/guli_log/edu" /> <!-- Color log --> <!-- Configure format variables: CONSOLE_LOG_PATTERN Color log format --> <!-- magenta:Magenta --> <!-- boldMagenta:Coarse red--> <!-- cyan:Cyan --> <!-- white:white --> <!-- magenta:Magenta --> <property name="CONSOLE_LOG_PATTERN" value="%yellow(%date{yyyy-MM-dd HH:mm:ss}) |%highlight(%-5level) |%blue(%thread) |%blue(%file:%line) |%green(%logger) |%cyan(%msg%n)"/> <!--Output to console--> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <!--This log appender It is used for development. Only the lowest level is configured. The log level output from the console is log information greater than or equal to this level--> <!-- For example, if you configure INFO Level, even if other locations are configured DEBUG Level logs will not be output --> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>INFO</level> </filter> <encoder> <Pattern>${CONSOLE_LOG_PATTERN}</Pattern> <!-- Set character set --> <charset>UTF-8</charset> </encoder> </appender> <!--output to a file--> <!-- Time scrolling output level by INFO journal --> <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- The path and file name of the log file being recorded --> <file>${log.path}/log_info.log</file> <!--Log file output format--> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> <charset>UTF-8</charset> </encoder> <!-- The rolling strategy of the logger is to record by date and by size --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- Daily log archive path and format --> <fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <!--Log file retention days--> <maxHistory>15</maxHistory> </rollingPolicy> <!-- This log file only records info Rank --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>INFO</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender> <!-- Time scrolling output level by WARN journal --> <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- The path and file name of the log file being recorded --> <file>${log.path}/log_warn.log</file> <!--Log file output format--> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> <charset>UTF-8</charset> <!-- Character set here --> </encoder> <!-- The rolling strategy of the logger is to record by date and by size --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <!--Log file retention days--> <maxHistory>15</maxHistory> </rollingPolicy> <!-- This log file only records warn Rank --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>warn</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender> <!-- Time scrolling output level by ERROR journal --> <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- The path and file name of the log file being recorded --> <file>${log.path}/log_error.log</file> <!--Log file output format--> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> <charset>UTF-8</charset> <!-- Set character set here --> </encoder> <!-- The rolling strategy of the logger is to record by date and by size --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <!--Log file retention days--> <maxHistory>15</maxHistory> </rollingPolicy> <!-- This log file only records ERROR Rank --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>ERROR</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender> <!-- <logger>Used to set the log printing level of a package or a specific class, and specify<appender>. <logger>Only one name Properties, An optional level And an optional addtivity Properties. name:Used to specify that this logger A package or a specific class of constraints. level:Used to set the printing level, regardless of case: TRACE, DEBUG, INFO, WARN, ERROR, ALL and OFF, If this property is not set, the current logger Will inherit the level of the superior. --> <!-- use mybatis When I was young, sql Statement is debug It will not be printed until next, and here we only configure it info,So I want to see sql Statement, there are two operations: First handle<root level="INFO">Change to<root level="DEBUG">This will print sql,But there will be a lot of other messages in the log The second is to give it alone mapper Directory configuration under DEBUG Mode, the code is as follows, which is configured in this way sql The statement will print, and others are normal DEBUG Level: --> <!--development environment :Print Console --> <springProfile name="dev"> <!--You can output data in a project debug Logs, including mybatis of sql journal--> <logger name="com.guli" level="INFO" /> <!-- root Node is a required node. It is used to specify the most basic log output level. There is only one node level attribute level:Used to set the printing level, regardless of case: TRACE, DEBUG, INFO, WARN, ERROR, ALL and OFF,Default is DEBUG Can contain zero or more appender Element. --> <root level="INFO"> <appender-ref ref="CONSOLE" /> <appender-ref ref="INFO_FILE" /> <appender-ref ref="WARN_FILE" /> <appender-ref ref="ERROR_FILE" /> </root> </springProfile> <!--production environment :output to a file--> <springProfile name="pro"> <root level="INFO"> <appender-ref ref="CONSOLE" /> <appender-ref ref="DEBUG_FILE" /> <appender-ref ref="INFO_FILE" /> <appender-ref ref="ERROR_FILE" /> <appender-ref ref="WARN_FILE" /> </root> </springProfile> </configuration>
2. Output the error log to a file
GlobalExceptionHandler. Add annotations to Java classes
@Slf4j
Add exception output statement in exception handling class:
3. Output log stack information to file
The above operations can only output the first line of error information to the file. If all information is required, the following operations are required
- Define tool classes
Create util package under Guli framework common and create exceptionutil Java tool class
public class ExceptionUtil { public static String getMessage(Exception e) { StringWriter sw = null; PrintWriter pw = null; try { sw = new StringWriter(); pw = new PrintWriter(sw); // Output the wrong stack information to printWriter e.printStackTrace(pw); pw.flush(); sw.flush(); } finally { if (sw != null) { try { sw.close(); } catch (IOException e1) { e1.printStackTrace(); } } if (pw != null) { pw.close(); } } return sw.toString(); } }
- call
log.error(ExceptionUtil.getMessage(e));
- Add toString method to GuliException
ingProfile>
```2. Output the error log to a file
GlobalExceptionHandler. Add annotations to Java classes
@Slf4j
Add exception output statement in exception handling class:
3. Output log stack information to file
The above operations can only output the first line of error information to the file. If all information is required, the following operations are required
- Define tool classes
Create util package under Guli framework common and create exceptionutil Java tool class
public class ExceptionUtil { public static String getMessage(Exception e) { StringWriter sw = null; PrintWriter pw = null; try { sw = new StringWriter(); pw = new PrintWriter(sw); // Output the wrong stack information to printWriter e.printStackTrace(pw); pw.flush(); sw.flush(); } finally { if (sw != null) { try { sw.close(); } catch (IOException e1) { e1.printStackTrace(); } } if (pw != null) { pw.close(); } } return sw.toString(); } }
- call
log.error(ExceptionUtil.getMessage(e));
- Add toString method to GuliException