一、Day01-开发环境搭建
1、实体类分析
2、Git仓库
commit =》push
3、Swagger-Controller层
/** * 员工管理 */ @RestController @RequestMapping("/admin/employee") @Slf4j @Api(tags = "员工操作相关接口") public class EmployeeController { @Autowired private EmployeeService employeeService; @Autowired private JwtProperties jwtProperties; /** * 登录 * * @param employeeLoginDTO * @return */ @PostMapping("/login") @ApiOperation(value = "员工登录") public Result<EmployeeLoginVO> login(@RequestBody EmployeeLoginDTO employeeLoginDTO) { log.info("员工登录:{}", employeeLoginDTO); Employee employee = employeeService.login(employeeLoginDTO); //登录成功后,生成jwt令牌 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); } /** * 退出 * * @return */ @PostMapping("/logout") @ApiOperation(value = "员工退出") public Result<String> logout() { return Result.success(); } /** * 新增员工 * @param employeeDTO * @return */ //因为接收的是JSON格式,所以需要使用@RequestBody,将数据转换为Java对象,并完成数据绑定 @PostMapping//需要进行的是POST请求 @ApiOperation(value = "新增员工") public Result save(@RequestBody EmployeeDTO employeeDTO) { log.info("新增员工{}", employeeDTO);//自动将DTO的属性,补齐至花括号中,占位符 employeeService.save(employeeDTO); return Result.success(); } /** * 分页查询 * 此处不是JSON数据,而是Query,所以不需要@RequestBody注解来转换 * @param employeePageQueryDTO * @return */ @GetMapping("/page") @ApiOperation(value = "员工分页查询") public Result<PageResult> page(EmployeePageQueryDTO employeePageQueryDTO) { log.info("员工分页查询,参数为{}", employeePageQueryDTO); // controller层调用service层实现接口 PageResult pageResult = employeeService.pageQuery(employeePageQueryDTO); //返回结果 return Result.success(pageResult); } /** * * @param status * @param id * @return */ @PostMapping("/status/{status}") @ApiOperation("启用禁用账号") //这里需要解析路径参数 外加@PathVariable public Result startOrStop(@PathVariable Integer status,long id){ //提示信息 log.info("启用禁用员工账号:{},{}",status,id); employeeService.startOrStop(status,id); //返回无参构造方法 return Result.success(); } }
4、config
/** * 配置类,注册web层相关组件 */ @Configuration @Slf4j public class WebMvcConfiguration extends WebMvcConfigurationSupport { @Autowired private JwtTokenAdminInterceptor jwtTokenAdminInterceptor; /** * 注册自定义拦截器 * * @param registry */ protected void addInterceptors(InterceptorRegistry registry) { log.info("开始注册自定义拦截器..."); registry.addInterceptor(jwtTokenAdminInterceptor) .addPathPatterns("/admin/**") .excludePathPatterns("/admin/employee/login"); } /** * 通过knife4j生成接口文档 * @return */ @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; } /** * 设置静态资源映射 * @param registry */ 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/"); } /** * 扩展Spring MVC框架的消息转化器 */ protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) { log.info("扩展消息转换器..."); //创建一个消息转换器对象 MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); //需要为消息转换器设置一个对象转换器,对象转换器可以将Java对象序列化为Json数据 converter.setObjectMapper(new JacksonObjectMapper()); //将自己的消息转换器加入容器中 converters.add(0,converter); } }
5、处理sql异常-拦截器
/** * 处理SQL异常 */ @ExceptionHandler public Result exceptionHandler(SQLIntegrityConstraintViolationException ex){ //Duplicate entry 'zhangsan' for key 'employee.idx_username' String message = ex.getMessage(); if(message.contains("Duplicate entry")){ String[] split = message.split(" "); String username = split[2]; String msg = username + MessageConstant.ALREADY_EXISTS; return Result.error(msg); }else { return Result.error(MessageConstant.UNKNOWN_ERROR); } }
6、更新数据所携带的id
log.info("当前员工id:", empId); //JWT校验完成登录操作,已经存储了用户的相关信息,其中包括了id,那么我们后续所有修改就是基于这个id BaseContext.setCurrentId(empId);
二、Day02-功能实现
1、根据用户名查询员工
/** * 根据用户名查询员工 * @param username * @return */ @Select("select * from employee where username = #{username}") Employee getByUsername(String username);
2、员工登录
/** * 员工登录 * @param employeeLoginDTO * @return */ Employee login(EmployeeLoginDTO employeeLoginDTO);
/** * 员工登录 * * @param employeeLoginDTO * @return */ 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); } //密码比对 // TODO 后期需要进行md5加密,然后再进行比对(已完成,利用DigestUtils) 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); } //3、返回实体对象 return employee; }
3、插入员工数据
/** * 插入员工数据 */ @Insert("insert into employee (name,username,password,phone,sex,id_number,create_time,update_time,create_user,update_user)" + "values" + "(#{name},#{username},#{password},#{phone},#{sex},#{idNumber},#{createTime},#{updateTime},#{createUser},#{updateUser})") void insert(Employee employee);
/** * 新增员工 * @param employeeDTO */ void save(EmployeeDTO employeeDTO);
/** * 新增员工 * * @param employeeDTO */ @Override public void save(EmployeeDTO employeeDTO) { //搞清楚谁包含谁的关系,是Employee包含EmployeeDTO的属性,所以需要进行copy操作 Employee employee = new Employee(); //对象属性拷贝 BeanUtils.copyProperties(employeeDTO, employee); // 设置账号的状态,默认正常状态 1表示正常,0表示锁定 employee.setStatus(StatusConstant.ENABLE); // 设置账号的密码,默认为123456,但是此处需要使用md5加密技术,同时使用DigestUtils工具类 employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes())); // 设置当前记录的创建时间 employee.setCreateTime(LocalDateTime.now()); // 设置当前记录的修改时间 employee.setUpdateTime(LocalDateTime.now()); // TODO 后期这里需要改成动态的ID(已完成,利用BaseContext类) // 设置创建人id employee.setCreateUser(BaseContext.getCurrentId()); // 设置修改人id employee.setUpdateUser(BaseContext.getCurrentId()); employeeMapper.insert(employee); }
4、分页查询
/** * 分页查询 * @param employeePageQueryDTO * @return */ Page<Employee> pageQuery(EmployeePageQueryDTO employeePageQueryDTO);
/** * 分页查询 * @param employeePageQueryDTO * @return */ PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO);
/** * 分页查询 * * @param employeePageQueryDTO * @return */ @Override public PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO) { // 传统做法:select * from employee limit 0,10 // 此处利用插件,pageHelper直接进行分页查询,获取当前页面,再获取页面大小 PageHelper.startPage(employeePageQueryDTO.getPage(), employeePageQueryDTO.getPageSize()); //调用Mapper,访问数据层,执行sql语句 Page<Employee> page = employeeMapper.pageQuery(employeePageQueryDTO); //以上所得为Page对象,我们所需要的是PageResult对象,所以需要获取 long total = page.getTotal(); List<Employee> records = page.getResult(); return new PageResult(total, records); }
5、启动禁用账号
/** * 启动禁用账号 * @param employee */ void update(Employee employee);
/** * 启用禁用用户账号 * @param status * @param id */ void startOrStop(Integer status, long id);
/** * 启用禁用员工账号 * @param status * @param id */ @Override public void startOrStop(Integer status, long id) { //一般为update employee set status = ? where id = ? Employee employee = new Employee(); employee.setId(id); employee.setStatus(status); employeeMapper.update(employee); }
6、EmployeeMapper.xml
<mapper namespace="com.sky.mapper.EmployeeMapper"> <select id="pageQuery" resultType="com.sky.entity.Employee"> select * from employee <where> <if test="name != null and name != ''"> and name like concat('%',#{name},'%') </if> </where> order by create_time desc </select> <update id="update"> 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> </mapper>
路径参数要加注解@PathVariable,可以用于取路径参数,结合花括号占位符,{}
7、根据ID查询用户信息
1. Controller层,直接接收前端的HTTP请求
注意返回对象的要求
注意请求类型、请求地址
注意返回形式是否为JSON数据
/** * 根据id查询员工 * @param id * @return */ @GetMapping("/{id}") @ApiOperation("根据ID查询员工") public Result<Employee> getById(@PathVariable long id) { Employee employee = employeeService.getById(id); return Result.success(employee); }
2. Service层的抽象接口
/** * 根据id查询用户信息 * @param id * @return */ Employee getById(long id);
3. Service层的接口实现类
/** * 根据id查询用户信息 * @param id * @return */ @Override public Employee getById(long id) { Employee employee = employeeMapper.getById(id); employee.setPassword("****"); return employee; }
4. Mapper层与数据交互
如若是简单的sql语句可以直接注解编写
若是动态sql,则需要在xml文件中编写
/** * 根据id查询用户信息 * @return */ @Select("select * from employee where id = #{id}") Employee getById(long id);
8、编辑员工信息
这里为什么只需要employeeDTO对象:
因为我们所需要的用户信息包含在此对象内
可以通过BeanUtils的工具类将获取到的employeeDTO属性复制到employee中
然后在employeeDTO中没有的属性,在employee中另行添加即可
这样可防止最开始直接使用employee对象,但是很多没必要的值被修改
/** * 编辑用户信息 * @param employeeDTO * @return */ @PutMapping @ApiOperation("编辑员工信息") public Result update(@RequestBody EmployeeDTO employeeDTO) { log.info("编辑员工信息{}", employeeDTO);//Swagger接口的信息提示 employeeService.update(employeeDTO); return Result.success(); }
/** * 编辑用户信息 * @param employeeDTO */ void update(EmployeeDTO employeeDTO);
/** * 编辑用户信息 * @param employeeDTO */ @Override public void update(EmployeeDTO employeeDTO) { Employee employee = new Employee(); BeanUtils.copyProperties(employeeDTO,employee); employee.setUpdateTime(LocalDateTime.now()); employee.setUpdateUser(BaseContext.getCurrentId()); employeeMapper.update(employee); }
/** * 启动禁用账号、更新账户信息【复用】 * @param employee */ void update(Employee employee);
<update id="update"> 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>
9、导入分类管理功能【略过】
三、Day03-功能实现
1、公共字段自动填充
四、剪不断理还乱
1、entity【此部分是对应数据库中字段】
2、dto【此部分用于前后端交互】
@Data public class DishDTO implements Serializable { private Long id; //菜品名称 private String name; //菜品分类id private Long categoryId; //菜品价格 private BigDecimal price; //图片 private String image; //描述信息 private String description; //0 停售 1 起售 private Integer status; //口味 private List<DishFlavor> flavors = new ArrayList<>(); }
@Data public class CategoryPageQueryDTO implements Serializable { //页码 private int page; //每页记录数 private int pageSize; //分类名称 private String name; //分类类型 1菜品分类 2套餐分类 private Integer type; }
3、vo【向前端进行页面展示】
4、Controller【接收处理前端发起的HTTP请求】
/** * 菜品分页查询 * @param dishPageQueryDTO * @return */ @GetMapping("/page") @ApiOperation("菜品分页查询") //最后得到的结果是一个集合,所以泛型返回需要的是一个集合对象,然后传递的参数是query类型,即key value形式,所以不需要@RequestBody public Result<PageResult> pageQuery(DishPageQueryDTO dishPageQueryDTO){ log.info("菜品分页查询:{}",dishPageQueryDTO); PageResult pageResult = dishService.pageQuery(dishPageQueryDTO); return Result.success(pageResult); }
首先需要知道的是:
使用的是什么Method
Get
Post
接口的设计,ApiOperation
返回类型的设定,是否需要返回值,看最后是否获取到数据,如果是个集合,那就要注意了,如果只是插入或者删除操作,则不需要返回值
传入的参数是否是地址参数,如果是地址参数,则需要@Requestbody,如果是普通的query参数,key、value形式
调用Service,在service中涉及具体的操作,实际处理业务
因为调用,所以需要自动注解dishService,使用其具体方法
最后返回Result结果给前端展示,success
5、Service【与Controller层进行交互,处理数据业务操作】
/** * 菜品分页查询,真正的实现操作 * @param dishPageQueryDTO * @return */ @Override public PageResult pageQuery(DishPageQueryDTO dishPageQueryDTO) { //分页操作的真正实现,此处使用pageHelper插件 PageHelper.startPage(dishPageQueryDTO.getPage(),dishPageQueryDTO.getPageSize()); //把剩余的参数传递 Page<DishVO> page = dishMapper.pageQuery(dishPageQueryDTO); //得到的list集合封装到PageResult中,total是总数量,Result是所有的返回结果 return new PageResult(page.getTotal(),page.getResult()); }
此处的分页操作
使用的是PageHelper插件
在service中操作,需要实际用到Mapper,所以需要在此处导入dishMapper
此处的方法被controller层使用,然后在此处,需要mapper实际与数据库进行交互
注意要返回的对象类型
6、Mapper【与数据库的数据进行交互】
/** * 菜品分页查询 * @param dishPageQueryDTO * @return */ Page<DishVO> pageQuery(DishPageQueryDTO dishPageQueryDTO);
写入Mapper操作,此处如果是静态sql,只需要在上面写上注解
一个Mapper文件对应一个sql操作
如果是动态sql,如下:
<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>
7、AutoFill,注解【公共字段】
/** * 自定义注解,用于标识某个方法需要进行字段自动填充 */ @Target(ElementType.METHOD)//指定注解只能加载在方法上 @Retention(RetentionPolicy.RUNTIME) public @interface AutoFill { // 数据库操作类型:Update、Insert OperationType value(); }
-
要 URL 或表单参数 →
@RequestParam
-
要 JSON/XML 请求体 →
@RequestBody
-
要 HTTP 请求头参数 →
@RequestHeader
-
要 定义接口路由 →
@RequestMapping
@RequestParam
可以省略的情况
条件:
-
方法参数名和请求参数名 完全一致。
-
请求是 GET 或表单 POST 类型。
-
参数是 非必须(即不是 required=true) 或 有默认值。
❌ 不能省略的情况(@RequestParam)
-
参数名不一致时必须用
@RequestParam("paramName")
-
想设置默认值或是否必须时也必须加注解
@Transactional
@Transactional
是 Spring 中用于 声明式事务管理 的注解。它可以确保一段代码在数据库操作时 要么全部成功、要么全部失败(原子性),常用于涉及多个数据库操作的方法上。
DTO、ENTITY、VO的三者区别
场景 | 用什么 |
---|---|
接收前端表单/请求参数 | DTO(参数校验、字段精简) |
与数据库交互 | Entity(ORM映射) |
返回前端展示用数据 | VO(展示友好、数据组合) |
五、Redis入门
1. 编写Yml
redis: host: ${sky.redis.host} port: ${sky.redis.port} password: ${sky.redis.password}
redis: host: localhost port: 6379 password: 123456
2. 编写配置文件
@Configuration @Slf4j public class RedisConfiguration { public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){ log.info("开始创建redis模板对象..."); RedisTemplate redisTemplate = new RedisTemplate(); //设置redis的连接工厂对象 redisTemplate.setConnectionFactory(redisConnectionFactory); //设置redis key的序列化器 redisTemplate.setKeySerializer(new StringRedisSerializer()); return redisTemplate; } }
3. 编写测试文件
@SpringBootTest public class SpringDataRedisTest { @Autowired private RedisTemplate redisTemplate; @Test public void testRedisTemplate() { System.out.println(redisTemplate); //Redis常用操作几种数据类型 ValueOperations valueOperations = redisTemplate.opsForValue();//【String】 HashOperations hashOperations = redisTemplate.opsForHash();//【Hash】 ListOperations listOperations = redisTemplate.opsForList();//【List】 SetOperations setOperations = redisTemplate.opsForSet();//【Set】 ZSetOperations zSetOperations = redisTemplate.opsForZSet();//【Zset】 } }
4. 测试类集合
@SpringBootTest public class SpringDataRedisTest { @Autowired private RedisTemplate redisTemplate; @Test public void testRedisTemplate() { System.out.println(redisTemplate); //Redis常用操作几种数据类型 ValueOperations valueOperations = redisTemplate.opsForValue();//【String】 HashOperations hashOperations = redisTemplate.opsForHash();//【Hash】 ListOperations listOperations = redisTemplate.opsForList();//【List】 SetOperations setOperations = redisTemplate.opsForSet();//【Set】 ZSetOperations zSetOperations = redisTemplate.opsForZSet();//【Zset】 } @Test public void testString() { //set get setex setnx redisTemplate.opsForValue().set("name", "xiaoming"); String name = (String) redisTemplate.opsForValue().get("name"); System.out.println(name); redisTemplate.opsForValue().set("code", "123", 3, TimeUnit.MINUTES);//设置过期时间 redisTemplate.opsForValue().setIfAbsent("lock", "1"); redisTemplate.opsForValue().setIfAbsent("lock", "2"); } @Test public void testHash() { //hset hget hdel hkeys hvals // HashOperations hashOperations = redisTemplate.opsForHash(); redisTemplate.opsForHash().put("hashlist", "name", "mao"); redisTemplate.opsForHash().put("hashlist", "age", "24"); String name = (String) redisTemplate.opsForHash().get("hashlist", "name"); String age = (String) redisTemplate.opsForHash().get("hashlist", "age"); System.out.println("name:" + name); System.out.println("age:" + age); System.out.println("有:" + redisTemplate.opsForHash().keys("hashlist")); System.out.println("删除前:" + redisTemplate.opsForHash().values("hashlist")); redisTemplate.opsForHash().delete("hashlist", "age"); System.out.println("删除后:" + redisTemplate.opsForHash().values("hashlist")); } @Test public void testList() { //lpush lrange lpop llen ListOperations listOperations = redisTemplate.opsForList(); listOperations.leftPush("list", "a");//一次一个 listOperations.leftPushAll("list", "b", "c", "d");//不限制 System.out.println("删除前:" + listOperations.range("list", 0, -1)); System.out.println("删除前:" + listOperations.size("list")); listOperations.rightPop("list"); listOperations.leftPop("list"); System.out.println("删除后:" + listOperations.range("list", 0, -1)); System.out.println("删除后:" + listOperations.size("list")); } @Test public void testSet() { SetOperations setOperations = redisTemplate.opsForSet(); setOperations.add("set1", "a", "b"); setOperations.add("set2", "b", "c", "d"); System.out.println(setOperations.size("set1")); System.out.println(setOperations.size("set2")); System.out.println("返回成员:" + setOperations.members("set1")); System.out.println("返回成员:" + setOperations.members("set2")); System.out.println("交集:" + setOperations.intersect("set1", "set2")); System.out.println("并集:" + setOperations.union("set1", "set2")); setOperations.remove("set1", "a"); System.out.println("删除后返回成员:" + setOperations.members("set1")); System.out.println(setOperations.size("set1")); System.out.println(setOperations.size("set2")); } @Test public void testZSet() { ZSetOperations zSetOperations = redisTemplate.opsForZSet(); zSetOperations.add("zset", "mao", 10.8); zSetOperations.add("zset", "chen", 10.5); zSetOperations.add("zset", "zhu", 10.3); System.out.println("开始:" + zSetOperations.range("zset", 0, -1)); zSetOperations.incrementScore("zset", "zhu", 5); System.out.println("结束:" + zSetOperations.range("zset", 0, -1)); zSetOperations.remove("zset", "zhu"); System.out.println("最后:" + zSetOperations.range("zset", 0, -1)); } @Test public void testCommon() { //keys exists type del Set keys = redisTemplate.keys("*"); System.out.println(keys); Boolean set = redisTemplate.hasKey("set"); System.out.println(redisTemplate.hasKey("set1")); for (Object key: keys){ DataType type = redisTemplate.type(key); System.out.println(type.name()); } redisTemplate.delete("set1"); } }
5. 店铺营业状态设置
package com.sky.controller.admin; import com.sky.result.Result; 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.data.redis.core.RedisTemplate; import org.springframework.web.bind.annotation.*; @RestController("adminShopController") @RequestMapping("/admin/shop") @Slf4j @Api("店铺相关接口") public class ShopController { public static final String KEY = "SHOP_STATUS"; @Autowired private RedisTemplate redisTemplate; //1. 更改管理端营业状态 //2. 获取管理端营业状态 //3. 获取用户端营业状态 /** * 设置店铺营业状态 * * @param status * @return */ @PutMapping("/{status}") @ApiOperation("设置店铺营业状态") public Result setStatus(@PathVariable Integer status) { log.info("设置店铺营业状态: {}", status == 1 ? "营业中" : "打烊中"); redisTemplate.opsForValue().set(KEY, status); return Result.success(); } @GetMapping("/status") @ApiOperation("查询店铺营业状态") public Result<Integer> getStatus() { Integer status = (Integer) redisTemplate.opsForValue().get(KEY); log.info("店铺营业状态为:{}", status == 1 ? "营业中" : "打烊中"); return Result.success(status); } }
package com.sky.controller.user; import com.sky.result.Result; 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.data.redis.core.RedisTemplate; import org.springframework.web.bind.annotation.*; @RestController("userShopController") @RequestMapping("/user/shop") @Slf4j @Api("店铺相关接口") public class ShopController { public static final String KEY = "SHOP_STATUS"; @Autowired private RedisTemplate redisTemplate; @GetMapping("/status") @ApiOperation("查询店铺营业状态") public Result<Integer> getStatus() { Integer status = (Integer) redisTemplate.opsForValue().get(KEY); log.info("店铺营业状态为:{}", status == 1 ? "营业中" : "打烊中"); return Result.success(status); } }
六、SpringCache入门
useGeneratedKeys
和 keyProperty
属性详解
这两个属性是 MyBatis 中用于处理数据库自动生成主键(如自增ID)的重要配置,通常一起使用在 <insert>
标签中。
parameterType="User"
是 MyBatis 映射文件(XML)中的一个属性,用于指定 SQL 语句接收的参数类型。它的主要作用是告诉 MyBatis 这个 SQL 语句将接收什么类型的参数对象。
在 MyBatis 的 <foreach>
标签中,open
和 close
(或 end
,取决于 MyBatis 版本)属性用于控制循环生成的 SQL 片段的前缀和后缀。它们通常在需要动态构建带有特定语法结构的 SQL 时使用。以下是详细说明:
open
和 close
属性的作用
属性 | 用途 | 示例值 |
---|---|---|
open | 在循环开始前添加的字符串(前缀) | "(" , "WHERE " |
close | 在循环结束后添加的字符串(后缀) | ")" , " OR " |
你提到的代码 for(ShoppingCart cart : shoppingCartList)
是 Java 的 增强型 for 循环(Enhanced for-loop),其结构可以分解为以下部分:
for(类型 对象名 : 集合/数组) { // 循环体 }
graph TD A[开始] --> B[初始化当前团 current = 空集,候选点集 candidate = 所有点] B --> C{candidate 是否为空?} C -- 否 --> D[选一个点 v ∈ candidate] D --> E{v 是否与 current 中所有点相连?} E -- 是 --> F[将 v 加入 current,更新 candidate 为与 v 相连的候选点] F --> C E -- 否 --> G[跳过 v] G --> C C -- 是 --> H{current 是否大于 best?} H -- 是 --> I[更新 best = current] H -- 否 --> J[回溯返回上一层] I --> J