前情提要:这是博主学习的笔记,不专业
知识点一:公共字段自动填充
它的作用:
是当代码的几个模块拥有相同的操作时,比如说在mapper层中不同mapper接口更新某个相同名称的属性时,可以把这段更新代码整合在一起变成公共字段。
具体流程如下:
1.先自定义注解:
@Target:用于描述注解的使用范围
ElementType.METHOD | 应用于方法 |
@Retention:表明该注解的生命周期
RetentionPolicy.RUNTIME | 由JVM 加载,包含在类文件中,在运行时可以被获取到 |
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
//数据库操作类型:UPDATE,INSERT
OperationType value();
}
2.基于Spring AOP的切面(Aspect),用于在特定方法执行前(前置通知)自动填充实体对象的公共字段。
@Aspect
@Component
@Slf4j
public class AutoFillAspect {
//切入点
@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
public void pointcut() {}
//before是前置通知,在通知中进行公共字段的赋值。
@Before("pointcut()")
public void before(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("setCreateTime",LocalDateTime.class);
Method setCreateUser =entity.getClass().getDeclaredMethod("setCreateUser",Long.class);
Method setUpdateTime =entity.getClass().getDeclaredMethod("setUpdateTime",LocalDateTime.class);
Method setUpdateUser =entity.getClass().getDeclaredMethod("setUpdateUser",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("setUpdateTime",LocalDateTime.class);
Method setUpdateUser =entity.getClass().getDeclaredMethod("setUpdateUser",Long.class);
//通过反射为对象属性赋值
setUpdateTime.invoke(entity,now);
setUpdateUser.invoke(entity,CurrentId);
}catch (Exception e){
e.printStackTrace();
}
}
}
}
反射:
-
entity.getClass().getDeclaredMethod(String name, Class<?>... parameterTypes)
:entity.getClass()
:获取实体对象的类对象。getDeclaredMethod
:根据方法名和参数类型获取方法对象。- 第一个参数是方法名,如
"setCreateTime"
。 - 第二个参数是方法参数类型,这里是
LocalDateTime.class
或Long.class
,表示方法的参数类型。
这些代码片段通过反射获取了当前实体类中名为
setCreateTime
、setCreateUser
、setUpdateTime
和setUpdateUser
的方法对象。 setCreateTime.invoke(entity, now)
:setCreateTime
是前面获取的方法对象。invoke
方法用于调用这个方法。- 第一个参数
entity
是要调用方法的对象实例。 - 第二个参数
now
是要传递给方法的参数(这里是当前时间LocalDateTime
类型的对象)。
知识点二:捕获异常
/**
* 全局异常处理器,处理项目中抛出的业务异常
*/
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
* 捕获业务异常
* @param ex
* @return
*/
@ExceptionHandler
public Result exceptionHandler(BaseException ex){
log.error("异常信息:{}", ex.getMessage());
return Result.error(ex.getMessage());
}
@ExceptionHandler
public Result exceptionHandler(SQLIntegrityConstraintViolationException ex){
String message = ex.getMessage();
if (message.contains("Duplicate key")){
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);
}
}
}
- @ExceptionHandler:同样的注解,这里用来捕获
SQLIntegrityConstraintViolationException
类型的异常。 - message:获取异常的消息内容。
- if (message.contains("Duplicate key")):检查异常消息中是否包含"Duplicate key",这通常表明数据库操作试图插入一个已存在的唯一键值。
- String[] split = message.split(" ");:将异常消息按空格拆分成字符串数组。
- String username = split[2];:从拆分后的数组中提取特定位置的字符串,这里假设是用户名(实际情况需要根据具体的异常消息格式调整)。
- String msg = username + MessageConstant.ALREADY_EXISTS;:构造用户友好的错误消息,
MessageConstant.ALREADY_EXISTS
可能是一个常量,表示某条记录已经存在。 - return Result.error(msg);:返回包含错误信息的
Result
对象。
- else:如果异常消息不包含"Duplicate key",则返回一个通用的未知错误信息。
- return Result.error(MessageConstant.UNKNOWN_ERROR);:返回通用错误信息。
知识点三:比较散的知识点
1.mybatis中的useGeneratedKeys
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
-
useGeneratedKeys="true"
:- 这个属性表示MyBatis在执行插入操作时会自动获取数据库生成的主键值(通常是自增ID)。这个特性对使用自增主键的表特别有用,因为它可以避免手动获取生成的主键。
-
keyProperty="id"
:- 这个属性指定了实体类中的哪个属性用于存储数据库生成的主键值。在插入记录后,MyBatis会将生成的主键值填充到指定的属性中。
使用:当service层需要mapper层结束后的数据时使用,如下:
dishMapper.insert(dish);
Long id = dish.getId();
2.mybatis的动态标签<foreach>
<foreach collection="flavors" item="df" separator=",">
(#{df.dishId},#{df.name},#{df.value})
</foreach>
表示遍历flavors这个List
、Set
或者其他类型的集合,其中每一个元素(自定义)叫做df,每个生成的SQL片段之间会用逗号 ,
分隔。还有open和close标签指定在遍历集合生成的SQL片段前后添加的字符,通过是和sql的in一起使用,如下:
select setmeal_id from setmeal_dish where dish_id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
3.分页查询-pagehelper
1.首先在pom.xml文件中导入相关依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
</dependency>
2.在service中使用
public PageResult page(DishPageQueryDTO dishPageQueryDTO) {
PageHelper.startPage(dishPageQueryDTO.getPage(),dishPageQueryDTO.getPageSize());
Page<DishVO> page=dishMapper.page(dishPageQueryDTO);
return new PageResult(page.getTotal(),page.getResult());
}
pagehelper是把mapper层中的相关查询自动添加了limit * *,所以在相关mapper层中不需要在添加该SQL命令。pageResult是自定义类,格式如下:
知识点四:注解
1.@RequestParam
@RequestParam注解有以下属性:
value:请求参数的名称。如果请求参数的名称与方法参数的名称相同,则该属性可以省略。
required:指定请求参数是否是必需的,默认为true。如果设置为false,则表示该参数是可选的。
defaultValue:指定当请求参数未提供时使用的默认值。
用途:
可以用在路径参数上:
@GetMapping("/users/{id}")
public User getUser(@RequestParam("id") Long userId) {
// 方法体
}
可以用在普通参数上,若是请求参数是字符串,但是接收参数是列表不同类型的,RequestParam可以自动转化(下图前端传入的是ids字符串(类似"1,2,3"),但是后端接收参数是List类型的):
@DeleteMapping
@ApiOperation("删除菜品")
public Result delete(@RequestParam List<Long> ids){
log.info("菜品批量删除{}",ids);
dishService.delete(ids);
return Result.success();
}
2.@Configuration
1.告诉spring这是一个配置类,相当于spring的xml配置文件
2.被@Configuration 注解的类,会被cglib代理进行增强
3.@Configuration类允许通过调用同一类中的其他@Bean方法来定义bean之间的依赖关系,保证@Bean的对象作用域受到控制,避免多例
知识点五:配置阿里云OSS
在application.yml配置:
在application-dev.yml配置:
创建一个配置类AliOssProperties:
@Component
@ConfigurationProperties(prefix = "sky.alioss")
@Data
public class AliOssProperties {
private String endpoint;
private String accessKeyId;
private String accessKeySecret;
private String bucketName;
}
创建一个工具类AliOssUtil:
@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();
}
}
给上面的工具类配置属性:
@Slf4j
@Configuration
public class OssConfiguration {
@Bean
@ConditionalOnMissingBean
public AliOssUtil aliOssUtil(AliOssProperties aliOssProperties) {
log.info("开始创建阿里云文件上传工具类{}", aliOssProperties);
return new AliOssUtil(aliOssProperties.getEndpoint(),
aliOssProperties.getAccessKeyId(),
aliOssProperties.getAccessKeySecret(),
aliOssProperties.getBucketName());
}
}