苍穹外卖项目(黑马)学习笔记DAY2

目录

新增员工功能

基础代码编写

代码完善

员工分页查询

基础代码编写

代码完善

启动或禁用员工账号

编辑员工        

附带MP查询数据库的写法

新增员工功能

  • 基础代码编写

        在开发每一个业务模块代码的时候,我们应该先根据产品原型进行需求分析和设计。根据产品原型,我们可以设计出对应的数据库和实体类,并根据属性自身的要求对其做相应的要求,例如手机号位数和身份证格式。

苍穹外卖项目(黑马)学习笔记DAY2_第1张图片

         紧接着我们根据接口文档的时候,时刻提问自己:用什么方法请求?传入什么参数?返回什么数据?(在完成项目的时候,有时候会因为返回给前端数据格式不对导致前端页面没有反应。) 因此这三个问题,非常的重要。有时候前端提交的表单并不包含实体类的所有属性值,例如本项目中employee的属性值多余传入的参数,因此需要设置对应的DTO来封装数据。

        新增员工的请求方法是Post,传入的参数事宜JSON格式的EmployeeDTO,返回数据只显示是否成功即可不挟带其余数据。后端接收JSON格式要在参数前面添加@RequestBody。

    @PostMapping
    @ApiOperation("新增员工")
    public Result save(@RequestBody EmployeeDTO employeeDTO){
        employeeService.save(employeeDTO);
        return Result.success();
    }

         注意在自己以后的项目中,如果有多处地方使用相同的字符串和数字可以将其定义常量。常量使用final来修饰,通常使用大写字母命名,多个单词之间使用下划线 _ 分隔。

    public static final String SET_CREATE_TIME = "setCreateTime";

        最后,这个功能的难点就在于持久层的编写了。

        在持久层操作使用实体类 ,需要将DTO----->实体 ,使用对象属性拷贝(要求属性名一致才行)

BeanUtils.copyProperties(employeeDTO,employee);

        进行属性拷贝后,我们要注意对那些没有数值的属性赋值后在进行数据库的插入操作。项目中使用的是Mybatis,注意插入语法的格式,第一个括号对应的是数据库对应的属性名称,第二个括号是实体类的属性名称。注意,实体类属性名称有的是和数据库中不一样采用的是驼峰命名的方式因此要再配置文件中要开启对应的驼峰命名。

    @Insert("insert into sky_take_out.employee (name, username, password, phone, sex, id_number," +
            " create_time, update_time, create_user, update_user,status) values (#{name},#{username},#{password},#{phone},#{sex}," +
            "#{idNumber},#{createTime},#{updateTime},#{createUser},#{updateUser},#{status})")
    void save(Employee employee);


mybatis:
  #mapper配置文件
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.sky.entity
  configuration:
    #开启驼峰命名
    map-underscore-to-camel-case: true

        这里也给出MP的方式添加数据。

        在配置完数据库后,添加MP的maven坐标,需要再启动类上添加注释说明在哪儿扫描相应的Mapper。@MapperScan("com.example.mapper"),然后创建Mapper接口类继承BaseMapper在业务层的实现类上直接调用对应的方法。

@SpringBootApplication
@MapperScan("com.example.mapper")
public class MybatispulsApplication {

    public static void main(String[] args) {
        SpringApplication.run(MybatispulsApplication.class, args);
    }

}

//业务层
@Service
public class AdminServiceImpl implements AdminService {
    @Autowired
    private AdminMapper adminMapper;
    @Override
    public void insert(EmployeeDTO employeeDTO) {
        Employee employee=new Employee();
        BeanUtils.copyProperties(employeeDTO,employee);
        employee.setStatus(1);
        employee.setCreateTime(LocalDateTime.now());
        employee.setUpdateTime(LocalDateTime.now());
        employee.setCreateUser(10L);
        employee.setPassword("e10adc3949ba59abbe56e057f20f883e");
        employee.setUpdateUser(10L);

        adminMapper.insert(employee);
    }
}

//Mapper
@Mapper
public interface AdminMapper extends BaseMapper {
}

       使用MP添加数据库 要注意ID不是自增的问题如果想要ID自增需要在对应实体类的属性值上面添加@TableId(type = IdType.AUTO)

  • 代码完善

        数据库中规定用户名这个字段是唯一的,因此在添加数据中如果出现相同的字段会出添加失败。使用全局异常捕捉相同用户名错误,并返回给前端。

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 普通用户名重复异常
     * @param exception
     * @return
     */
    @ExceptionHandler
    public Result exceptionHandler(SQLIntegrityConstraintViolationException exception){
        if(exception.getMessage().contains("Duplicate entry")){
            String[] strings = exception.getMessage().split(" ");
            String msg = strings[2] + "已存在";
            return  Result.error(msg);

        }else return Result.error(MessageConstant.UNKNOWN_ERROR);
    }

}

        通过在带有 @ExceptionHandler 注解的方法中捕获异常,@RestControllerAdvice 可以实现全局的异常处理。这样一来,无论哪个控制器发生异常,都可以通过统一的异常处理类进行处理,返回适当的错误响应。

        在员工实体类中有一项属性值是创建用户和修改用户,如何获取当前是哪个用户在操作新增用户这一功能呢?

        我们在DAY1复习JWT的时候知道,在生成JWT令牌的时候我们会将用户的ID传入然后根据其它信息生成Token,存储到浏览器中,浏览器的每次请求都会携带这个令牌。我们可以在验证用户身份合法后,解析令牌获取里面ID这一数值,存入ThreadLocal中。ThreadLocal它允许你在每个线程中存储和访问特定于该线程的数据,而不会与其他线程的数据混淆。ThreadLocal 经常用于在多线程应用程序中保持线程私有的状态或数据,例如用户会话信息、数据库连接等。

public class BaseContext {

    public static ThreadLocal threadLocal = new ThreadLocal<>();

    public static void setCurrentId(Long id) {
        threadLocal.set(id);
    }

    public static Long getCurrentId() {
        return threadLocal.get();
    }

    public static void removeCurrentId() {
        threadLocal.remove();
    }

}

        静态成员变量在类加载时被初始化,当设定 BaseContext.setCurrentId(empId);threadLoal会被创建。然后无论是新增或者编辑员工时候直接获取其中的Id值就可以了。

员工分页查询

  • 基础代码编写

        用什么方法请求?传入什么参数?返回什么数据?

        根据产品原型我们知道可以根据员工名称进行查询,并且每一页需要10条数据。在查看接口文档,使用get方式请求,需要传入name,page,pageSize,返回的数据是需要包括页总数和emplyee这个实体类的信息。在项目中使用EmployeePageQueryDTO来接收前端传过来的数据,

@Data
public class EmployeePageQueryDTO implements Serializable {

    //员工姓名
    private String name;

    //页码
    private int page;

    //每页显示记录数
    private int pageSize;

}

使用PageResult作为最终的数据返回给前端,其中records集合里包含了employee实体类的信息。

public class PageResult implements Serializable {

    private long total; //总记录数

    private List records; //当前页数据集合

}

         分页查询的实现,本项目中使用的是PageHelper,在使用前需要导入对应的依赖。然后设置分页参数使用PageHelper.startPage()。然后通过page接收查询的结果。那设置的startPage()和查询操作是怎么联系起来的呢?其实PageHelper.startPage()底层也是通过threadlocal来实现的,因此查询的时候可以获取到limit(第几页,每页多少)然后动态拼接进去。

    public PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO) {
        PageHelper.startPage(employeePageQueryDTO.getPage(),employeePageQueryDTO.getPageSize());
        Page page=employeeMapper.pageQuery(employeePageQueryDTO);
        long total = page.getTotal();
        List result = page.getResult();
        return new PageResult(total,result);
    }

        分页的查询操作,需要使用到动态sql因此在xml文件中写。

        :用于判断条件是否成立。使用test属性进行条件判断,如果结果是true才会进行sql的拼接。

        :where元素只会在子元素有内容的情况下才会插入where子句,并且会删除除了子句开头的AND或OR。

        concat:用于连接多个字符串成为一个新的字符串,这里使用模糊查询搜索只要含有name的所有数据

        这里也给出MP的方式分页查询。

        首先MP的分页查询需要先配置分页拦截器。

@Configuration
public class Myconfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor mybatisPlusInterceptor=new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return mybatisPlusInterceptor;
    }
}

        然后就可以设置一些分页相关的参数

    public void pageQuery() {
        LambdaQueryWrapper lqw=new LambdaQueryWrapper();
        lqw.like(Employee::getName,"林");
        IPage page=new Page(1,3);
        adminMapper.selectPage(page,null);
    }

        通过 Employee::getName 获取员工名字的属性,然后查找名字中包含 "林" 的员工记录,条件可以作为like的第一个参数,只不过我这里直接默认为true了。此外,在yml配置文件中配置如下可以在控制台中看到mp的操作过程。

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  • 代码完善

        从数据库中读取到前端的时间存在问题如何结局?

         可以在对应的实体类上使用注解@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")规定格式。

        也可以在WebMvcConfiguration 中扩展Spring MVC的消息转换器,统一对日期类型进行格式化处理

    protected void extendMessageConverters(List> converters) {
        log.info("开启消息转换器...");
        //创建一个消息转换对象
        MappingJackson2HttpMessageConverter converter=new MappingJackson2HttpMessageConverter();
        //为消息转换器设置一个对象转化器将java对象序列化为json数据
        converter.setObjectMapper(new JacksonObjectMapper());
        //将自己的消息转换器加入容器中
        converters.add(0,converter);
    }



/**
 * 对象映射器:基于jackson将Java对象转为json,或者将json转为Java对象
 * 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]
 * 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]
 */
public class JacksonObjectMapper extends ObjectMapper {

    public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
    //public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm";
    public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";

    public JacksonObjectMapper() {
        super();
        //收到未知属性时不报异常
        this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);

        //反序列化时,属性不存在的兼容处理
        this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

        SimpleModule simpleModule = new SimpleModule()
                .addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                .addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                .addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))
                .addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                .addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                .addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));

        //注册功能模块 例如,可以添加自定义序列化器和反序列化器
        this.registerModule(simpleModule);
    }
}
  • 启动或禁用员工账号

        用什么方法请求?传入什么参数?返回什么参数?

        该请求使用Post方式,采用路径参数需要传入状态码和用户id,无需返回多余数据。采用路径参数需要在参数前面添加@PathVariable。一个小细节就是,将传入的参数封装成employee实体类,更新的时候传入的是整个实体类,这样对于后边编辑员工也可以使用相同的update操作。

    
        update employee
        
            
                name=#{name},
            
            
                username=#{username},
            

            
                password=#{password},
            
            
                phone=#{phone},
            
            
                sex=#{sex},
            
            
                id_Number=#{idNumber},
            
            
                update_Time=#{updateTime},
            
            
                update_User=#{updateUser},
            

            
                status=#{status},
            

        
        
            id=#{id}
        
    

        这里也给出MP的方式修改操作。

    public void startOrstop(Integer status, Long id) {
        Employee employee=new Employee();
        employee.setStatus(status);
        employee.setId(id);
       // employee.setUpdateTime(LocalDateTime.now());
        employee.setUpdateUser(9L);
        LambdaQueryWrapper lqw=new LambdaQueryWrapper();
        lqw.eq(Employee::getId,id);
        adminMapper.update(employee,lqw);
    }

如果有需要更新的值就给employee,MP会根据实体类中的内容实现动态的sql。

  • 编辑员工        

        用什么方法请求?传入什么参数?返回什么参数?

         对于编辑操作,要有回显操作即根据id或其他信息将要修改的数据先给前端。

    @Select("select * from sky_take_out.employee where id=#{id}")
    Employee getById(long id);

        编辑操作使用的是Put方式请求,传入的是json格式employeeDTO实体,无额需要返回的数据,使用的更新操作和启动禁用一样。

如果有帮助你,希望可以点赞收藏转发~

你可能感兴趣的:(苍穹外卖学习笔记,学习,笔记,spring,maven,spring,boot,mybatis)