技术选型
配置属性类 将application.yml中的属性配置封装成一个对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package com.sky.properties; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @ConfigurationProperties(prefix = "sky.jwt") @Data public class JwtProperties { private String adminSecretKey; private long adminTtl; private String adminTokenName; private String userSecretKey; private long userTtl; private String userTokenName; }
然后再将这个对象注入到controller对象中
Builder 在EmployeeLoginV0类中添加注解@Builder 在EmployeeLoginV0类中添加注解@Builder
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package com.sky.vo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; @Data @Builder @NoArgsConstructor @AllArgsConstructor @ApiModel(description = "员工登录返回的数据格式") public class EmployeeLoginVO implements Serializable { @ApiModelProperty("主键值") private Long id; @ApiModelProperty("用户名") private String userName; @ApiModelProperty("姓名") private String name; @ApiModelProperty("jwt令牌") private String token; }
前端发送的请求是如何请求到后端服务的 使用nginx方向代理的好处
提高访问速度
进行负载均衡(将请求均衡的分配给集群中的服务器)
保证后端服务安全
nginx反向代理配置方式
nginx负载均衡配置
使用MD5加密 DigestUtils.md5DigestAsHex
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public Employee login (EmployeeLoginDTO employeeLoginDTO) { String username = employeeLoginDTO.getUsername(); String password = employeeLoginDTO.getPassword(); Employee employee = employeeMapper.getByUsername(username); if (employee == null ) { throw new AccountNotFoundException (MessageConstant.ACCOUNT_NOT_FOUND); } password = DigestUtils.md5DigestAsHex(password.getBytes()); if (!password.equals(employee.getPassword())) { throw new PasswordErrorException (MessageConstant.PASSWORD_ERROR); } if (employee.getStatus() == StatusConstant.DISABLE) { throw new AccountLockedException (MessageConstant.ACCOUNT_LOCKED); } return employee; }
前后端开发流程
Swagger ** 在配置类中加入Knife4j的配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Bean public Docket docket1 () { log.info("准备生成接口文档..." ); ApiInfo apiInfo = new ApiInfoBuilder () .title("苍穹外卖项目接口文档" ) .version("2.0" ) .description("苍穹外卖项目接口文档" ) .build(); Docket docket = new Docket (DocumentationType.SWAGGER_2) .groupName("管理端接口" ) .apiInfo(apiInfo) .select() .apis(RequestHandlerSelectors.basePackage("com.sky.controller.admin" )) .paths(PathSelectors.any()) .build(); return docket; }
设置静态资源映射,否则接口文档页面无法访问
1 2 3 4 5 6 7 8 9 protected void addResourceHandlers (ResourceHandlerRegistry registry) { log.info("开始设置静态资源映射..." ); registry.addResourceHandler("/doc.html" ).addResourceLocations("classpath:/META-INF/resources/" ); registry.addResourceHandler("/webjars/**" ).addResourceLocations("classpath:/META-INF/resources/webjars/" ); }
Yapi的区别
Swagger注解方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 @RestController @RequestMapping("/admin/employee") @Slf4j @Api(tags = "员工相关接口") public class EmployeeController { @Autowired private EmployeeService employeeService; @Autowired private JwtProperties jwtProperties; @PostMapping("/login") @ApiOperation(value = "员工登录") public Result<EmployeeLoginVO> login (@RequestBody EmployeeLoginDTO employeeLoginDTO) { log.info("员工登录:{}" , employeeLoginDTO); Employee employee = employeeService.login(employeeLoginDTO); Map<String, Object> claims = new HashMap <>(); claims.put(JwtClaimsConstant.EMP_ID, employee.getId()); String token = JwtUtil.createJWT( jwtProperties.getAdminSecretKey(), jwtProperties.getAdminTtl(), claims); EmployeeLoginVO employeeLoginVO = EmployeeLoginVO.builder() .id(employee.getId()) .userName(employee.getUsername()) .name(employee.getName()) .token(token) .build(); return Result.success(employeeLoginVO); }
1 2 3 4 5 6 7 8 9 10 11 @Data @ApiModel(description = "员工登录时传递的数据模型") public class EmployeeLoginDTO implements Serializable { @ApiModelProperty("用户名") private String username; @ApiModelProperty("密码") private String password; }
用户属性拷贝 Employee
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 package com.sky.entity; import com.fasterxml.jackson.annotation.JsonFormat; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; import java.time.LocalDateTime; @Data @Builder @NoArgsConstructor @AllArgsConstructor public class Employee implements Serializable { private static final long serialVersionUID = 1L ; private Long id; private String username; private String name; private String password; private String phone; private String sex; private String idNumber; private Integer status; private LocalDateTime createTime; private LocalDateTime updateTime; private Long createUser; private Long updateUser; }
前端传来的数据封装EmployeeDTO
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.sky.dto; import lombok.Data; import java.io.Serializable; @Data public class EmployeeDTO implements Serializable { private Long id; private String username; private String name; private String phone; private String sex; private String idNumber; }
属性拷贝
1 2 BeanUtils.copyProperties(employeeDTO, employee);
设置新增用户修改人ID 流程图
设置修改人id 解析出登录员工id后,如何传递给Service的save方法? 答: 使用TreadLocal #
员工分页查询 使用pagehelper插件
Controller层 最后返回Result的一个泛型
1 2 3 4 5 6 public static <T> Result<T> success (T object) { Result<T> result = new Result <T>(); result.data = object; result.code = 1 ; return result; }
1 2 3 4 5 6 7 8 9 10 11 12 @GetMapping("/page") @ApiOperation("员工分页查询") public Result<PageResult> page (EmployeePageQueryDTO employeePageQueryDTO) { log.info("员工分页查询,参数为:{}" , employeePageQueryDTO); PageResult pageResult = employeeService.pageQuery(employeePageQueryDTO); return Result.success(pageResult); }
PageResult @AllArgsConstructor有参构造方法 @NoArgsConstructor无参构造方法
1 2 3 4 5 6 7 8 9 10 11 12 13 @Data @AllArgsConstructor @NoArgsConstructor public class PageResult implements Serializable { private long total; private List records; }
Service 层 pageSize就是那个插件,startPage指从哪一页开始,查询多少条数据,然后动态添加到sql语句中,limit x,y mapper层最后返回的也是Employee
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public PageResult pageQuery (EmployeePageQueryDTO employeePageQueryDTO) { PageHelper.startPage(employeePageQueryDTO.getPage(), employeePageQueryDTO.getPageSize()); Page<Employee> page = employeeMapper.pageQuery(employeePageQueryDTO); long total = page.getTotal(); List<Employee> records = page.getResult(); return new PageResult (total, records); }
Mapper层 没有使用注解方式,而是使用的映射文件
1 2 3 4 5 6 Page<Employee> pageQuery (EmployeePageQueryDTO employeePageQueryDTO) ;
查询得到的操作时间格式修正
在属性上加入注解,对日期进行格式化1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 package com.sky.entity; import com.fasterxml.jackson.annotation.JsonFormat; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; import java.time.LocalDateTime; @Data @Builder @NoArgsConstructor @AllArgsConstructor public class Employee implements Serializable { private static final long serialVersionUID = 1L ; private Long id; private String username; private String name; private String password; private String phone; private String sex; private String idNumber; private Integer status; private LocalDateTime createTime; private LocalDateTime updateTime; private Long createUser; private Long updateUser; }
在WebMvcConfiguration中扩展Spring MVC的消息转换器,统一对日期类型进行格式化处理1 2 3 4 5 6 7 8 9 10 11 12 13 protected void extendMessageConverters (List<HttpMessageConverter<?>> converters) { log.info("扩展消息转换器..." ); MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter (); converter.setObjectMapper(new JacksonObjectMapper ()); converters.add(0 ,converter); }
启用禁用员工账号
Controller层 status在路径上传参,添加注解@PathVariable 只需要返回code即可,data没有数据,Result不用写泛型
1 2 3 4 5 6 7 8 9 10 11 12 13 @PostMapping("/status/{status}") @ApiOperation("启用禁用员工账号") public Result startOrStop (@PathVariable Integer status,Long id) { log.info("启用禁用员工账号:{},{}" ,status,id); employeeService.startOrStop(status,id); return Result.success(); }
Service层 重新构造一个Employee实体,Employee实体中添加了@Builder注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public void startOrStop (Integer status, Long id) { Employee employee = Employee.builder() .status(status) .id(id) .build(); employeeMapper.update(employee); }
Mapper层 1 2 3 4 5 6 @AutoFill(value = OperationType.UPDATE) void update (Employee employee) ;
使用mapper层映射文件,动态修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <update id ="update" parameterType ="Employee" > update employee <set > <if test ="name != null" > name = #{name},</if > <if test ="username != null" > username = #{username},</if > <if test ="password != null" > password = #{password},</if > <if test ="phone != null" > phone = #{phone},</if > <if test ="sex != null" > sex = #{sex},</if > <if test ="idNumber != null" > id_Number = #{idNumber},</if > <if test ="updateTime != null" > update_Time = #{updateTime},</if > <if test ="updateUser != null" > update_User = #{updateUser},</if > <if test ="status != null" > status = #{status},</if > </set > where id = #{id} </update >
公共字段填充 这里的公共字段填充是填充到比如下面的Employee实体的属性中,不是sql语句时候临时添加
自定义注解AutoFill,用于标识需要进行公共字段自动填充的方法
自定义切面类AutoFillAspect,统一拦截加入了AutoFill注解的方法,通过反射为公共字段赋值
在Mapper的方法上加入AutoFill注解
注解AutoFill,@Target用于只对方法起作用 用于指定被该注解修饰的注解要保留多久。在这种情况下,RetentionPolicy.RUNTIME
意味着被该注解修饰的注解在运行时仍然可用,可以通过反射等机制访问
1 2 3 4 5 6 7 8 9 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface AutoFill { OperationType value () ; }
这里的OperationType是一个操作枚举类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public enum OperationType { UPDATE, INSERT }
定义切入点以及通知,这个切入类也是容器内的一个组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 @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("开始进行公共字段自动填充..." ); 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文件中所有的insert、update方法上添加@AutoFill注解
1 2 3 4 5 @Insert("insert into 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})") @AutoFill(value = OperationType.INSERT) void insert (Employee employee) ;
例子
1 2 3 4 5 6 7 8 9 @Insert("insert into 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})") @AutoFill(value = OperationType.INSERT) void insert (Employee employee) ;
上传文件 使用阿里云Bucket工具桶存储,ali属性配置类
1 2 3 4 5 6 7 8 9 10 11 @Component @ConfigurationProperties(prefix = "sky.alioss") @Data public class AliOssProperties { private String endpoint; private String accessKeyId; private String accessKeySecret; private String bucketName; }
属性值都是yml文件提供,这里的值则是dev开发环境中的yml文件提供,这样便可解耦调整环境
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 spring: profiles: active: dev sky: jwt: admin-secret-key: itcast admin-ttl: 7200000 admin-token-name: token user-secret-key: itheima user-ttl: 7200000 user-token-name: authentication alioss: endpoint: ${sky.alioss.endpoint} access-key-id: ${sky.alioss.access-key-id} access-key-secret: ${sky.alioss.access-key-secret} bucket-name: ${sky.alioss.bucket-name}
dev.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 sky: datasource: driver-class-name: com.mysql.cj.jdbc.Driver host: localhost port: 3306 database: sky_take_out username: root password: root alioss: endpoint: oss-cn-beijing.aliyuncs.com access-key-id: your-access-key-id access-key-secret: your-access-key-secret bucket-name: your-bucket-name
alie有专门提供的util类,初始化实例并请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 @Data @AllArgsConstructor @Slf4j public class AliOssUtil { private String endpoint; private String accessKeyId; private String accessKeySecret; private String bucketName; public String upload (byte [] bytes, String objectName) { OSS ossClient = new OSSClientBuilder ().build(endpoint, accessKeyId, accessKeySecret); try { ossClient.putObject(bucketName, objectName, new ByteArrayInputStream (bytes)); } catch (OSSException oe) { System.out.println("Caught an OSSException, which means your request made it to OSS, " + "but was rejected with an error response for some reason." ); System.out.println("Error Message:" + oe.getErrorMessage()); System.out.println("Error Code:" + oe.getErrorCode()); System.out.println("Request ID:" + oe.getRequestId()); System.out.println("Host ID:" + oe.getHostId()); } catch (ClientException ce) { System.out.println("Caught an ClientException, which means the client encountered " + "a serious internal problem while trying to communicate with OSS, " + "such as not being able to access the network." ); System.out.println("Error Message:" + ce.getMessage()); } finally { if (ossClient != null ) { ossClient.shutdown(); } } stringBuilder .append(bucketName) .append("." ) .append(endpoint) .append("/" ) .append(objectName); log.info("文件上传到:{}" , stringBuilder.toString()); return stringBuilder.toString(); } }
为了创建Util类,需要添加一个配置文件,项目启动时便可以创建类 @ConditionalOnMissingBean是指只有当缺失此类bean的时候创建,也就是spring容器中只会有一个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Configuration @Slf4j public class OssConfiguration { @Bean @ConditionalOnMissingBean public AliOssUtil aliOssUtil (AliOssProperties aliOssProperties) { log.info("开始创建阿里云文件上传工具类对象:{}" ,aliOssProperties); return new AliOssUtil (aliOssProperties.getEndpoint(), aliOssProperties.getAccessKeyId(), aliOssProperties.getAccessKeySecret(), aliOssProperties.getBucketName()); } }
Controller接口 使用UUID格式,将原始文件名取后缀和UUID拼接
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 @RestController @RequestMapping("/admin/common") @Api(tags = "通用接口") @Slf4j public class CommonController { @Autowired private AliOssUtil aliOssUtil; @PostMapping("/upload") @ApiOperation("文件上传") 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.error(MessageConstant.UPLOAD_FAILED); } }
插入口味 service层 使用@Transactional 添加事务注解,那么启动项需要添加@EnableTransactionManagement
1 2 3 4 5 6 7 8 9 10 11 @SpringBootApplication @EnableTransactionManagement @Slf4j @EnableCaching @EnableScheduling public class SkyApplication { public static void main (String[] args) { SpringApplication.run(SkyApplication.class, args); log.info("server started" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Transactional public void saveWithFlavor (DishDTO dishDTO) { Dish dish = new Dish (); BeanUtils.copyProperties(dishDTO, dish); dishMapper.insert(dish); Long dishId = dish.getId(); List<DishFlavor> flavors = dishDTO.getFlavors(); if (flavors != null && flavors.size() > 0 ) { flavors.forEach(dishFlavor -> { dishFlavor.setDishId(dishId); }); dishFlavorMapper.insertBatch(flavors); } }
上面的代码中,由于前端没有传来dishid的值,所以我们要自己获取,insert插入后就会产生dishid,在mapper层的映射文件中,使用useGeneratedKeys=”true” keyProperty=”id”,其中的id是要赋值给dish类中的id属性;然后使用forEach逐一set id
1 2 3 <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 >
saveBatch逻辑映射文件,collection=”flavors”中的flavors是指调用mapper层时传来的集合flavors
1 dishFlavorMapper.insertBatch(flavors);
1 2 3 4 5 6 <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层映射文件 dish用d,left outer join左外连接,category用c,on指条件 如果直接查询c.name,那么查询结果会有两个name字段,返回给DishVo的时候,就会报错; 所以使用c.name as categoryName用别名
1 Page<DishVO> page = dishMapper.pageQuery(dishPageQueryDTO);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @Data @Builder @NoArgsConstructor @AllArgsConstructor public class DishVO implements Serializable { private Long id; private String name; private Long categoryId; private BigDecimal price; private String image; private String description; private Integer status; private LocalDateTime updateTime; private String categoryName; private List<DishFlavor> flavors = new ArrayList <>(); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <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 >
Controller层 方法请求参数没有加注解@RequestBody,是因为前端不是json格式,而是请求路径传参
1 2 3 4 5 6 7 8 ```java @GetMapping("/page") @ApiOperation("菜品分页查询") public Result<PageResult> page(DishPageQueryDTO dishPageQueryDTO) { log.info("菜品分页查询:{}", dishPageQueryDTO); PageResult pageResult = dishService.pageQuery(dishPageQueryDTO); return Result.success(pageResult); }
前端ids参数传递添加注解 @RequestParam是让mvc可以将前端传来的string id参数封装到list集合中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @DeleteMapping @ApiOperation("菜品批量删除") public Result delete (@RequestParam List<Long> ids) { log.info("菜品批量删除:{}" , ids); dishService.deleteBatch(ids); cleanCache("dish_*" ); return Result.success(); } xml映射文件中,使用forEach
1 2 3 4 5 6 <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 >
一些参数说明 1、这里的 @CacheEvict
注解用于清除指定缓存中的所有条目。下面是各个参数的含义:
cacheNames = "setmealCache"
:指定要清除的缓存名称为 “setmealCache”,即清除名为 “setmealCache” 的缓存中的数据。
allEntries = true
:表示清除整个缓存,而不是只清除部分条目。如果为 false
(默认),则只清除与方法参数匹配的缓存项
1 2 3 4 5 6 7 8 9 10 11 12 13 @DeleteMapping @ApiOperation("批量删除套餐") @CacheEvict(cacheNames = "setmealCache",allEntries = true) public Result delete (@RequestParam List<Long> ids) { setmealService.deleteBatch(ids); return Result.success(); }
2、这段代码是 MyBatis 中的一个 XML 映射语句,用于向数据库中插入一个 Setmeal
对象,并返回插入记录的主键值。下面是各个参数的含义:
insert
:是该 SQL 语句的唯一标识符,供 MyBatis 使用。
parameterType="Setmeal"
:指定了这个 SQL 语句的参数类型为 Setmeal
类型,即插入操作的参数是一个 Setmeal
对象。
useGeneratedKeys="true"
:表示要使用数据库的自动生成主键功能。
keyProperty="id"
:指定了自动生成的主键值要设置到 Setmeal
对象的 id
属性中。
1 2 3 <insert id="insert" parameterType="Setmeal" useGeneratedKeys="true" keyProperty="id" > insert into setmeal (category_id, name, price, status, description, image, create_time, update_time, create_user, update_user) values (#{categoryId}, #{name}, #{price}, #{status}, #{description}, #{image}, #{createTime}, #{updateTime}, #{createUser}, #{updateUser})</insert>
3、这段代码是一个 MyBatis 的 XML 映射语句,用于根据ID查询套餐(Setmeal)信息,并包含关联的菜品(Dish)信息。下面是各个参数的含义:
id
:是该 SQL 语句的唯一标识符,供 MyBatis 使用。
parameterType="long"
:指定了这个 SQL 语句的参数类型为 long
,即查询操作的参数是一个长整型的ID。
resultMap="setmealAndDishMap"
:指定了查询结果的映射规则,即如何将查询结果映射为对象。setmealAndDishMap
是一个自定义的结果映射规则,定义了如何将查询结果映射为包含套餐和菜品信息的对象。1 2 3 <select id="getByIdWithDish" parameterType="long" resultMap="setmealAndDishMap" > select a.*, b.id sd_id, b.setmeal_id, b.dish_id, b.name sd_name, b.price sd_price, b.copies from setmeal a left join setmeal_dish b on a.id = b.setmeal_id where a.id = #{id}</select>
这段代码定义了一个结果映射(resultMap),用于将查询结果映射为 SetmealVO
类型的对象,其中包含了套餐(Setmeal)和菜品(Dish)的信息。具体含义如下:
<resultMap id="setmealAndDishMap" type="com.sky.vo.SetmealVO" autoMapping="true">
:定义了一个名为 setmealAndDishMap
的结果映射,将查询结果映射为 SetmealVO
类型的对象。autoMapping="true"
表示启用自动映射,可以将数据库列名自动映射到对象属性,前提是列名和属性名相同。
<result column="id" property="id"/>
:将查询结果中名为 id
的列映射到 SetmealVO
对象的 id
属性。
<collection property="setmealDishes" ofType="SetmealDish">
:定义了一个集合属性 setmealDishes
,用于存储套餐和菜品的关联信息,其中 ofType="SetmealDish"
表示集合中元素的类型为 SetmealDish
类型。
<result column="sd_id" property="id"/>
:将查询结果中名为 sd_id
的列映射到 SetmealDish
对象的 id
属性。
<result column="setmeal_id" property="setmealId"/>
:将查询结果中名为 setmeal_id
的列映射到 SetmealDish
对象的 setmealId
属性。
<result column="dish_id" property="dishId"/>
:将查询结果中名为 dish_id
的列映射到 SetmealDish
对象的 dishId
属性。
<result column="sd_name" property="name"/>
:将查询结果中名为 sd_name
的列映射到 SetmealDish
对象的 name
属性。
<result column="sd_price" property="price"/>
:将查询结果中名为 sd_price
的列映射到 SetmealDish
对象的 price
属性。
<result column="copies" property="copies"/>
:将查询结果中名为 copies
的列映射到 SetmealDish
对象的 copies
属性。
1 2 3 4 5 6 7 8 9 10 <resultMap id="setmealAndDishMap" type="com.sky.vo.SetmealVO" autoMapping="true" > <result column="id" property="id" /> <collection property="setmealDishes" ofType="SetmealDish" > <result column="sd_id" property="id" /> <result column="setmeal_id" property="setmealId" /> <result column="dish_id" property="dishId" /> <result column="sd_name" property="name" /> <result column="sd_price" property="price" /> <result column="copies" property="copies" /> </collection></resultMap>