在线教育-谷粒学院学习笔记(二)

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

      image-20220712101247327

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 = "讲师所有列表")

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());
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值