目录
Java操作redis ---Spring Data Redis
Day-3
公共字段自动填充
枚举
自定义注解
注解按生命周期来划分可分为3类:
1、RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
2、RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
3、RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;这3个生命周期分别对应于:Java源文件(.java文件) ---> .class文件 ---> 内存中的字节码。
那怎么来选择合适的注解生命周期呢?
首先要明确生命周期长度 SOURCE < CLASS < RUNTIME ,所以前者能作用的地方后者一定也能作用。一般如果需要在运行时去动态获取注解信息,那只能用 RUNTIME 注解;如果要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife),就用 CLASS注解;如果只是做一些检查性的操作,比如 @Override 和 @SuppressWarnings,则可选用 SOURCE 注解。
/**
* 自定义注解,用于标识某个方法需要进行公共字段自动填充处理
*/
//@Target指定注解只能加在方法上
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
//数据库操作类型:update insert
OperationType value();
}
自定义切面类
@component是spring中的一个注解,它的作用就是实现bean的注入,在探究@component前先了解一下注解?何为注解?注解本质上就是一个类,开发中我们可以使用注解 取代 xml配置文件。
sky-server/src/main/java/com/sky/aspect/AutoFillAspect.java
1.execution指定拦截的是所有返回值,所有类,所有方法,匹配所有参数类型
再加上加自定义注解的方法
2.当匹配到切点表达式,执行通知方法;传入连接点,通过连接点可知哪些方法被拦截到以及其具体参数
@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("开始进行公共字段自动填充...");
}
}
ctrl+alt+B 向下转型 接口转成方法
直接捕获大的异常
以免方法名写错,将字符串设为常量类
sky-common/src/main/java/com/sky/constant/AutoFillConstant.java
/**
* 公共字段自动填充相关常量
*/
public class AutoFillConstant {
/**
* 实体类中的方法名称
*/
public static final String SET_CREATE_TIME = "setCreateTime";
public static final String SET_UPDATE_TIME = "setUpdateTime";
public static final String SET_CREATE_USER = "setCreateUser";
public static final String SET_UPDATE_USER = "setUpdateUser";
}
@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("开始进行公共字段自动填充...");
//获取到当前被拦截的方法上的数据库操作类型
//joinPoint.getSignature().var回车
MethodSignature signature = (MethodSignature)joinPoint.getSignature();//方法签名对象
//signature.getMethod().getAnnotation(AutoFill.class).var
AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获得方法上的注解对象
//autoFill.value().var
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){
//为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);//为entity对象赋值now
setCreateUser.invoke(entity,currentId);
setUpdateTime.invoke(entity,now);
setUpdateUser.invoke(entity,currentId);
} catch (Exception e) {
throw new RuntimeException(e);
}
}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) {
throw new RuntimeException(e);
}
}
}
}
Impl中就不需要再处理公共字段
Mapper 方法加注解
/**
* 插入员工数据
* @param employee
*/
@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);
/**
* 根据主键动态修改属性
* @param employee
*/
@AutoFill(value = OperationType.UPDATE)
void update(Employee employee);
阶段测试:前后端联调测试,加断点可知拦截到了update方法,验证可知自定义切面类能执行到
文件上传
配置属性类
sky-common/src/main/java/com/sky/properties/AliOssProperties.java
@ConfigurationProperties(prefix = "sky.alioss")读取配置文件的配置项,将其封装为java对象
类用驼峰,配置文件用-
application.yml和application-dev.yml,前者引用,后者写具体的值
开发SpringBoot应用的时候,通常程序需要在测试环境测试成功后才会上线到生产环境。而测试环境和生产环境的数据库地址、服务器端口等配置都不同。在为不同环境打jar包时,需要频繁的修改application.yml配置文件,十分麻烦。
可以采用创建多个配置文件的方法解决这一问题。
application-dev.yml:本地开发环境
application-test.yml:测试环境
application-prod.yml:生产环境其中application.yml存放公共配置,可通过修改active切换读取的配置文件,比如
active: dev
改成active: test
就是将读取application-dev.yml改为application-test.yml,环境也从本地开发变成了测试环境
配置文件中的配置项通过配置属性类来加载(即通过配置属性类获得配置文件中的具体配置,作用:获得具体配置)
@Component
@ConfigurationProperties(prefix = "sky.alioss")
@Data
public class AliOssProperties {
private String endpoint;
private String accessKeyId;
private String accessKeySecret;
private String bucketName;
}
配置文件
注意yml中要加空格
sky-server/src/main/resources/application.yml
sky:
jwt:
# 设置jwt签名加密时使用的秘钥
admin-secret-key: itcast
# 设置jwt过期时间
admin-ttl: 7200000
# 设置前端传递过来的令牌名称
admin-token-name: token
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}
sky-server/src/main/resources/application-dev.yml
sky:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
host: localhost
port: 3306
database: sky_take_out
username: root
password: 2693488974jhy
alioss:
endpoint: oss-cn-beijing.aliyuncs.com
access-key-id:
access-key-secret:
bucket-name: jhy-auny
工具类 (固定代码)
sky-common/src/main/java/com/sky/utils/AliOssUtil.java
@Data
@AllArgsConstructor
@Slf4j
public class AliOssUtil {
private String endpoint;
private String accessKeyId;
private String accessKeySecret;
private String bucketName;
/**
* 文件上传
*
* @param bytes
* @param objectName
* @return
*/
public String upload(byte[] bytes, String objectName) {
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
try {
// 创建PutObject请求。
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();
}
}
//文件访问路径规则 https://BucketName.Endpoint/ObjectName
StringBuilder stringBuilder = new StringBuilder("https://");
stringBuilder
.append(bucketName)
.append(".")
.append(endpoint)
.append("/")
.append(objectName);
log.info("文件上传到:{}", stringBuilder.toString());
return stringBuilder.toString();
}
}
配置类
用于创建工具类对象,传入配置属性类中获取的配置文件数据
/**
* 配置类,用于创建AliOssUtil对象
*/
@Configuration
@Slf4j
public class OssConfiguration {
//@Bean当项目启动时,就会将Bean对象创建出来,交给spring容器管理
//@ConditionalOnMissingBean保证spring容器中只有一个util对象,工具类对象一个即可
@Bean
@ConditionalOnMissingBean
public AliOssUtil aliOssUtil(AliOssProperties aliOssProperties){
log.info("开始创建阿里云文件上传工具类对象:{}",aliOssProperties);
return new AliOssUtil(aliOssProperties.getEndpoint(),
aliOssProperties.getAccessKeyId(),
aliOssProperties.getAccessKeySecret(),
aliOssProperties.getBucketName());
}
}
controller
sky-server/src/main/java/com/sky/controller/admin/CommonController.java
uuid防止文件重名,再加后缀
/**
* 通用接口
*/
@RestController
@RequestMapping("/admin/common")
@Api(tags = "通用接口")
@Slf4j
public class CommonController {
@Autowired
private AliOssUtil aliOssUtil;
/**
* 文件上传
* @param file
* @return
*/
//需要返回String类型的data,即文件上传路径,前端请求到图片文件
//前端传来的为文件类型,springMVC将其封装为MultipartFile 注意参数名与前端提交的参数名一致
@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);
}
}
测试报错,因为配置文件中配置错误。通过查看OSS中图片的路径发现可以查到,对比断点调试过程中得出的路径,发现有问题
新增菜品
DTO
根据接口进行代码编写
sky-pojo/src/main/java/com/sky/dto/DishDTO.java
@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<>();
}
sky-pojo/src/main/java/com/sky/entity/DishFlavor.java
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DishFlavor implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
//菜品id
private Long dishId;
//口味名称
private String name;
//口味数据list
private String value;
}
C
/**
* 菜品管理
*/
@RestController
@RequestMapping("/admin/dish")
@Api(tags = "菜品相关接口")
@Slf4j
public class DishController {
@Autowired
private DishService dishService;
//因为非必须,无需泛型
//@RequestBody封装json格式数据
@PostMapping
@ApiOperation("新增菜品")
public Result save(@RequestBody DishDTO dishDTO){
dishService.saveWithFlavor(dishDTO);
return Result.success();
}
}
S
public interface DishService {
/**
* 新增菜品和对应的口味
* @param dishDTO
*/
public void saveWithFlavor(DishDTO dishDTO);
}
I
@SpringBootApplication
@EnableTransactionManagement //开启注解方式的事务管理
@Slf4j
public class SkyApplication {
public static void main(String[] args) {
SpringApplication.run(SkyApplication.class, args);
log.info("server started");
}
}
处理多张表,加 @Transactional
DTO中包含不需要的口味,所以传实体Dish
flavors集合,直接批量插入
口味表中需要设置的dishId自增,前端无法传入;
菜品保存后会分配好Id,将菜品表SQL语句insert产生的主键值赋给id返回给程序;
由此就可以遍历flavors一次设置dishId。
@Service
@Slf4j
public class DishServiceImpl implements DishService {
@Autowired
private DishMapper dishMapper;
@Autowired
private DishFlavorMapper dishFlavorMapper;
/**
* 新增菜品和对应的口味
* @param dishDTO
*/
//事务注解,保证方法为原子型,全成功or全失败
//在SkyApplication中@EnableTransactionManagement 开启注解方式的事务管理,所以可加注解
@Transactional
public void saveWithFlavor(DishDTO dishDTO) {
Dish dish = new Dish();
BeanUtils.copyProperties(dishDTO,dish);
//向菜品表插入1条数据
dishMapper.insert(dish);
//获取insert语句生成的主键值
Long dishId = dish.getId();
List<DishFlavor> flavors = dishDTO.getFlavors();
if ( flavors != null && flavors.size() > 0){
flavors.forEach(dishFlavor -> {
dishFlavor.setDishId(dishId);
});
//向口味表插入n条数据
dishFlavorMapper.insertBatch(flavors);
}
}
}
M
@Mapper
public interface DishMapper {
/**
* 根据分类id查询菜品数量
* @param categoryId
* @return
*/
@Select("select count(id) from dish where category_id = #{categoryId}")
Integer countByCategoryId(Long categoryId);
/**
* 插入菜品数据
* @param dish
* @return
*/
@AutoFill(value = OperationType.INSERT)
void insert(Dish dish);
}
//mapper都是接口,具体实现在xml
@Mapper
public interface DishFlavorMapper {
/**
* 批量插入口味数据
* @param flavors
*/
void insertBatch(List<DishFlavor> flavors);
}
Y
<mapper namespace="com.sky.mapper.DishMapper">
<!--useGeneratedKeys="true"获得insert语句插入时所生成的主键值
将Dish的id,即主键值赋值给id,让service获取到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>
<mapper namespace="com.sky.mapper.DishFlavorMapper">
<!-- 遍历flavors集合,每个元素起名为df,每次遍历之间用,分隔
此处报错separator="," 注意为英文逗号-->
<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>
菜品分页查询
DTO
接收前端数据适应接口
@Data
public class DishPageQueryDTO implements Serializable {
private int page;
private int pageSize;
private String name;
//分类id
private Integer categoryId;
//状态 0表示禁用 1表示启用
private Integer status;
}
VO
返回前端数据适应接口
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DishVO 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 LocalDateTime updateTime;
//分类名称
private String categoryName;
//菜品关联的口味
private List<DishFlavor> flavors = new ArrayList<>();
//private Integer copies;
}
C
query地址栏方式请求,非json格式无需@RequestBody
/**
* 菜品分页查询
* @param dishPageQueryDTO
* @return
*/
@GetMapping("/page")
@ApiOperation("菜品分页查询")
public Result<PageResult> page(DishPageQueryDTO dishPageQueryDTO){
PageResult pageResult = dishService.pageQuery(dishPageQueryDTO);
return Result.success(pageResult);
}
S
/**
* 菜品分页查询
* @param dishPageQueryDTO
* @return
*/
PageResult pageQuery(DishPageQueryDTO dishPageQueryDTO);
I
/**
* 菜品分页查询
* @param dishPageQueryDTO
* @return
*/
public PageResult pageQuery(DishPageQueryDTO dishPageQueryDTO) {
// select * from employee limit 0,10 前端传入DTO,根据之动态计算拼接到SQL语句中,用插件方便操作
//开始分页查询
PageHelper.startPage(dishPageQueryDTO.getPage(),dishPageQueryDTO.getPageSize());
Page<DishVO> page = dishMapper.pageQuery(dishPageQueryDTO);
return new PageResult(page.getTotal(),page.getResult());
}
M
/**
* 菜品分页查询
* @param dishPageQueryDTO
* @return
*/
Page<DishVO> pageQuery(DishPageQueryDTO dishPageQueryDTO);
Y
复杂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>
删除菜品
C
@RequestParam 数组参数:mvc框架解析前端传来的字符串1,2,3,并根据逗号分隔,将分隔后的每一个元素封装到List中
/**
* 菜品批量删除
* @param ids
* @return
*/
@DeleteMapping
@ApiOperation("菜品批量删除")
public Result delete(@RequestParam List<Long> ids){
dishService.deleteBatch(ids);
return Result.success();
}
S
/**
* 菜品批量删除
* @param ids
*/
void deleteBatch(List<Long> ids);
I
处理多个表,加@Transactional注解,保证事务一致性
/**
* 菜品批量删除
* @param ids
*/
@Transactional
public void deleteBatch(List<Long> ids) {
//判断当前菜品是否能够删除---是否存在其起售中的菜品??
//ids.for
for (Long id : ids) {
Dish dish = dishMapper.getById(id);
if(dish.getStatus() == StatusConstant.ENABLE){
//当前菜品处于起售中,不能删除
throw new DeletionNotAllowedException(MessageConstant.DISH_ON_SALE);
}
}
//判断当前菜品是否能够删除---是否被套餐关联了??
//根据菜品id查询套餐id,若能查,则关联,不能删除,操作什么表,用什么mapper
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.deleteByDishId(id);
}
M
/**
* 根据主键查询菜品
* @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);
/**
* 根据菜品id查询对应的套餐id
* 多对多,所以用List
* @param dishIds
* @return
*/
//select setmeal_id from setmeal_dish where dish_id in (1,2,3,4)动态SQL,因为List<Long> dishIds不确定
List<Long> getSetmealIdsByDishIds(List<Long> dishIds);
/**
* 根据菜品id删除对应的口味数据
* @param dishid
*/
@Delete("delete from dish_flavor where dish_id = #{dishid}")
void deleteByDishId(Long dishid);
Y
<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>
修改菜品
根据id查询菜品
C
URL 中的 {xxx} 占位符可以通过@PathVariable(“xxx”) 绑定到操作方法的入参中。
若方法参数名称和需要绑定的url中变量名称一致时,可以简写。
/**
* 根据id查询菜品
* @param id
* @return
*/
@GetMapping("/{id}")
@ApiOperation("根据id查询菜品")
public Result<DishVO> getById(@PathVariable Long id){
DishVO dishVO = dishService.getByIdWithFlavor(id);
return Result.success(dishVO);
}
S
/**
* 根据id查询菜品和对应的口味数据
* @param id
* @return
*/
DishVO getByIdWithFlavor(Long id);
I
/**
* 根据id查询菜品和对应的口味数据
* @param id
* @return
*/
public DishVO getByIdWithFlavor(Long id) {
//根据id查询菜品数据
Dish dish = dishMapper.getById(id);
//根据菜品id查询对应的口味数据
List<DishFlavor> dishFlavors = dishFlavorMapper.getByDishId(id);
//将查询到的数据封装到VO
DishVO dishVO = new DishVO();
BeanUtils.copyProperties(dish,dishVO);
dishVO.setFlavors(dishFlavors);
return dishVO;
}
M
/**
* 根据主键查询菜品
* @param id
* @return
*/
@Select("select * from dish where id = #{id}")
Dish getById(Long id);
/**
* 根据菜品id查询对应的口味数据
* @param dishId
* @return
*/
@Select("select * from dish_flavor where dish_id = #{dishId}")
List<DishFlavor> getByDishId(Long dishId);
修改菜品与口味
C
泛型需要写?看有无返回前端的数据。 查有,改无。
/**
* 修改菜品
* @param dishDTO
* @return
*/
@PutMapping
@ApiOperation("修改菜品")
public Result update(@RequestBody DishDTO dishDTO){
dishService.updateWithFlavor(dishDTO);
return Result.success();
}
S
/**
* 修改菜品
* @param dishDTO
*/
void updateWithFlavor(DishDTO dishDTO);
I
/**
* 修改菜品
* @param dishDTO
*/
public void updateWithFlavor(DishDTO dishDTO) {
Dish dish = new Dish();
BeanUtils.copyProperties(dishDTO,dish);
//修改菜品表基本信息
dishMapper.update(dish);
//删除原有的口味数据
dishFlavorMapper.deleteByDishId(dishDTO.getId());
//重新插入口味数据
List<DishFlavor> flavors = dishDTO.getFlavors();
if ( flavors != null && flavors.size() > 0){
flavors.forEach(dishFlavor -> {
dishFlavor.setDishId(dishDTO.getId());
});
//向口味表插入n条数据
dishFlavorMapper.insertBatch(flavors);
}
}
M
/**
* 根据id动态修改菜品
* 不为空时再修改,动态SQL
* @param dish
*/
@AutoFill(value = OperationType.UPDATE)
void update(Dish dish);
/**
* 根据菜品id删除对应的口味数据
* @param dishId
*/
@Delete("delete from dish_flavor where dish_id = #{dishId}")
void deleteByDishId(Long dishId);
/**
* 批量插入口味数据
* @param flavors
*/
void insertBatch(List<DishFlavor> flavors);
Y
<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>
<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>
Day-5
Redis
启动redis服务
输入redis-ser,redis.wind后Tab可自动补全
ctrl+c停止服务
客户端连接redis服务
#为注释 默认密码foobared 可自行修改
-h host 需要连接redis服务的ip
-p port 端口号
-a 密码
安装redis图形界面
常用数据类型
常用命令
头插法队列
Java操作redis ---Spring Data Redis
1.导入坐标
sky-server/pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.配置数据源
sky-server/src/main/resources/application-dev.yml
sky:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
host: localhost
port: 3306
database: sky_take_out
username: root
password: 2693488974jhy
alioss:
endpoint: oss-cn-beijing.aliyuncs.com
access-key-id: LTAI5tHQy68vcDc3WSvrQeTf
access-key-secret: aOffkCtFfV81UYiHXpFnyNRi4FUK56
bucket-name: jhy-auny
redis:
host: localhost
port: 6379
database: 10
sky-server/src/main/resources/application.yml
spring:
profiles:
active: dev
main:
allow-circular-references: true
datasource:
druid:
driver-class-name: ${sky.datasource.driver-class-name}
url: jdbc:mysql://${sky.datasource.host}:${sky.datasource.port}/${sky.datasource.database}?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
username: ${sky.datasource.username}
password: ${sky.datasource.password}
redis:
host: ${sky.redis.host}
port: ${sky.redis.port}
database: ${sky.redis.database}
3.编写配置类,创建对象
sky-server/src/main/java/com/sky/config/RedisConfiguration.java
Redis图形界面看起来默认乱码,设置序列化器后正常
@Configuration
@Slf4j
public class RedisConfiguration {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
log.info("开始创建redis模板对象...");
RedisTemplate redisTemplate = new RedisTemplate();
//设置redis的连接工厂对象
redisTemplate.setConnectionFactory(redisConnectionFactory);
//设置redis key的序列化器
redisTemplate.setKeySerializer(new StringRedisSerializer());
return redisTemplate;
}
}
4.通过对象操作redis
店铺营业状态设置
若建表,表中只有一个status,所以用redis存储
单元测试类测试完注释注解,否则每次启动都会测试一遍,影响启动速度
sky-server/src/main/java/com/sky/controller/admin/ShopController.java
//指定bean的名称,不然相同类名默认bean名称冲突
@RestController("adminShopController")
@RequestMapping("/admin/shop")
@Api(tags = "店铺相关接口")
@Slf4j
public class ShopController {
public static final String KEY = "SHOP_STATUS";
@Autowired
private RedisTemplate redisTemplate;
/**
* 设置店铺的营业状态
* 动态接收路径参数
* @param status
* @return
*/
@PutMapping("/{status}")
@ApiOperation("设置店铺的营业状态")
public Result setStatus(@PathVariable Integer status){
log.info("设置店铺的营业状态为:{}",status == 1 ? "营业中":"打烊中");
redisTemplate.opsForValue().set(KEY,status);
return Result.success();
}
/**
* 获取店铺的营业状态
* @return
*/
@GetMapping("/status")
@ApiOperation("获取店铺的营业状态")
public Result<Integer> getStatus(){
Integer status = (Integer) redisTemplate.opsForValue().get(KEY);
log.info("获取店铺的营业状态为:{}",status == 1 ? "营业中":"打烊中");
return Result.success(status);
}
}
sky-server/src/main/java/com/sky/controller/user/ShopController.java
@RestController("userShopController")
@RequestMapping("/user/shop")
@Api(tags = "店铺相关接口")
@Slf4j
public class ShopController {
public static final String KEY = "SHOP_STATUS";
@Autowired
private RedisTemplate redisTemplate;
/**
* 获取店铺的营业状态
* @return
*/
@GetMapping("/status")
@ApiOperation("获取店铺的营业状态")
public Result<Integer> getStatus(){
Integer status = (Integer) redisTemplate.opsForValue().get(KEY);
log.info("获取店铺的营业状态为:{}",status == 1 ? "营业中":"打烊中");
return Result.success(status);
}
}
sky-server/src/main/java/com/sky/config/WebMvcConfiguration.java
接口文档将管理端和用户端接口区分开
/**
* 通过knife4j生成接口文档
* @return
*/
@Bean
public Docket docket1() {
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;
}
@Bean
public Docket docket2() {
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.user"))
.paths(PathSelectors.any())
.build();
return docket;
}