苍穹外卖知识点总结

 苍穹外卖用到的所有注解:

@Target:用来限制注解的使用范围,以防止误用。

常见的 @Target 注解的使用示例:

  1. @Target(ElementType.TYPE):该注解只能用于类或接口上。
  2. @Target(ElementType.METHOD):该注解只能用于方法上。
  3. @Target(ElementType.FIELD):该注解只能用于字段上。
  4. @Target(ElementType.PARAMETER):该注解只能用于参数上。

 @Retention:

注解可以用于类、方法、字段等元素上,用于指定该元素上的注释应该如何处理。它有一个属性 value,用于指定注释的保留时间。该属性的取值可以为以下三个值之一:

  • RetentionPolicy.SOURCE:注释只在源代码中保留,编译时会被丢弃。这是默认值。
  • RetentionPolicy.CLASS:注释在编译时被保留,并在运行时被丢弃。
  • RetentionPolicy.RUNTIME:注释在运行时被保留,可以通过反射等方式进行访问。

 @Aspect:
注解用于标记一个类作为切面类,这样的类可以包含一些通知(advice)和方法,这些通知和方法能够定义在何时、何地以及如何执行某些代码。

@Component:
用于将普通Java类声明为Bean的注解,从而不需要手动注册和配置每个Bean。

@Pointcut:
用于定义切点

@Before:
用于标记在执行某个测试方法之前应该执行的代码块。

 @RequestBody:
它用于处理来自 HTTP 请求体的 JSON 数据,只能用于处理 POST 和 PUT 请求。

 @Transactional:
保证事务的一致性,比如新增菜品时,菜品添加进了数据库,而菜品口味因为异常而没有添加进,处理起来就比较麻烦,加入此注解后,有一个没添加进数据库,全部都不会被添加进数据库

 @RequestParam:
是Spring MVC中用于处理HTTP请求参数的重要注解,它可以将请求中的参数值绑定到Java方法参数上,使开发者能够更方便地处理和操作请求数据。

 @PathVariable:
用于从请求的URL中提取参数值并传递给处理程序方法的参数。要是/user/id这种,而不是/user?id

公共字段自动填充

使用注解:
@Target,@Retention,@Aspect,@Component,@Pointcut,@Before
流程分析:
1.先创建一个自定义的注解,加入@Target和Retention注解,再调用含有插入和修改的方法
2.再创建一个自动注入类,加入@Aspect和@Component,在方法里用@Pointcut定义切点,并创建一个@Before方法
3.在@Before方法里先获取到当前被拦截的方法上的数据库操作类型,再获取到当前被拦截的方法的参数--实体对象,再准备赋值的数据,最后根据当前不同的操作类型,为对应的属性通过反射来赋值
4.利用上面AOP思想创好自动注入方法后,在Mapper里给需要的新增和修改方法添加我们自己创建的自动注入注解
实际操作:

注解代码:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
    OperationType value();
}

切面切点代码 :

@Aspect
@Component
@Slf4j
public class AutoFillAspect {
    @Pointcut("execution(* com.sky.mapper.*.*(..)) && 
           @annotation(com.sky.annotation.AutoFill)"
         )
    public void autoFillPointCut() {
    }

    @Before("autoFillPointCut()")
    public void autoFill(JoinPoint joinPoint) {
        log.info("开始进行公共字段自动填充...");
        //1.获取到当前被拦截的方法上的数据库操作类型
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();//方法签名对象
        AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获得方法上的注解对象
        OperationType operationType = autoFill.value();//获得数据库操作类型
        //2.获取到当前被拦截的方法的参数--实体对象
        Object[] args = joinPoint.getArgs();
        if (args == null || args.length == 0) {
            return;
        }
        Object entity = args[0];
        //3.准备赋值的数据
        LocalDateTime now = LocalDateTime.now();
        Long currentId = BaseContext.getCurrentId();
        //4.根据当前不同的操作类型,为对应的属性通过反射来赋值
        if (operationType == OperationType.INSERT) {
            //为4个公共字段赋值
            try {
                Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
                Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
                Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
                Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
            //通过反射为对象属性赋值
                setCreateTime.invoke(entity, now);
                setCreateUser.invoke(entity, currentId);
                setUpdateTime.invoke(entity, now);
                setUpdateUser.invoke(entity, currentId);
            } catch (Exception e) {
                e.printStackTrace();
            }


        } else if (operationType == OperationType.UPDATE) {
            //为2个公共字段赋值
            try {
                Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
                Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
                //通过反射为对象属性赋值
                setUpdateTime.invoke(entity, now);
                setUpdateUser.invoke(entity, currentId);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

 Swagger:

请求路径:localhost:8080/doc.html
使用方式:

1.导入Knife4j的maven坐标

<dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
        </dependency>


2.在配置类中加入Knife4j的相关配置

@Bean
    public Docket docket() {
        ApiInfo apiInfo = new ApiInfoBuilder()
                .title("苍穹外卖项目接口文档")
                .version("2.0")
                .description("苍穹外卖项目接口文档")
                .build();
        Docket docket = new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.sky.controller"))
                .paths(PathSelectors.any())
                .build();
        return docket;
    }


3.设置静态资源映射,否则接口文档页面无法访问

 protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }
常用注解:

登录接口 :

流程分析:

1.controller层正常操作,参数是自己写的DTO.
2.实现层首先需要把账号和密码取出来,然后通过名字去查数据库
3.先判断数据库中是否有这个账号,再判断密码是否正确,最后判断账号状态是否处于禁用
4,登录成功生成jwt令牌

用到的注解:@RequestBody
实际操作:

    public Employee login(EmployeeLoginDTO employeeLoginDTO) {
        String username = employeeLoginDTO.getUsername();
        String password = employeeLoginDTO.getPassword();

        //1、根据用户名查询数据库中的数据
        Employee employee = employeeMapper.getByUsername(username);

        //2、处理各种异常情况(用户名不存在、密码不对、账号被锁定)
        if (employee == null) {
            //账号不存在
            throw new AccountNotFoundException(MessageConstant.ACCOUNT_NOT_FOUND);
        }

        password = DigestUtils.md5DigestAsHex(password.getBytes());

        //密码比对
        //进行md5加密后,然后再进行比对
        if (!password.equals(employee.getPassword())) {
            //密码错误
            throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR);
        }

        if (employee.getStatus() == StatusConstant.DISABLE) {
            //账号被锁定
            throw new AccountLockedException(MessageConstant.ACCOUNT_LOCKED);
        }

        //3、返回实体对象
        return employee;
    }

退出接口:

流程分析:

直接连上接口后,return就行

用到的注解:无
实际操作:
@PostMapping("/logout")
    public Result<String> logout() {
        return Result.success();
    }

新增接口:

普通新增:
流程分析:

1.先创建一个DTO,因为前端只传过来几个值,直接用Employee会增大数据的传输量
2.再在实现类里面创建sava方法,在方法里new一个Employee,把DTO的值拷贝过去,并设置好状态和密码(记得加密),因为放进数据库需要Employee里面的完整数据,所以不能直接传DTO进去
3.调用Mapper层的insert方法,要自己写insert语句

用到的注解:@RequestBody
实际操作:

实现类代码:

public void save(EmployeeDTO employeeDTO) {
        Employee employee = new Employee();
        BeanUtils.copyProperties(employeeDTO, employee);
        employee.setStatus(StatusConstant.ENABLE);
        employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes()));
       // employee.setCreateTime(LocalDateTime.now());
       // employee.setUpdateTime(LocalDateTime.now());

       // employee.setCreateUser(BaseContext.getCurrentId());
       // employee.setUpdateUser(BaseContext.getCurrentId());
        employeeMapper.insert(employee);
    }
多表新增 
注意:多表操作需要加上事务注解
流程分析:

(1).和普通新增一样的操作,不过在实现类里还需要用DTO把没有的那张表get出来,并进行foeach来给每一个数据添加id(这个id在第一个表的sql语句中添加useGeneratedKeys为ture,keyProperty为id,才可以取得).
(2).调用Mapper层的insert方法,要自己写insert语句,不同表是不同的Mapper层

用到的注解:@Transactional,@RequestBody
实际操作:
实现层代码
@Transactional
    public void saveWithFlavor(DishDTO dishDTO) {
        Dish dish = new Dish();
        BeanUtils.copyProperties(dishDTO, dish);
        dishMapper.insert(dish);
     //这个id要在sql语句上加useGeneratedKeys,keyProperty才能获取到
        Long dishId = dish.getId();

        List<DishFlavor> flavors = dishDTO.getFlavors();
        if (flavors != null && flavors.size() > 0 ){
            flavors.forEach(dishFlavor -> {
                dishFlavor.setDishId(dishId);
            });
            dishFlavorMapper.insertBatch(flavors);
        }
    }
sql语句

 单条数据

<insert id="insert" useGeneratedKeys="true" keyProperty="id">
insert into sky_take_out.dish (name, category_id, price, image, description, create_time, update_time, create_user, update_user,status)
  values
(#{name},#{categoryId},#{price},#{image},#{description},#{createTime},#{updateTime},#{createUser},#{updateUser},#{status})
    </insert>

 多条数据

<insert id="insertBatch">
        insert into sky_take_out.dish_flavor(dish_id, name, value) VALUES
        <foreach collection="flavors" item="df" separator=",">
            (#{df.dishId},#{df.name},#{df.value})
        </foreach>
    </insert>

删除接口:

流程分析:
(1).用List<Long>来当参数接收多个id(注意不是/是?所以不能用@Requestbody)
(2).用增强for遍历每一个id.把本类方法查询数据库,判断状态是否是起售状态,是报异常
(3).在需要判断是否跟其他表有关联时,需创建一个新的Mapper,并在这个Mapper的xml里用foreach遍历id查询(collection是要遍历的集合,item是集合中的每一个元素,separator是元素间的符号,open是集合开始的符号,close是集合结束的符号).判断查询出来的结果是否为空以及是否大于0,是报异常
(4).用增强for遍历每一个id并调用本类以及与其关联的表的删除方法,删除菜品以及关联口味
(5).最后加上事务管理注解
用到的注解:@RequestParam
实际操作:

实现层代码:

@Transactional
    public void removeById(List<Long> ids) {
//判断状态
        for (Long id : ids){
            Dish dish = dishMapper.getById(id);
            if (dish.getStatus()== StatusConstant.ENABLE){
                throw new DeletionNotAllowedException(MessageConstant.DISH_ON_SALE);
            }
        }
//判断是否关联
        List<Long> setmealIds = setmealDishMapper.dishBySetmealId(ids);
        if (setmealIds != null && setmealIds.size()>0){
            throw new DeletionNotAllowedException(MessageConstant.DISH_BE_RELATED_BY_SETMEAL);
        }
//删除菜品以及关联口味
        for (Long id :ids){
            dishMapper.removeById(id);
            dishFlavorMapper.removeById(id);
        }

 查询是否有关联的xml代码:

<select id="dishBySetmealId" resultType="java.lang.Long">
        select setmeal_id from sky_take_out.setmeal_dish where dish_id in
        <foreach collection="dishIds" item="dishId" separator="," open="(" close=")">
            #{dishId}
        </foreach>
    </select>

修改接口:

普通修改 
流程分析:

1.首先明确是页面传一个值还是一堆值过来,一个值的参数就是那个值加当前id,一堆值就需要创一个适合的DTO来当参数
2.都需要new一个和数据库表对应的实体类,因为最后需要sql语句来操作这张表
3.一个值的直接set值,setId,一堆值的先把DTO的数据拷贝到new出来的实体类中,再在实现层setDTO没有的数据,最后统一在mapper层进行sql语句修改
4.sql语句复杂的需要在xml文件中写

用到的注解:@RequestBody, @PathVariable
实际操作:

一堆值实现类代码

public void update(EmployeeDTO employeeDTO) {
        Employee employee = new Employee();
        BeanUtils.copyProperties(employeeDTO, employee);
        employee.setUpdateTime(LocalDateTime.now());
        employee.setUpdateUser(BaseContext.getCurrentId());
        employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes()));
        employeeMapper.update(employee);
    }
多表修改
流程分析:

1.参数是DTO来接受前端多表数据,在实现层我们需要先创一个包含本表的实体类,把DTO中的值拷贝过去,用新的实体类调用本类方法修改普通数据
2.再删除原本数据的另一张表的数据,例如菜品删除口味数据(用dishFlavorMapper的批量删除方法)
3.最后像多表新增一样把我们修改的口味数据添加上去,给每一个口味设上id,用dishFlavorMapper的批量新增方法把数据新增上去

用到的注解:@RequestBody
实际操作: 
public void update(DishDTO dishDTO) {
        Dish dish = new Dish();
        BeanUtils.copyProperties(dishDTO,dish);
        dishMapper.update(dish);
        //删除原先口味表数据
        dishFlavorMapper.removeById(dishDTO.getId());
        //添加我们新修改的口味表数据
        List<DishFlavor> flavors = dishDTO.getFlavors();
        if (flavors != null && flavors.size() > 0) {
            flavors.forEach(dishFlavor -> {
                dishFlavor.setDishId(dishDTO.getId());
            });
            dishFlavorMapper.insertBatch(flavors);
        }
    }

查询接口:

流程分析:

1.先判断是回显数据还是分页查询,回显直接用id查询就行
2.回显需要创一个VO,包含原本实体类所有信息以及要多表回显的实体类,在实现类里查询本类信息(写sql语句),再查询另一张表的信息(在另一个Mapper里写sql语句),最后把本类信息拷贝到VO中,并给VO的新值设置好口味信息
3.分页查询先创建一个有页码,每页记录数以及搜索姓名的DTO当参数(分页查询不需要@Requesbody),然后用PageHelper.startPage把页码和每页记录数传过去,并调用mapper层方法,sql是在xml里面写的带有搜索姓名的查询全部语句
4.如果是多表分页查询,那就是在实现层里的Page对象是新创建的VO,VO包含了另一张表的数据
5.最后把page里面的total和result拿出来并传回给controller层

用到的注解:@PathVariable
实际操作:

回显查询实现层代码

@Override
    public DishVO getById(Long id) {
        //查询原本信息
        Dish byId = dishMapper.getById(id);
        //查询口味信息
        List<DishFlavor> dishFlavors = dishFlavorMapper.getDishFlavor(id);
        //把原本信息拷贝到有口味信息的VO上
        DishVO dishVO = new DishVO();
        BeanUtils.copyProperties(byId,dishVO);
        //设置好口味信息
        dishVO.setFlavors(dishFlavors);
        return dishVO;
    }

单表分页查询实现层代码

public PageResult list(EmployeePageQueryDTO queryDTO) {
        PageHelper.startPage(queryDTO.getPage(), queryDTO.getPageSize());
        Page<Employee> page = employeeMapper.list(queryDTO);
        long total = page.getTotal();
        List<Employee> records = page.getResult();
        return new PageResult(total, records);
    }

多表分页查询实现层代码

@Override
    public PageResult page(DishPageQueryDTO queryDTO) {
        PageHelper.startPage(queryDTO.getPage(), queryDTO.getPageSize());
        Page<DishVO> page = dishMapper.pageInfo(queryDTO);
        return new PageResult(page.getTotal(), page.getResult());
    }

xml层sql代码 

<select id="list" resultType="com.sky.entity.Employee">
        select * from sky_take_out.employee
        <where>
            <if test="name != null and name != '' ">
                and name like concat('%',#{name},'%')
            </if>
        </where>
        order by create_time desc
</select>

  • 21
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值