黑马苍穹外卖学习Day3

目录

  • 公共字段自动填充
    • 问题分析
    • 实现思路
    • 代码实现
  • 新增菜品
    • 需求分析和设计
    • 接口设计
    • 代码开发
      • 开发文件上传接口
      • 功能开发
  • 菜品分页查询
    • 需求分析和设计
    • 代码开发
  • 菜品删除功能
    • 需求分析与设计
    • 代码实现
    • 代码优化
  • 修改菜品
    • 需求分析和设计
    • 代码实现

公共字段自动填充

问题分析

员工表和分类表中公共字段可以优化,代码冗余后期修改时麻烦。
在这里插入图片描述

实现思路

在这里插入图片描述
下面我们来重温一下用到的技术点

反射的作用:当我们编写程序时,通常我们在编译时就知道类的结构,可以直接使用类的方法和字段。但有些时候,我们可能希望在程序运行时,根据一些条件来决定使用哪个类,或者根据类的信息执行一些操作。这时候,反射就派上用场了。
简单来说,反射允许我们在程序运行时动态地了解和操作类的信息,比如创建对象、调用方法、访问字段等。这样我们就能够在不预先知道类结构的情况下,通过代码来处理和使用类。反射提供了一种动态性,但也需要注意使用时可能带来的性能损耗和一些潜在的安全问题。

其主要作用表现为:
动态加载类:
允许在运行时根据条件动态加载类,而不需要在编译时确定要加载的确切类。
动态创建对象:
允许在运行时通过类名创建对象实例,而不需要在编译时知道确切的类。
获取类的信息:
提供了获取类的各种信息(类名、字段、方法等)的能力,使得在运行时可以动态地了解类的结构。
调用方法:
允许在运行时通过方法名调用类的方法,包括私有方法。
访问和修改字段:
允许在运行时访问和修改类的字段,包括私有字段。
实现通用框架和库:
一些通用的框架和库,如 Spring 框架,利用反射处理用户定义的类和对象,提供了高度的灵活性和可扩展性。
注解处理:
允许在运行时获取类上的注解信息,并根据注解执行相应的逻辑。
动态代理:
提供了实现动态代理的能力,使得可以在运行时创建代理对象并拦截对这些代理对象的方法调用。

AOP的作用:
AOP 是一种编程范式,旨在通过将横切关注点(cross-cutting concerns)从主要业务逻辑中分离出来,使代码更模块化、易维护。横切关注点是那些涉及多个模块、不容易用传统的面向对象方法解决的问题,比如日志记录、事务管理、性能优化等。
在 AOP 中,横切关注点被封装成一个切面(Aspect),而切面是一组连接点(Join Point)和通知(Advice)的集合。连接点是在应用执行过程中可以插入切面的点,通知是在连接点上执行的代码。AOP 提供了一种将切面与主要业务逻辑分开的方式,使代码更清晰,易于维护。
AOP与反射的关联:
在某些 AOP 框架中,反射被用于实现切面的动态织入。织入是将切面与应用的主要业务逻辑结合的过程。AOP 框架通过使用反射来动态创建代理对象,将切面的代码插入到连接点上,从而实现在运行时对应用进行横切关注点的处理。
综合来看,AOP 与反射可以协同工作,通过反射来实现 AOP 中的横切关注点的动态织入。反射提供了在运行时获取和操作类信息的能力,而 AOP 则通过切面的概念将这些横切关注点模块化,使代码更易于维护和理解。

代码实现

新建注解

/*** 自定义注解 用于标识某个方法需要进行功能字段自动填充处理*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {//数据库操作类型 update insertOperationType 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) throws NoSuchMethodException {//进行公共字段赋值log.info("开始进行公共字段填充");//需要获取到当前被拦截方法数据库的操作类型MethodSignature signature = (MethodSignature) joinPoint.getSignature();//方法签名对象AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获得方法上的注解对象OperationType operationType = autoFill.value();//获得数据库操作类型//获取到当前被拦截方法的实体对象Object[] args = joinPoint.getArgs();if (args == null || args.length == 0){return;}Object entity = args[0];//准备赋值数据LocalDateTime now = LocalDateTime.now();Long currentId = BaseContext.getCurrentId();//根据当前不同操作类型,为对应属性赋值(利用反射)if(operationType == OperationType.INSERT){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){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();}}}
}

给各Mapper接口加上定义的注解

    @AutoFill(value = OperationType.UPDATE)void update(Employee employee);@Insert("insert into employee(name, username, password, phone, sex, id_number, status, create_time, update_time, create_user, update_user) " +"values " +"(#{name},#{username},#{password},#{phone},#{sex},#{idNumber},#{status},#{createTime},#{updateTime},#{createUser},#{updateUser})")@AutoFill(value = OperationType.INSERT)void insert(Employee employee);    

另外对应前文在各Service实现类里为公共字段赋值语句全部可以省略,自此完成该功能。

新增菜品

需求分析和设计

在这里插入图片描述
在这里插入图片描述

接口设计

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码开发

开发文件上传接口

使用阿里云储存桶进行开发。
在这里插入图片描述
在application.yml中设置

sky:jwt:# 设置jwt签名加密时使用的秘钥admin-secret-key: itcast# 设置jwt过期时间admin-ttl: 72000000# 设置前端传递过来的令牌名称admin-token-name: tokenalioss:access-key-id: ${sky.alioss.access-key-id}access-key-secret: ${sky.alioss.access-key-secret}bucket-name: ${sky.alioss.bucket-name}endpoint: ${sky.alioss.endpoint}

在application-dev中配置具体参数(自己的OSS信息)

  alioss:endpoint: oss-cn-nanjing.aliyuncs.comaccess-key-id: xxxxxxxaccess-key-secret: xxxxxxxbucket-name: xxxxxxxx
@Component
@ConfigurationProperties(prefix = "sky.alioss")
@Data
public class AliOssProperties {private String endpoint;private String accessKeyId;private String accessKeySecret;private String bucketName;}
/*** 配置类,用于创建AliOssUtil对象*/
@Configuration
@Slf4j
public class OssConfiguration {@Beanpublic AliOssUtil aliOssUtil(AliOssProperties aliOssProperties){log.info("开始创建阿里云上传工具对象:{}",aliOssProperties);return new AliOssUtil(aliOssProperties.getEndpoint(),aliOssProperties.getAccessKeyId(),aliOssProperties.getAccessKeySecret(),aliOssProperties.getBucketName());}
}

这两段代码逻辑
OssConfiguration 这个配置类的主要作用是创建并配置 AliOssUtil 这个 Bean 对象,为其构造函数所需的参数赋值。具体来说:

  • 通过 @Bean 注解,aliOssUtil 方法被标记为一个 Bean 的生产方法,它会被 Spring 容器调用,用于创建一个 AliOssUtil 的实例。
  • 方法的参数 AliOssProperties aliOssProperties 是通过依赖注入的方式得到的,Spring 容器会负责注入 AliOssProperties 对象。这个对象是通过 @ConfigurationProperties 注解读取配置文件中的属性,并映射到 AliOssProperties 类的实例中。
  • 在方法体中,通过 aliOssProperties 对象获取了一些配置信息,然后使用这些信息创建了 AliOssUtil 的实例,并返回给 Spring 容器。
    所以,你可以把这个配置类看作是一个对象的装配工厂,负责创建和配置应用程序中需要用到的对象(这里是 AliOssUtil),并将这些对象注册到 Spring 容器中,方便其他地方进行使用。

新建一个Conroller层

package com.sky.controller.admin;import com.sky.result.Result;
import com.sky.utils.AliOssUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.UUID;/*** 通用接口*/
@RestController
@RequestMapping("/admin/common")
@Api(tags = "通用接口")
@Slf4j
public class CommonController {@Autowiredprivate AliOssUtil aliOssUtil;/*** 文件上传* @param file* @return*/@ApiOperation("文件上传")@PostMapping("/upload")public Result<String> upload(MultipartFile file){log.info("文件上传,{}",file);try {//原始文件名String originalFilename = file.getOriginalFilename();//截取原始文件后缀String extension = originalFilename.substring(originalFilename.lastIndexOf("."));//构造新文件名称String objectName=UUID.randomUUID().toString()+extension;//文件请求路径String filePath = aliOssUtil.upload(file.getBytes(), objectName);return Result.success(filePath);} catch (IOException e) {log.error("文件上传失败:{}",e);}return Result.success();}}

功能开发

新建DishController

package com.sky.controller.admin;import com.sky.dto.DishDTO;
import com.sky.result.Result;
import com.sky.service.DishService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;/*** 菜品管理*/
@RestController
@RequestMapping("/admin/dish")
@Slf4j
@Api(tags = "菜品相关接口")
public class DishController {@Autowiredprivate DishService dishService;/*** 新增菜品* @param dishDTO* @return*/@ApiOperation("新增菜品")@PostMappingpublic Result save(@RequestBody DishDTO dishDTO){log.info("新增菜品:{}",dishDTO);dishService.saveWithFlavor(dishDTO);return Result.success();}}

Service实现类

package com.sky.service.impl;import com.sky.dto.DishDTO;
import com.sky.entity.Dish;
import com.sky.entity.DishFlavor;
import com.sky.mapper.DishFlavorMapper;
import com.sky.mapper.DishMapper;
import com.sky.service.DishService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import java.util.List;@Service
@Slf4j
public class DishServiceImpl implements DishService {@Autowiredprivate DishMapper dishMapper;@Autowiredprivate DishFlavorMapper dishFlavorMapper;/*** 新增菜品和对应口味* @param dishDTO*/@Override@Transactionalpublic void saveWithFlavor(DishDTO dishDTO) {Dish dish=new Dish();BeanUtils.copyProperties(dishDTO, dish);//向菜品表插入一条数据并没有口味,所以不需要DTOdishMapper.insert(dish);//获取insert语句生成的主键值Long dishId=dish.getId();//向口味表插入N条数据,一道菜的口味可能有很多List<DishFlavor> flavors = dishDTO.getFlavors();if(flavors != null && flavors.size()>0){//为菜品的dishId赋值,防止后面插入空值flavors.forEach(dishFlavor -> {dishFlavor.setDishId(dishId);});//在if里面插入多条数据//sql支持批量插入dishFlavorMapper.insertBatch(flavors);}}
}

因为除了有菜品数据库还有对应的口味数据库,为解耦合需要定义两个Mapper文件。
Mapper接口类

package com.sky.mapper;import com.sky.annotation.AutoFill;
import com.sky.dto.DishDTO;
import com.sky.entity.Dish;
import com.sky.enumeration.OperationType;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
@Mapper
public interface DishMapper {/*** 根据分类id查询菜品数量* @param categoryId* @return*/@Select("select count(id) from dish where category_id = #{categoryId}")Integer CountByCategoryId(Long categoryId);/*** 插入菜品数据* @param dish*/@AutoFill(value = OperationType.INSERT)void insert(Dish dish);
}
package com.sky.mapper;import com.sky.entity.DishFlavor;
import org.apache.ibatis.annotations.Mapper;import java.util.List;@Mapper
public interface DishFlavorMapper {/*** 批量插入口味数据* @param flavors*/void insertBatch(List<DishFlavor> flavors);
}

Mapper类

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sky.mapper.DishMapper"><!--insert语句执行完后产生的主键值会赋值给dish的id--><insert id="insert" useGeneratedKeys="true" keyProperty="id">insert into 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>
</mapper>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sky.mapper.DishFlavorMapper"><insert id="insertBatch">insert into dish_flavor (dish_id, name, value) VALUES <foreach collection="flavors" item="df" separator=",">(#{df.dishId},#{df.name},#{df.value})</foreach></insert>
</mapper>

菜品分页查询

需求分析和设计

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码开发

注意联表查询

Controller层

    /*** 菜品分页查询* @param dishPageQueryDTO* @return*/@GetMapping("/page")@ApiOperation("菜品分页查询")//通过URL传参,并不是json格式故不需要加@RequestBodypublic Result<PageResult> page(DishPageQueryDTO dishPageQueryDTO){log.info("菜品分页查询:{}",dishPageQueryDTO);PageResult pageResult=dishService.pageQuery(dishPageQueryDTO);return Result.success(pageResult);}

Service实现类

    /*** 菜品分页查询* @param dishPageQueryDTO* @return*/@Overridepublic PageResult pageQuery(DishPageQueryDTO dishPageQueryDTO) {PageHelper.startPage(dishPageQueryDTO.getPage(),dishPageQueryDTO.getPageSize());Page<DishVO> page = dishMapper.pageQuery(dishPageQueryDTO);return new PageResult(page.getTotal(),page.getResult());}

Mapper层以及xml

    /*** 菜品分页查询* @param dishPageQueryDTO* @return*/Page<DishVO> pageQuery(DishPageQueryDTO dishPageQueryDTO);
    <select id="pageQuery" resultType="com.sky.vo.DishVO">SELECT d.*,c.`name` as categoryName FROM dish d LEFT OUTER JOIN category c on d.category_id = c.id<where><if test="name != null">and d.name like concat('%',#{name},'%')</if><if test="categoryId != null">and d.category_id = #{categoryId}</if><if test="status != null">and d.status = #{status}</if></where>order by d.create_time desc</select>

在此复习一下左外连接的链表查询

左外连接(Left Outer Join)是一种数据库查询操作,用于联接两个表并返回左表中的所有行以及与右表中匹配的行。如果在右表中找不到匹配的行,那么结果集中将包含右表中的列,但是这些列的值将为NULL。

SELECT *
FROM 表A
LEFT OUTER JOIN 表B ON 表A.共有列 = 表B.共有列;

在这里,共有列 是两个表中用于匹配的列。左外连接会返回表A中的所有行,同时匹配的表B中的行,如果没有匹配的行,那么右表中的列将包含NULL。

例如,假设有两个表 学生表 和 成绩表:

学生表 (Students):

学生ID姓名
1小明
2小红
3小刚

成绩表 (Grades):

学生ID科目成绩
1数学90
1英语85
3数学95

使用左外连接,可以得到一个包含所有学生以及其对应成绩(如果有的话)的结果集:

SELECT *
FROM 学生表
LEFT OUTER JOIN 成绩表 ON 学生表.学生ID = 成绩表.学生ID;

左外连接结果集:

学生表.学生ID学生表.姓名成绩表.学生ID成绩表.科目成绩表.成绩
1小明1数学90
1小明1英语85
2小红NULLNULLNULL
3小刚3数学95

结果集将包含所有学生,以及他们的成绩(如果有的话)。对于没有成绩的学生,成绩表中的列将为NULL。

菜品删除功能

需求分析与设计

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

代码实现

Controller层

    /*** 菜品批量删除* @param ids* @return* @RequestParam 用于从HTTP请求中提取参数的注解*/@DeleteMapping@ApiOperation("菜品批量删除")public Result delete(@RequestParam List<Long> ids){log.info("菜品批量删除:{}", ids);dishService.deleteBatch(ids);return Result.success();}

Service实现类

    /** @Transactional 保证事务一致性* 菜品批量删除* @param ids*/@Override@Transactionalpublic void deleteBatch(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.getSetmealIdsByDishIds(ids);if(setmealIds!=null && setmealIds.size() > 0){//当前菜品被套餐关联了就不能删除throw new DeletionNotAllowedException(MessageConstant.DISH_BE_RELATED_BY_SETMEAL);}//删除菜品表中的菜品数据for (Long id: ids) {dishMapper.deleteById(id);//删除菜品中关联的口味数据dishFlavorMapper.deleteDishId(id);}}

由于要关联菜品与套餐表需要多个Mapper

@Mapper
public interface SetmealDishMapper {/*** 根据菜品id来查套餐id* @param dishIds* @return*///select setmeal_id from setmeal_dish where dish_id in (1,2,3,4)List<Long> getSetmealIdsByDishIds(List<Long> dishIds);
}
public interface DishFlavorMapper {/*** 批量插入口味数据* @param flavors*/void insertBatch(List<DishFlavor> flavors);/*** 根据菜品Id删除对应口味数据* @param dishId*/@Delete("delete from dish_flavor where dish_id = #{dishId}")void deleteDishId(Long dishId);
}
    /*** 根据主键查询菜品数据* @param id* @return*/@Select("select * from dish where id = #{id}")Dish getById(Long id);/*** 根据主键删除菜品数据* @param id*/@Delete("delete from dish where id = #{id}")void deleteById(Long id);
@Mapper
public interface DishMapper {/*** 根据主键查询菜品数据* @param id* @return*/@Select("select * from dish where id = #{id}")Dish getById(Long id);/*** 根据主键删除菜品数据* @param id*/@Delete("delete from dish where id = #{id}")void deleteById(Long id);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sky.mapper.SetmealDishMapper"><select id="getSetmealIdsByDishIds" resultType="java.lang.Long">select setmeal_id from setmeal_dish where dish_id in<foreach collection="dishIds" item="dishId" separator="," open="(" close=")">#{dishId}</foreach></select>
</mapper>

代码优化

因为在删除时每次要执行两条删除的SQL语句,性能不佳,如果数量过多可能会造成卡顿
修改Service实现类

        //删除菜品表中的菜品数据
//        for (Long id: ids) {
//            dishMapper.deleteById(id);
//            //删除菜品中关联的口味数据
//            dishFlavorMapper.deleteDishId(id);
//        }//根据菜品id集合批量删除菜品数据//sql: delete from dish where id in (?,?,?)dishMapper.deleteByIds(ids);//根据菜品id集合批量删除关联的口味数据//sql: delete from dish_flavor where dish_id in (?,?,?)dishFlavorMapper.deleteByDishIds(ids);

修改Mapper对应的xml文件

    <delete id="deleteByIds">delete from dish where id in<foreach collection="ids" open="(" close=")" separator="," item="id">#{id}</foreach></delete>
    <delete id="deleteByDishIds">delete from dish_flavor where dish_id<foreach collection="dishIds" open="(" close=")" separator="," item="dishId">#{dishId}</foreach></delete>

修改菜品

需求分析和设计

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现

Controller层

    /*** 根据id查询菜品* @param id* @return*/@GetMapping("/{id}")@ApiOperation("根据id查询菜品")public Result<DishVO> getById(@PathVariable Long id){log.info("根据id查询菜品:{}",id);DishVO dishVO = dishService.getByIdWithFlavor(id);return Result.success(dishVO);}/*** 修改菜品* @param dishDTO* @return*/@PutMapping@ApiOperation("修改菜品")public Result update(@RequestBody DishDTO dishDTO){log.info("修改菜品:{}",dishDTO);dishService.updateWithFlavor(dishDTO);return Result.success();}

Service层实现类

   /*** 根据id查询菜品和对应口味数据* @param id* @return*/@Overridepublic DishVO getByIdWithFlavor(Long id) {//根据id查询到菜品数据Dish dish = dishMapper.getById(id);//根据菜品id查询到口味数据List<DishFlavor> dishFlavors = dishFlavorMapper.getByDishId(id);//将查询到的数据封装到VODishVO dishVO = new DishVO();BeanUtils.copyProperties(dish, dishVO);dishVO.setFlavors(dishFlavors);return dishVO;}/*** 根据id修改菜品基本信息和口味信息* @param dishDTO*/@Overridepublic void updateWithFlavor(DishDTO dishDTO) {//修改菜品表基本信息Dish dish = new Dish();BeanUtils.copyProperties(dishDTO, dish);dishMapper.update(dish);//删除原有的口味数据dishFlavorMapper.deleteDishId(dishDTO.getId());//重新插入口味数据List<DishFlavor> flavors = dishDTO.getFlavors();if(flavors != null && flavors.size() > 0){flavors.forEach(dishFlavor -> {dishFlavor.setDishId(dishDTO.getId());});//向口味表插入n条数据dishFlavorMapper.insertBatch(flavors);}}

Mapper层

    /*** 根据菜品id查询对应的口味数据* @param dishId* @return*/@Select("select * from dish_flavor where dish_id = #{dishId}")List<DishFlavor> getByDishId(Long dishId);
    /*** 根据id来动态修改菜品* @param dish*/@AutoFill(value = OperationType.UPDATE)void update(Dish dish);
    <update id="update">update dish<set><if test="name != null">name = #{name},</if><if test="categoryId != null">category_id  = #{categoryId},</if><if test="price != null">price = #{price},</if><if test="image != null">image = #{image},</if><if test="description != null">description = #{description},</if><if test="status != null">status = #{status},</if><if test="updateTime != null">update_time = #{updateTime},</if><if test="updateUser != null">update_user = #{updateUser},</if></set>where id = #{id}</update>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/607375.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

静态网页设计——崩坏3(HTML+CSS+JavaScript)

前言 声明&#xff1a;该文章只是做技术分享&#xff0c;若侵权请联系我删除。&#xff01;&#xff01; 感谢大佬的视频&#xff1a; 使用技术&#xff1a;HTMLCSSJS&#xff08;静态网页设计&#xff09; 主要内容&#xff1a;对游戏崩坏3进行简单介绍。 https://www.bilib…

【Linux进程】查看进程fork创建进程

目录 前言 1. 查看进程 2. 通过系统调用创建进程-fork初识 总结 前言 你有没有想过在使用Linux操作系统时&#xff0c;后台运行的程序是如何管理的&#xff1f;在Linux中&#xff0c;进程是一个非常重要的概念。本文将介绍如何查看当前运行的进程&#xff0c;并且讨论如何使用…

将项目同时提交到GitHub和码云Gitee上面,GitHub与Gitee同步

多个远程仓库同时使用 新建GitHub仓库 创建成功 在终端中创建仓库 如果你想在本地机器上创建Git仓库&#xff0c;或者想添加一个文件夹或文件到已经存在的Git仓库中&#xff0c;你应该在终端中创建你的Git仓库。在你可以通过终端来创建一个Git仓库。以下是在终端中创建Git仓…

java解析json复杂数据的第三种思路

文章目录 一、概述二、数据预览1. 接口json数据2. json转xml数据 三、代码实现1. pom.xml2. 核心代码3. 运行结果 四、源码传送 一、概述 接上篇 java解析json复杂数据的两种思路 我们已经通过解析返回json字符串得到数据,现在改变思路, 按照如下流程获取数据: #mermaid-svg-k…

AcrelCloud-3000环保用电监管云平台解决方案——安科瑞赵嘉敏

概述 国家全面推进打赢蓝天保卫战&#xff0c;打好碧水保卫战&#xff0c;打胜净土保卫战&#xff0c;加快生态环境保护、建设美丽中国。环保用电监管系统针对企业内的环保设施、设备运行状况进行检测&#xff0c;发挥环保设备的作用&#xff0c;提高监察效率&#xff0c;并为…

洛谷 P1217 [USACO1.5] 回文质数 Prime Palindromes 刷题笔记

P1217 [USACO1.5] 回文质数 Prime Palindromes - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 思路 直接枚举 减枝优化判断 优化1 只有偶数才会是质数 优化2 回文数的判断次数要优于检查素数 先判断是否为回文数再检查是否为质数 if( hw(i)&&isprime(i)) 这里…

Springcloud Gateway网关

简介 SpringCloud Gateway是基于WebFlux框架实现的&#xff0c;而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty。 Spring Cloud Gateway的目标提供统一的路由方式且基于 Filter链的方式提供了网关基本的功能&#xff0c;例如:安全&#xff0c;监控/指标&#xf…

lv14 IO模型:阻塞和非阻塞 7

1 五种IO模型------读写外设数据的方式 阻塞: 不能操作就睡觉 非阻塞&#xff1a;不能操作就返回错误(通过轮询即才能实现阻塞的情况 &#xff09; 多路复用&#xff1a;委托中介监控 信号驱动&#xff1a;让内核如果能操作时发信号&#xff0c;在信号处理函数中操作 异步IO&a…

ES6(一部分)未完...

文章目录 ES61.ES6 let声明变量2.ES6 const声明常量3.变量解构赋值3-1解构对象3-2解构数组3-3字符串解构 4.模板字符串5.字符串扩展5-1 include函数5-2 repeat函数&#xff08;重复&#xff09; 6.数值扩展6-1二进制和八进制表示法6-2isFinite 与 isNaN方法6-3islnteger方法6-4…

Vue3+TS+Vite 构建自动导入开发环境

关注⬆️⬆️⬆️⬆️ 专栏后期更新更多前端内容 在一个使用 Vue 3、Vite 和 TypeScript 的项目中,配置 unplugin-auto-import 和 unplugin-vue-components 插件可以极大地提高开发效率,因为它们可以自动导入 Vue 相关的 API 和 Vue 组件,从而减少了手动导入的需要。 文章目…

FPGA UDP协议栈:基于88E1111,支持RGMII、GMII、SGMII三种模式,提供3套工程源码和技术支持

目录 1、前言免责声明 2、相关方案推荐我这里已有的以太网方案本协议栈的 1G-UDP版本本协议栈的 10G-UDP版本本协议栈的 25G-UDP版本1G 千兆网 TCP-->服务器 方案1G 千兆网 TCP-->客户端 方案10G 万兆网 TCP-->服务器客户端 方案 3、该UDP协议栈性能4、详细设计方案设…

Hyperledger Fabric 消息协议

Fabric 中大量采用了 gRPC 消息在不同组件之间进行通信交互&#xff0c;主要包括如下几种情况&#xff1a;客户端访问 Peer 节点&#xff0c;客户端和 Peer 节点访问排序节点&#xff0c;链码容器与 Peer 节点交互&#xff0c;以及多个 Peer 节点之间的 Gossip 交互。 消息结构…

Android 架构 - 模块化

参考文章 谷歌官方指南 一、概念 将大型、复杂问题拆解成一个个小的、简单问题&#xff0c;从而可以做到各个击破。模块化简单讲就是把多功能高耦合的代码逻辑拆散成多个功能单一职责明确的模块。模块指 Android 项目中的 module&#xff0c;通常会包含 Gradle 构建脚本、源代…

【Android Studio】创建第一个APP工程及生成APK安装包

&#x1f31f;博主领域&#xff1a;嵌入式领域&人工智能&软件开发 前言&#xff1a;本文详细介绍创建Android Studio第一个APP工程及打包生成APK安装包。 如下两个博客我记录了第一次创建项目时出现的问题&#xff0c;若你也遇见了同样的问题&#xff0c;可参考&#…

QT上位机开发(会员充值软件)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 所有的控件当中&#xff0c;除了label、edit、radio、combobox和button之外&#xff0c;另外一个用的比较多的控件就是grid&#xff0c;也可称之为…

数学的雨伞下:理解世界的乐趣

这本书没有一个公式&#xff0c;却讲透了数学的本质&#xff01; 《数学的雨伞下&#xff1a;理解世界的乐趣》。一本足以刷新观念的好书&#xff0c;从超市到对数再到相对论&#xff0c;娓娓道来。对于思维空间也给出了一个更容易理解的角度。 作者&#xff1a;米卡埃尔•洛奈…

WindowsServer安装mysql最新版

安装 下载相应mysql安装包&#xff1a; MySQL :: Download MySQL Installer 选择不登陆下载 双击运行下载好的mysql-installer-community-*.*.*.msi 进入类型选择页面&#xff0c;本人需要mysql云服务就选择了server only server only&#xff08;服务器&#xff09;&#x…

day11 有效的括号 删除字符串中的所有相邻重复项 逆波兰表达式求值

题目1&#xff1a;20 有效的括号 题目链接&#xff1a;20 有效的括号 题意 判断字符串是否有效&#xff0c;若有效&#xff1a; 1&#xff09;左括号必须用相应的右括号 2&#xff09;左括号的闭合顺序正确 ({)}顺序不正确&#xff0c;应该是&#xff08;{}&#xff09; …

如何使用GaussDB创建脱敏策略(MASKING POLICY)

目录 一、前言 二、GaussDB中的脱敏策略 1、数据脱敏的定义 2、创建脱敏策略的语法说明 三、在GaussDB中如何创建数据脱敏策略(示例) 1、创建脱敏策略的一般步骤 2、GaussDB数据库中创建脱敏策略的完整示例 1&#xff09;开启安全策略开关&#xff0c;以初识用户omm登录…

Qt/QML编程学习之心得:一个音频播放器的实现(29)

在window下&#xff0c;打开音乐播放器&#xff0c;然后打开一个.mp3文件&#xff0c;就可以实现播放了&#xff0c;那么在Qt/QML中如何实现呢&#xff1f;首先所有的设计都是基于音乐播放器的&#xff0c;嵌入式linux下同样也有音乐播放器&#xff0c;比如mplayer。其调用方法…