项目整合MybatisPlus
添加依赖
Springboot项目添加Web项目启动依赖,MYbatisPlus启动依赖,数据库连接依赖等
<!-- Web项目启动类-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MybatisPlus启动类-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!-- 数据库连接-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- MybatisPlus代码生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
</dependency>
<!-- MybatisPlus代码生成器模板引擎-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
</dependency>
复制代码
添加代码生成器
// 演示例子,执行 main 方法控制台输入模块表名回车自动生成对应项目目录中
public class CodeGenerator {
/**
* <p>
* 读取控制台内容
* </p>
*/
public static String scanner(String tip) {
Scanner scanner = new Scanner(System.in);
StringBuilder help = new StringBuilder();
help.append("请输入" + tip + ":");
System.out.println(help.toString());
if (scanner.hasNext()) {
String ipt = scanner.next();
if (StringUtils.isNotBlank(ipt)) {
return ipt;
}
}
throw new MybatisPlusException("请输入正确的" + tip + "!");
}
public static void main(String[] args) {
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor("YU.TAN");
gc.setOpen(false);
// gc.setSwagger2(true); 实体属性 Swagger2 注解
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/springcloud_demo?serverTimezone=UTC&useUnicode=true&useSSL=false&characterEncoding=utf8");
// dsc.setSchemaName("public");
dsc.setDriverName("com.mysql.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("root");
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
// pc.setModuleName(scanner("admin"));
pc.setParent("com.future.admin");
mpg.setPackageInfo(pc);
// 自定义配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
// to do nothing
}
};
// 如果模板引擎是 freemarker
// String templatePath = "/templates/mapper.xml.ftl";
// 如果模板引擎是 velocity
String templatePath = "/templates/mapper.xml.vm";
// 自定义输出配置
List<FileOutConfig> focList = new ArrayList<>();
// 自定义配置会被优先输出
focList.add(new FileOutConfig(templatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
+ "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
}
});
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
// 配置模板
TemplateConfig templateConfig = new TemplateConfig();
// 配置自定义输出模板
//指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
templateConfig.setXml(null);
mpg.setTemplate(templateConfig);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
// strategy.setSuperEntityClass("你自己的父类实体,没有就不用设置!");
strategy.setEntityLombokModel(true);
strategy.setRestControllerStyle(true);
// 公共父类
// strategy.setSuperControllerClass("你自己的父类控制器,没有就不用设置!");
// 写于父类中的公共字段
strategy.setSuperEntityColumns("id");
strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
strategy.setControllerMappingHyphenStyle(true);
strategy.setTablePrefix(pc.getModuleName() + "_");
mpg.setStrategy(strategy);
// mpg.setTemplateEngine(new FreemarkerTemplateEngine());
mpg.setTemplateEngine(new VelocityTemplateEngine());
mpg.execute();
}
}
复制代码
配置文件添加数据库链接
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/springcloud_demo?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=true
username: root
password: root
application:
name: future-admin
复制代码
项目整合SWagger
添加Swagger依赖
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
</dependency>
复制代码
添加Swagger配置
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.pathMapping("/")
.select()
.apis(RequestHandlerSelectors.withClassAnnotation(RestController.class))
.paths(PathSelectors.any())
.build().apiInfo(new ApiInfoBuilder()
.title("FUTURE-admin-基础用户信息")
.description("FUTURE-基础模块,详细信息")
.version("1.0")
.contact(new Contact("邮件", "http://www.baidu.com", "aaaa.email.com"))
.license("The Apache License")
.licenseUrl("http://www.baidu.com")
.build());
}
}
复制代码
效果图
项目同一返回值
该方法我们提供两种方法进行处理。
一种是自定义返回类型,在每一个Controller中的方法都对结果进行该类型的包装处理,即在方法使用时对方法返回值进行手动包装。
另一种是利用拦截器的方式,对所用Controller中的方法进行拦截,并对其返回值进行统一格式化处理。
两种方法比较来看,第一种可以定义更加具体的返回类型和返回码;但是第二种对代码的侵入性更低,降低了代码的耦合性,对于代码的格式重构和修改更加方便。
不论是手动封装返回值还是拦截器实现,我们首先都需要先定义返回格式以及返回状态码。
返回体格式
- @JsonIgnoreProperties(ignoreUnknown = true):这个注解写在类上之后,就会忽略类中不存在的字段。这个注解还可以指定要忽略的字段,例如@JsonIgnoreProperties({ “password”, “secretKey” })
- @sonInclude(JsonInclude.Include.NON_NULL):在实体类序列化成json的时候在某些策略下,加了该注解的字段不去序列化该字段。
@Data
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
@ApiModel(value = "基础API返回对象")
@JsonInclude(JsonInclude.Include.NON_NULL)
@AllArgsConstructor
public class BaseFxResponse<T> {
@ApiModelProperty(value = "处理结果code", required = true)
private int code = ResultCode.SUCCESS.getCode();
@ApiModelProperty(value = "处理结果描述信息")
private String msg = ResultCode.SUCCESS.getMessage();
@ApiModelProperty(value = "请求结果生成时间戳")
private String timestamp;
@ApiModelProperty(value = "处理结果数据信息")
private T data;
public BaseFxResponse(int code, String msg) {
this.code = code;
this.msg = msg;
}
/**
* 1、内部使用,用于构造成功的结果
* 2、对接外部系统错误码及消息
*
* @param code
* @param msg
* @param data
*/
public BaseFxResponse(int code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
/**
* 快速创建成功结果并返回结果数据
*
* @param data
* @return ApiResult
*/
public static <T> BaseFxResponse<T> success(T data) {
return new BaseFxResponse<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data);
}
/**
* 快速创建成功结果
*
* @return ApiResult
*/
public static <T> BaseFxResponse<T> success() {
return success(null);
}
/**
* 系统异常类没有返回数据
*
* @return BaseFxResponse
*/
public static <T> BaseFxResponse<T> fail() {
return new BaseFxResponse(ResultCode.EXCEPTION.getCode(), ResultCode.EXCEPTION.getMessage(), null);
}
/**
* 快速创建失败结果并返回结果数据
*
* @return BaseFxResponse
*/
public static <T> BaseFxResponse<T> fail(T data) {
return new BaseFxResponse(ResultCode.FAIL.getCode(), ResultCode.FAIL.getMessage(), data);
}
/**
* 成功code=000000
*
* @return true/false
*/
@JsonIgnore
public static boolean isSuccess(int code) {
return ResultCode.SUCCESS.getCode() == code;
}
/**
* 快速创建成功结果并返回结果数据
*
* @param msg
* @return ApiResult
*/
public static <T> BaseFxResponse<T> failMsg(String msg) {
return new BaseFxResponse<>(ResultCode.FAIL.getCode(), msg, null);
}
public static <T> BaseFxResponse<T> fail(ResultCode resultCode) {
return new BaseFxResponse(resultCode.getCode(), resultCode.getMessage(), null);
}
public static BaseFxResponse<Object> result(int code, String msg) {
return new BaseFxResponse<>(code, msg, null);
}
}
复制代码
定义返回状态码的枚举
@AllArgsConstructor
@Getter
public enum ResultCode {
/**
*
*/
//成功状态码
SUCCESS(200,"成功"),
FAIL(201,"失败"),
EXCEPTION(400,"系统异常"),
EXCEPTION_NO_PERMISSION(401,"权限不足,无法访问"),
EXCEPTION_NO_PARAMETER(402,"方法参数异常");
private final int code;
private final String message;
}
复制代码
手动封装返回值
该方式使用时所有方法都返回BaseFxResponse类型的数据,将具体的返回结果赋值给属性data,可以对具体场景使用不同的状态码,例如:
@ApiOperation("模拟返回成功")
@RequestMapping(value = "/test/result/success",method = RequestMethod.GET)
public BaseFxResponse<String> testResultSuccess() {
return BaseFxResponse.success("Hello,World!");
}
@ApiOperation("模拟返回失败")
@RequestMapping(value = "/test/result/fail",method = RequestMethod.GET)
public BaseFxResponse<String> testResultFail() {
return BaseFxResponse.fail("Sorry,Fail!");
}
@ApiOperation("模拟返回异常")
@RequestMapping(value = "/test/result/exception",method = RequestMethod.GET)
public BaseFxResponse<String> testResultException() {
return BaseFxResponse.fail(ResultCode.EXCEPTION);
}
复制代码
拦截器封装返回值
定义执行器ResultResponseHandler
在该执行器上方添加@ControllerAdvice("com.response.controller")注解,标记该在哪些类的方法放格式化返回值。并且要实现ResponseBodyAdvice接口,实现接口中的supports()方法和beforeBodyWrite()方法。
supports():该方法作用是定义哪些方法需要格式化返回值。
beforeBodyWrite():对返回值进行具体的格式化操作。
@Slf4j
@ControllerAdvice("com.response.controller")
public class ResultResponseHandler implements ResponseBodyAdvice<Object> {
public static final String RESPONSE_RESULT_ANN = "RESPONSE-RESULT-ANN";
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
return true;
}
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
log.info("进入返回体,格式重写中");
if (o instanceof Result){
return o;
}
if (o instanceof String) {
return JSON.toJSONString(Result.success(o));
}
return Result.success(o);
}
}
复制代码
也可以对具体哪些方法需要封装进行更细致的划分,例如在前面的基础上只对某些的方法进行封装。
首先定义一个自定义注解,用于标记方法是否需要格式化返回值。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
@Documented
public @interface ResponseResult {
}
复制代码
定义一个拦截器,可以拦截具体的请求,判断该请求是否需要格式化。
我们在该方法或者该类上添加该注解,拦截到方法后,利用反射,判断该类或者该方法是否具有@ResponseResult注解,如果存在该注解,则在请求中添加一个标记。然后方法执行完成后,在ResultResponseHandler类中的supports()方法中判断该方法的请求是否含有该标记,如果存在标记,则返回true,对返回值进行封装,不存在则返回false。具体实现如下:
Slf4j
@Component
public class ResponseResultInterceptor implements HandlerInterceptor {
//标记名称
public static final String RESPONSE_RESULT_ANN="RESPONSE-RESULT-ANN";
/**
* 拦截请求,是否此请求返回的值需要包装,其实就是运行的时候,解析@ResponseResult注解
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod){
final HandlerMethod handlerMethod=(HandlerMethod) handler;
final Class<?> clazz = handlerMethod.getBeanType();
final Method method = handlerMethod.getMethod();
if (clazz.isAnnotationPresent(ResponseResult.class)){
request.setAttribute(RESPONSE_RESULT_ANN,clazz.getAnnotation(ResponseResult.class));
}else if (method.isAnnotationPresent(ResponseResult.class)){
request.setAttribute(RESPONSE_RESULT_ANN,clazz.getAnnotation(ResponseResult.class));
}
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
复制代码
继承WebMvcConfigurerAdapter并对该拦截器进行注册。
项目公共模块定义全局异常
构建一个自定义异常类,在使用时直接抛出该异常类
public class BusinessException extends RuntimeException{
/**
* 自定义异常Code
*/
private int exceptionCode;
/**
* 自定义异常内容
*/
private String exception;
public BusinessException(){ }
public BusinessException(String exception){
this.exceptionCode= ResultCode.EXCEPTION.getCode();
this.exception=exception;
}
public BusinessException(int exceptionCode,String exception){
this.exceptionCode=exceptionCode;
this.exception=exception;
}
public int getExceptionCode() {
return exceptionCode;
}
public void setExceptionCode(int exceptionCode) {
this.exceptionCode = exceptionCode;
}
public String getException() {
return exception;
}
public void setException(String exception) {
this.exception = exception;
}
}
复制代码
定义一个全局异常,该类可以捕捉系统中所有抛出的异常信息,并对异常信息进行相应的格式化和持久化处理。
- 在类上添加@RestControllerAdvice注解
- 在方法上添加拦截器注解,该注解可定义拦截哪些异常@ExceptionHandler(Exception.class)
我们统一拦截Exception类型的异常,及所有异常信息,在方法中再对异常信息类型进行判断和识别,并对具体类型进行我们需要的格式化及持久化处理。
@RestControllerAdvice
@Slf4j
@Order(1)
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
@ResponseBody
public BaseFxResponse<Object> resolveException(Exception ex) {
log.error("系统异常", ex);
//判断异常类型
if (ex instanceof BusinessException) {
BusinessException exception = (BusinessException) ex;
return BaseFxResponse.result(exception.getExceptionCode(), exception.getException());
}
if (ex instanceof MethodArgumentNotValidException) {
return BaseFxResponse.fail(ResultCode.EXCEPTION_NO_PARAMETER);
}
return BaseFxResponse.fail(ResultCode.EXCEPTION.getCode());
}
}
复制代码
近期评论