项目开发准备工作:(参考log项目:D:\shen\java\webdevelop\spring\log)
start.spring.io
导入相关依赖
定义数据库schema、表结构
flyway映射
<properties>
<mybatis-plus-boot-starter.version>3.3.1</mybatis-plus-boot-starter.version>
<java.version>1.8</java.version>
<!-- flyway mvn cmd配置-->
<flyway.url>jdbc:mysql://10.1.252.23:3306/springisp?serverTimezone=GMT%2B8&useSSL=false&characterEncoding=utf8</flyway.url>
<flyway.user>root</flyway.user>
<flyway.password>%tBqnWF1y3ku</flyway.password>
<flyway.locations>filesystem:src/main/resources/db/migration</flyway.locations>
<!-- <flyway.schemas>schemaName</flyway.schemas>-->
</properties>
d:\shen\java\webdevelop\spring\ispManager
λ mvn flyway:migrate
[INFO] Scanning for projects...
[INFO]
[INFO] ----------------------< ik.starriver:ispManager >-----------------------
[INFO] Building ispManager 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- flyway-maven-plugin:6.2.3:migrate (default-cli) @ ispManager ---
[INFO] Flyway Community Edition 6.2.3 by Redgate
[INFO] Database: jdbc:mysql://10.1.252.23:3306/springisp (MySQL 5.7)
[INFO] Successfully validated 1 migration (execution time 00:00.053s)
[INFO] Current version of schema `springisp`: << Empty Schema >>
[INFO] Migrating schema `springisp` to version 1 - init tables
[WARNING] DB: Unknown table 'springisp.tbl_change_history_manager' (SQL State: 42S02 - Error Code: 1051)
[WARNING] DB: Unknown table 'springisp.tbl_contract_file_manager' (SQL State: 42S02 - Error Code: 1051)
[WARNING] DB: Unknown table 'springisp.tbl_contract_manager' (SQL State: 42S02 - Error Code: 1051)
[WARNING] DB: Unknown table 'springisp.tbl_line_manager' (SQL State: 42S02 - Error Code: 1051)
[INFO] Successfully applied 1 migration to schema `springisp` (execution time 00:00.436s)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.679 s
[INFO] Finished at: 2020-03-02T16:49:33+08:00
[INFO] ------------------------------------------------------------------------
d:\shen\java\webdevelop\spring\ispManager
λ
mybatis 代码生成器生成
<dependencies>
<!-- MBP code Generator start -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
<version>1.7</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.3.1</version>
</dependency>
<!-- MBP code Generator end -->
D:\shen\java\webdevelop\spring\ispManager\src\test\java\MPGenerator.java
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* <p>
* 代码生成器演示
* </p>
*/
public class MPGenerator {
final static String dirPath = "D:\\shen\\java\\webdevelop\\spring\\ispmanager\\src\\main\\java\\";
final static String mapperXmlPath = "D:\\shen\\java\\webdevelop\\spring\\ispmanager\\src\\main\\resources\\mappers\\";
/**
* <p>
* MySQL 生成演示
* </p>
*/
public static void main(String[] args) {
AutoGenerator mpg = new AutoGenerator();
// 选择 freemarker 引擎,默认 Velocity
// mpg.setTemplateEngine(new FreemarkerTemplateEngine());
// 全局配置
GlobalConfig gc = new GlobalConfig();
gc.setOutputDir(dirPath);
gc.setAuthor("shenxianjie");
gc.setFileOverride(true); //是否覆盖
// gc.setActiveRecord(true);// 不需要ActiveRecord特性的请改为false
gc.setEnableCache(false);// XML 二级缓存
gc.setBaseResultMap(true);// XML ResultMap
gc.setBaseColumnList(true);// XML columList
// 自定义文件命名,注意 %s 会自动填充表实体属性!
// gc.setMapperName("%sDao");
// gc.setXmlName("%sMapper");
// gc.setServiceName("MP%sService");
// gc.setServiceImplName("%sServiceDiy");
// gc.setControllerName("%sAction");
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setDbType(DbType.MYSQL);
dsc.setTypeConvert(new MySqlTypeConvert(){
// 自定义数据库表字段类型转换【可选】
@Override
public DbColumnType processTypeConvert(GlobalConfig globalConfig,String fieldType) {
System.out.println("转换类型:" + fieldType);
// 注意!!processTypeConvert 存在默认类型转换,如果不是你要的效果请自定义返回、非如下直接返回。
return (DbColumnType) super.processTypeConvert(globalConfig, fieldType);
}
});
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("%tBqnWF1y3ku");
dsc.setUrl("jdbc:mysql://10.1.252.23:3306/springisp?serverTimezone=GMT%2B8&useSSL=false&characterEncoding=utf8");
mpg.setDataSource(dsc);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
// strategy.setCapitalMode(true);// 全局大写命名 ORACLE 注意
// strategy.setTablePrefix(new String[] { "tianshu_log" });// 此处可以修改为您的表前缀
strategy.setNaming(NamingStrategy.underline_to_camel);// 表名生成策略
strategy.setInclude(new String[] {
"tbl_change_history_manager"
, "tbl_contract_file_manager"
, "tbl_contract_manager"
, "tbl_line_manager"
}); // 需要生成的表
// strategy.setExclude(new String[]{"test"}); // 排除生成的表
// 自定义实体父类
// strategy.setSuperEntityClass("ik.starriver.log.SuperEntity");
// 自定义实体,公共字段
// strategy.setSuperEntityColumns(new String[] { "id"});
// 自定义 mapper 父类
// strategy.setSuperMapperClass("com.baomidou.demo.TestMapper");
// 自定义 service 父类
// strategy.setSuperServiceClass("com.baomidou.demo.TestService");
// 自定义 service 实现类父类
// strategy.setSuperServiceImplClass("com.baomidou.demo.TestServiceImpl");
// 自定义 controller 父类
// strategy.setSuperControllerClass("com.baomidou.demo.TestController");
// 【实体】是否生成字段常量(默认 false)
// public static final String ID = "test_id";
// strategy.setEntityColumnConstant(true);
// 【实体】是否为构建者模型(默认 false)
// public User setName(String name) {this.name = name; return this;}
strategy.setEntityBuilderModel(true);
mpg.setStrategy(strategy);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setParent("ik");
pc.setModuleName("starriver.ispmanager");
pc.setController("controller");
pc.setEntity("entity");
pc.setMapper("mapper");
pc.setService("service");
pc.setServiceImpl("serviceImpl");
//pc.setXml("mapperXml");
mpg.setPackageInfo(pc);
// 注入自定义配置,可以在 VM 中使用 cfg.abc 【可无】
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
Map<String, Object> map = new HashMap();
map.put("abc", this.getConfig().getGlobalConfig().getAuthor() + "-mp");
this.setMap(map);
}
};
// 自定义 xxList.jsp 生成
List<FileOutConfig> focList = new ArrayList();
/* focList.add(new FileOutConfig("/template/list.jsp.vm") {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输入文件名称
return "D://my_" + tableInfo.getEntityName() + ".jsp";
}
});
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);*/
// 调整 xml 生成目录演示
focList.add(new FileOutConfig("/templates/mapper.xml.vm") {
@Override
public String outputFile(TableInfo tableInfo) {
return mapperXmlPath + tableInfo.getEntityName() + "Mapper.xml";
}
});
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
// 关闭默认 xml 生成,调整生成 至 根目录
/* TemplateConfig tc = new TemplateConfig();
tc.setXml(null);
mpg.setTemplate(tc);*/
// 自定义模板配置,可以 copy 源码 mybatis-plus/src/main/resources/templates 下面内容修改,
// 放置自己项目的 src/main/resources/templates 目录下, 默认名称一下可以不配置,也可以自定义模板名称
// TemplateConfig tc = new TemplateConfig();
// tc.setController("...");
// tc.setEntity("...");
// tc.setMapper("...");
// tc.setXml("...");
// tc.setService("...");
// tc.setServiceImpl("...");
// 如上任何一个模块如果设置 空 OR Null 将不生成该模块。
// mpg.setTemplate(tc);
// 执行生成
mpg.execute();
// System.err.println("mpg finished!");
// 打印注入设置【可无】
// System.err.println(mpg.getCfg().getMap().get("abc"));
}
}
执行MPGenerator main
配置mapper接口的扫描路径
package ik.starriver.ispmanager;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan({"ik.starriver.ispmanager.mapper*"})
public class IspManagerApplication {
public static void main(String[] args) {
SpringApplication.run(IspManagerApplication.class, args);
}
}
启动服务
正常
导入相关工具类包
添加注解
@Component
public class TblContractFileManager extends BaseModel {
@Service
public class TblContractFileManagerServiceImpl extends ServiceImpl<TblContractFileManagerMapper, TblContractFileManager> implements ITblContractFileManagerService {
}
通过spring的RestController注解Bean
@RestController
@RequestMapping("/starriver.ispmanager/tblContractFileManager")
public class TblContractFileManagerController extends BaseController<TblContractFileManager, TblContractFileManagerServiceImpl>{
}
或者通过ApplicationAutoConfiguration的@Bean注解(2种方式二选一)
@Configuration
@ConditionalOnWebApplication
public class ApplicationAutoConfiguration {
@Bean
public TblChangeHistoryManagerController tblChangeHistoryManagerController() {
return new TblChangeHistoryManagerController();
}
@Bean
public TblContractFileManagerController tblContractFileManagerController() {
return new TblContractFileManagerController();
}
@Bean
public TblLineManagerController tblLineManagerController() {
return new TblLineManagerController();
}
@Bean
public TblContractManagerController tblContractManagerController() {
return new TblContractManagerController();
}
}
数据库字段名设计最好不要和关键字重复:
@TableField(value = "description") //映射数据库的字段名
private String description; //不要使用desc,这是关键字段,会和数据库冲突
数据校验:
方式一:自定义ConstraintValidator实现类,通过泛型传递给BaseController进行验证
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE, TYPE})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {ContractManagerValidator.class})
public @interface ContractManagerCA {
String message() default "ContractManagerCA message";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
@Component
public class ContractManagerValidator implements ConstraintValidator<ContractManagerCA, ContractManager>{
@Override
public void initialize(ContractManagerCA constraintAnnotation) {
}
@SneakyThrows
@Override
public boolean isValid(ContractManager value, ConstraintValidatorContext context) {
ValidateTimeDelta(value);
return true;
}
public void ValidateTimeDelta(ContractManager data) throws Exception {
if(!data.getEndDate().isAfter(data.getStartDate())){
throw new DefaultException("合同结束日期应该大于开始日期");
}
}
}
public class BaseController<E extends BaseModel, S extends ServiceImpl, V extends ConstraintValidator> implements
DeleteOneController
, DeleteManyController
, GetOneController
, GetPageController
, InsertOneController<ResponseEntity, E>
, UpdateOneController<ResponseEntity, E> {
@Autowired
private S service;
@Autowired
private E entity;
@Autowired
private V validator;
...
@Override
@RequestMapping(path = "/", method = {RequestMethod.PUT})
public ResponseEntity update2(@RequestBody E object) {
try {
validator.isValid(object, null);
} catch (Exception e) {
return this.responseEntity.error(e.getMessage().toString());
}
方式二:@Valid注解方式校验
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE, TYPE})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {ContractManagerValidator.class})
public @interface ContractManagerCA {
String message() default "ContractManagerCA message";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
@Component
public class ContractManagerValidator implements ConstraintValidator<ContractManagerCA, ContractManager>{
@Override
public void initialize(ContractManagerCA constraintAnnotation) {
}
@SneakyThrows
@Override
public boolean isValid(ContractManager value, ConstraintValidatorContext context) {
ValidateTimeDelta(value);
return true;
}
public void ValidateTimeDelta(ContractManager data) throws Exception {
if(!data.getEndDate().isAfter(data.getStartDate())){
throw new DefaultException("合同结束日期应该大于开始日期");
}
}
}
在entity上添加校验注解
@ContractManagerCA
public class ContractManager extends BaseModel {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
// @Max(value = 0, message = "id validate failed")
private Integer id;
校验注解请求接口的RequestBody 校验实体对象
@Override
@RequestMapping(path = "/", method = {RequestMethod.PUT})
public ResponseEntity update(@Valid @RequestBody E object, Errors error) {
if (error.hasErrors()) {
return this.responseEntity.error(error.getFieldError().getDefaultMessage().toString());
}
int updateById = service.getBaseMapper().updateById(object);
return this.responseEntity.success(service.getById(object.getId()));
}
JWT token校验
方案一:AOP
package ik.starriver.log.aspect;
import ik.starriver.log.utils.DefaultResponseEntity;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* //匹配任何以find开头而且只有一个Long参数的方法
*
* @Pointcut("execution(* *..find*(Long))")
* //匹配任何以find开头的而且第一个参数为Long型的方法
* @Pointcut("execution(* *..find*(Long,..))")
* //匹配任何只有一个Long参数的方法
* @Pointcut("within(com.imooc..*) && args(Long)")
* //匹配第一个参数为Long型的方法
* @Pointcut("within(com.imooc..*) && args(Long,..)")
* Created by cat on 2016-12-04.
*/
@Aspect
@Component
public class ControllerAspect {
@Autowired
private DefaultResponseEntity responseEntity;
// @Pointcut("args(Long,String) && within(com.imooc.service.*)")
// public void matchArgs() {
// }
//
// @Before("matchArgs()")
// public void before() {
// System.out.println("");
// System.out.println("###before");
// }
// @Around("execution(* ik.starriver.log.controller..*.*(..))")
public Object invoke(ProceedingJoinPoint joinPoint) throws Throwable {
if (joinPoint.toString().endsWith(".value())")){
return joinPoint.proceed(); //判断通过,返回执行原函数
}
Object[] args = joinPoint.getArgs();
for (Object arg : args) {
if (arg instanceof HttpHeaders) {
List<String> tokens = ((HttpHeaders) arg).getValuesAsList(HttpHeaders.AUTHORIZATION);
System.err.println(tokens.toString());
// TODO validate token
//validate success
// return joinPoint.proceed();
//validate fail
return this.responseEntity.error("validate fail");
}
}
return false;
}
}
jwt的逻辑
restTemplate
代码实例:
D:\shen\java\webdevelop\spring\log\src\test\java\ik\starriver\log\web\rest\CallingRESTServiceswithRestTemplate.java
use exchange for CRUD
参考spring-boot-reference
Calling REST Services with
RestTemplate
Calling REST Services with
HttpClient