文章目录
1 内容介绍
- 前后端分离开发
- 讲师管理模块(后端)——curd
2 前后端分离开发
- 前端
- html css js ——数据显示
- ajax操作
- 后端
- controller service mapper —— 返回数据、操作数据
- 开发接口 (开发controller service mapper )
ajax调用接口,接口返回数据——json数据
3 搭建项目环境
1 数据库
- 创建数据库,创建讲师数据库表
guli_edu.sql
- 设计规范:《阿里巴巴Java开发手册》
- 必备三个字段:id,gmt_create, gmt_modified
2 项目结构
- 创建父工程——springboot工程
-
pom类型 管理依赖版本和放公共依赖
-
子模块1——maven工程
- 子子模块1 eg.讲师管理模块——maven工程
- 子子模块2 eg. 统计分析模块
-
子模块2
-
4 开发讲师模块
代码生成器
-
1 创建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=1234 # mybatis日志 mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
-
2 编写controller service mapper相关代码,mp提供代码生成器,直接生成相关代码(会修改就行)
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("D:\\Learning\\springboot\\online-educate\\guli_parent\\service\\service_edu" + "/src/main/java"); gc.setAuthor("testjava"); gc.setOpen(false); //生成后是否打开资源管理器 gc.setFileOverride(false); //重新生成时文件是否覆盖 gc.setServiceName("%sService"); //去掉Service接口的首字母I 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("1234"); dsc.setDbType(DbType.MYSQL); mpg.setDataSource(dsc); // 4、包配置 PackageConfig pc = new PackageConfig(); // 包:com.mys.eduservice pc.setParent("com.mys"); pc.setModuleName("eduservice"); //模块名 // 包:com.mys.eduservice.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(); } }
5 讲师列表
1 创建controller
@RestController
@RequestMapping("/eduservice/teacher")
public class EduTeacherController {
// 1 注入service
@Autowired
private EduTeacherService teacherService;
// 2 查询讲师表中的所有数据
// rest风格
@GetMapping("findAll")
public List<EduTeacher> findAll() {
// 3 调用service
List<EduTeacher> list = teacherService.list(null);
return list;
}
}
2 创建启动类
@SpringBootApplication
public class EduApplication {
public static void main(String[] args) {
SpringApplication.run(EduApplication.class, args);
}
}
3 创建配置类
mapper扫描和其他
@Configuration
@MapperScan("com.mys.eduservice.mapper")
public class EduConfig {
}
4 最终测试
-
启动项目
-
访问地址:
http://localhost:8001/eduservice/teacher/findAll
5 设置时间格式配置
# 返回json的全局时间格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
6 讲师逻辑删除
1 配置逻辑删除插件
@Bean
public ISqlInjector sqlInjector() {
return new LogicSqlInjector();
}
**2 逻辑删除属性上加注解 **
@TableLogic
private Integer isDeleted;
3 编写controller
@DeleteMapping("{id}") // id值需要通过路径传递
public boolean removeTeacher(@PathVariable String id) {
return teacherService.removeById(id);
}
4 如何测试?
工具:swagger、postman
swagger进行接口测试
-
生成在线接口文档;方便接口测试
-
创建公共模块,整合swagger,为了所有模块都能进行使用
-
具体使用
- 1、gui_parent创建子模块common,在common创建子模块service_base,写配置类
@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("Helen", "http://atguigu.com", "55317332@qq.com")) .build(); } }
-
2、在service_edu中引入service_base依赖
<dependency> <groupId>com.mys</groupId> <artifactId>service_base</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
-
3、在service_edu启动类添加注释,设置包扫描规则
@ComponentScan(basePackages = {"com.mys"})
-
swagger注释的注解
- 在EduTeacherController类上加:
@Api(description = "讲师管理")
- 在方法findAll上加:
@ApiOperation(value = "讲师所有列表")
- 在EduTeacherController类上加:
7 统一结果返回
-
json数据格式2种
-
对象
-
数组
{ "success": 布尔, //响应是否成功 "code": 数字, //响应码 "message": 字符串, //返回消息 "data": HashMap //返回数据,放在键值对中 }
-
-
步骤
-
1 在common模块创建子模块common_utils
-
2 创建interface,定义数据返回状态码,20000成功 20001失败
public interface ResultCode { public static Integer SUCCESS = 20000; // 成功 public static Integer ERROR = 20001; // 失败 }
-
-
使用
-
1 在service引入common_utils
<dependency> <groupId>com.mys</groupId> <artifactId>common_utils</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
-
2 统一返回结果类R
用于使用链式编程调用:
R.ok().code().message()
@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; } }
-
3 把原来的接口的方法改为R
// 1 查询讲师表中的所有数据 // rest风格 @ApiOperation(value = "讲师所有列表") @GetMapping("findAll") public R findAll() { // 调用service List<EduTeacher> list = teacherService.list(null); return R.ok().data("items", list); } // 2 逻辑删除讲师的方法 @ApiOperation(value = "逻辑删除讲师") @DeleteMapping("{id}") // id值需要通过路径传递 public R removeTeacher(@ApiParam(name = "id", value = "讲师ID", required = true) @PathVariable String id) { boolean flag = teacherService.removeById(id); return flag ? R.ok() : R.error(); }
-
8 分页功能
1 配置mp分页插件
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
2 编写讲师分页查询接口
@ApiOperation(value = "分页查询讲师")
@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(); // 数据list集合
// 1
//return R.ok().data("total", total).data("rows", records);
// 2
Map map = new HashMap();
map.put("total", total);
map.put("rows", records);
return R.ok().data(map);
}
9 多条件组合查询带分页
1 把条件值传递到接口中,把对象传递到接口里——VO
entity/vo/
@Data
public class TeacherQuery {
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 根据条件值判断,拼接条件
- RequestBody:使用json传递数据,将数据封装到对应对象里
- 使用post方式提交
- 加上注解
@RequestBody(required = false)
(参数值可以为空)
- ResponseBody:返回json数据
// 条件查询带分页
@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);
// 多条件组合查询 动态sql
// 构建条件
QueryWrapper<EduTeacher> wrapper = new QueryWrapper<>();
String name = teacherQuery.getName();
Integer level = teacherQuery.getLevel();
String begin = teacherQuery.getBegin();
String end = teacherQuery.getEnd();
// 判断条件是否为空,如果不为空拼接条件 column:表中华字段名
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_create", end);
}
// 调用方法实现条件查询分页
teacherService.page(pageTeacher, wrapper);
long total = pageTeacher.getTotal(); // 总记录数
List<EduTeacher> records = pageTeacher.getRecords(); // 数据list集合
return R.ok().data("total", total).data("rows", records);
}
10 添加讲师
-
注意
-
gmtCreate是属性名
this.setFieldValByName("gmtCreate", new Date(), metaObject);
-
name是表中字段名
wrapper.like("name", name);
-
1 自动填充
// 1.加注解@TableField
@ApiModelProperty(value = "创建时间")
@TableField(fill = FieldFill.INSERT)
private Date gmtCreate;
@ApiModelProperty(value = "更新时间")
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date gmtModified;
// 2.编写MyMetaObjectHandler
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
// filedName是属性名称,不是字段名
// 注意区分:wrapper.like("name", name); 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 编写controller
// 5 添加讲师
@PostMapping("addTeacher")
public R addTeacher(@RequestBody EduTeacher eduTeacher) {
boolean save = teacherService.save(eduTeacher);
return save ? R.ok() : R.error();
}
11 讲师修改功能
1 根据讲师id查询
@GetMapping("getTeacher/{id}")
public R getTeacher(@PathVariable String id) {
EduTeacher eduTeacher = teacherService.getById(id);
return R.ok().data("teacher", eduTeacher);
}
2 实现讲师修改
@PostMapping("updateTeacher")
public R updateTeacher(@RequestBody EduTeacher eduTeacher) {
boolean flag = teacherService.updateById(eduTeacher);
return flag ? R.ok() : R.error();
}
12 统一异常处理
1 全局异常处理
- 在common中创建exceptionhandler/GlobalExceptionHandler
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
@ResponseBody // 为了返回数据
public R error(Exception e) {
e.printStackTrace();
return R.error().message("执行了全局异常处理");
}
}
- 在common_base的pom中引入common_utils;同时去掉service的pom中的common_utils依赖引入,防止重复
<dependency>
<groupId>com.mys</groupId>
<artifactId>common_utils</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
2 特殊异常处理
@ExceptionHandler(ArithmeticException.class)
@ResponseBody // 为了返回数据
public R error(ArithmeticException e) {
e.printStackTrace();
return R.error().message("特定异常处理:ArithmeticException");
}
3 自定义异常处理
-
创建自定义异常类,继承RuntimeException,写异常属性
@Data @AllArgsConstructor // 有参构造 @NoArgsConstructor // 无参构造 public class GuliException extends RuntimeException{ private Integer code; // 状态码 private Integer msg; // 异常信息 }
-
在统一异常类添加规则
@ExceptionHandler(GuliException.class) @ResponseBody // 为了返回数据 public R error(GuliException e) { e.printStackTrace(); return R.error().code(e.getCode()).message(e.getMsg()); }
13 同一日志处理
-
配置日志级别
-
OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL
-
# 设置日志级别 logging.level.root=INFO
-
-
把日志不仅输出到控制台,也可以输出到文件中,使用日志工具
-
log4j
-
logback
-
删除application.properties日志配置
# 设置日志级别 #logging.level.root=INFO # mybatis日志 #mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
-
resources中创建logback-spring.xml (参考完整代码资源)
-
-
-
如果程序运行出现异常,把异常信息输出到文件中
- 在GlobalExceptionHandler上加注解:
@Slf4j
- 在异常处理方法中加上
log.error(e.getMessage());
- 在GlobalExceptionHandler上加注解: