fix: 增加日记记录

This commit is contained in:
2023-06-13 16:37:18 +08:00
parent a4a8110c2b
commit 4a0eed5453
49 changed files with 7920 additions and 20 deletions

12
springboot/README.md Normal file
View File

@@ -0,0 +1,12 @@
这里是Springboot的学习笔记
* [定时任务](定时任务.md)
* [返回数据全局统一](返回数据全局统一.md)
* [热部署](热部署.md)
* [文件上传](文件上传.md)
* [chapter1](chapter1.md)
* [chapter2](chapter2.md)
* [chapter3](chapter3.md)
* [chapter4-SpringMVC](chapter4-SpringMVC.md)
* [chapter4-整合Mybatis](chapter5-整合MyBatis.md)
* [lab1](lab1.md)

10
springboot/_sidebar.md Normal file
View File

@@ -0,0 +1,10 @@
* [定时任务](定时任务.md)
* [返回数据全局统一](返回数据全局统一.md)
* [热部署](热部署.md)
* [文件上传](文件上传.md)
* [chapter1](chapter1.md)
* [chapter2](chapter2.md)
* [chapter3](chapter3.md)
* [chapter4-SpringMVC](chapter4-SpringMVC.md)
* [chapter4-整合Mybatis](chapter5-整合MyBatis.md)
* [lab1](lab1.md)

39
springboot/chapter1.md Normal file
View File

@@ -0,0 +1,39 @@
## 目录说明
```- src
-main
-java
-package
#主函数,启动类,运行它如果运行了 Tomcat、Jetty、Undertow 等容器
-SpringbootApplication
-resouces
#存放静态资源 js/css/images 等
- statics
#存放 html 模板文件
- templates
#主要的配置文件SpringBoot启动时候会自动加载application.yml/application.properties
- application.yml
#测试文件存放目录
-test
# pom.xml 文件是Maven构建的基础里面包含了我们所依赖JAR和Plugin的信息
- pom
```
##注意
- pom.xml中配置文件和程序的编码格式为utf-8
在<properties>标签中添加
```
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
```
- 设置默认端口号和默认访问路径在application.properties中配置
```
// 设置默认端口号
server.port=8090
// 设置根路径
server.servlet.context-path=/xiao
```
- 修改默认banner
- 在resource目录下添加banner.txt、banner.jpg、banner.gif、banner.jpeg等文件即可

77
springboot/chapter2.md Normal file
View File

@@ -0,0 +1,77 @@
## 配置详解
* 可以在任何Component中注入application.properties中的属性内容格式为
```
@Value("${全名称}")
```
* 如果想在编辑配置文件时有提示内容可以在pom.xml中增加此依赖
```
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
```
* 默认application.properties文件中增加属性内容后注入到实体类可以新建java类通过下面的注解进行注入
```
// 声明该类为组件类spring会自动注入到ApplicationContext中
@Component
// 声明当前类通过config文件注入属性prefix为前缀为指定内容的属性会按照名称自动注入到类属性中
@ConfigurationProperties(prefix = "snapswitch")
```
* 自定义配置文件
* 如果不想将所有配置都放在application.propeties中可以自定义自己的配置文件,然后通过下面的注解,将类和属性文件关联
```
@Component
@PropertySource("classpath:map.properties")
@ConfigurationProperties(prefix = "map")
```
* 可以在application.properties中配置当前环境名称这样程序在读取配置时会根据配置的环境名称自动读取对应的配置
```
# 当前使用环境,程序启动后会自动读取application-dev.properties的配置
# 如果默认配置和dev配置存在相同的配置则以dev为准
# 如果dev存在而默认不环境存在则以dev环境配置为准
# 如果默认环境存在而dev环境不存在则以默认环境配置为准
spring.profiles.active=dev
```
* 命令行打包程序,启动程序,指定参数启动
* 打包命令,进入项目目录后,执行
```
mvn clean install
mvn package
```
* 启动打包的jar
打包后的jar包会生成在target中最终的jar文件就是可执行程序在该jar文件所在目录执行
```
java -jar
jar文件名 例如chapter2-0.0.1-SNAPSHOT.jar
[--参数 例如:--spring.profiles.active=test --my1.age=32]
```
## 非常用参数
* 如果在配置文件中引用另外一个配置文件的值,也可以通过${参数名}的方式引用
```
app.name = appName
app.newName = ${app.name}V1.0
```
此时app.newName的值即为appNameV1.0
* 如果需要在配置文件中使用随机数,可以通过此方式生成
```
# 随机字符串
com.didispace.blog.value=${random.value}
# 随机int
com.didispace.blog.number=${random.int}
# 随机long
com.didispace.blog.bignumber=${random.long}
# 10以内的随机数
com.didispace.blog.test1=${random.int(10)}
# 10-20的随机数
com.didispace.blog.test2=${random.int[10,20]}
```

6
springboot/chapter3.md Normal file
View File

@@ -0,0 +1,6 @@
## 日志配置
* spring默认使用logback作为日志组件logback默认输出INFO、WARN、ERROR
如果需要更多的日志级别输出可以在application.properties中增加配置
```
debug=true
```

View File

@@ -0,0 +1,107 @@
## MVC设置
* @Controller修饰class用来创建处理http请求的对象
* @RestControllerSpring4之后加入的注解原来在@Controller中返回json需要@ResponseBody来配合,如果直接用@RestController替代@Controller就不需要再配置@ResponseBody默认返回json格式。
* @RequestMapping配置url映射
* value 请求路径 例如:@RequestMapping(value="/a/b"),则该方法对应的路径即为/a/b
* **在请求中可以以Ant通配符的方式配置路径**,例如:@RequestMapping(value="/*/b") 表示a/b、c/b等各个请求均可以访问到该方法@RequestMapping(value="/\*\*/b")则表示1/2/b3/4/b都可以访问到该方法单个\*号表示任意字符通配符,两个星号代表任意路径通配符
* @PathVariable("id") 配置rest请求中的可变路径参数
* @ModelAttribute 配置参数对象,前端可通过传递对象中的属性值来填充该对象
* @RequestParam 配置参数,对应名值对的形式
* @RequestBody 配置请求参数该数据一个请求只有一个且请求类型不能为get类型
## Swagger2配置
> 由于接口众多并且细节复杂需要考虑不同的HTTP请求类型、HTTP头部信息、HTTP请求内容等高质量地创建这份文档本身就是件非常吃力的事下游的抱怨声不绝于耳。
随着时间推移,不断修改接口实现的时候都必须同步修改接口文档,而文档与代码又处于两个不同的媒介,除非有严格的管理机制,不然很容易导致不一致现象。
为了解决上面这样的问题本文将介绍RESTful API的重磅好伙伴Swagger2它可以轻松的整合到Spring Boot中并与Spring MVC程序配合组织出强大RESTful API文档。
> 简单来说swagger就是用来自动生成Spring MVC文档的工具接口开发完毕后无需手动再修改接口文档直接让客户端/前端人员查看swagger生成的文档即可。
- 首先在pom.xml中导入swagger2的依赖
```
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.2.2</version>
</dependency>
```
- 构建swagger的配置类
```
/**
* swagger的配置文件
* */
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Value("${baseUrl}")
private String baseUrl;
@Bean
public Docket createRestRequestApi(){
return new Docket(DocumentationType.SWAGGER_2)
// 配置ApiInfo级当前应用的基本信息
.apiInfo(getAppInfo())
// 生成DocketBuilder
.select()
// 指定api类生成的包名,swagger会自动扫描指定包下的所有类生成说明文档
.apis(RequestHandlerSelectors.basePackage("com.xiaoxiao.springboot2.controller"))
// 指定实现了RestController接口的类生成说明文档
.apis(RequestHandlerSelectors.withClassAnnotation(RestController.class))
// 路径过滤规则,多数情况下不需要过滤
.paths(PathSelectors.any())
.build();
}
private ApiInfo getAppInfo(){
return new ApiInfoBuilder()
.title("api文档")
.description("小小的api文档说明")
.contact(new Contact("xiaoxiao", null, "xiaoyan159@163.com"))
.version("V0.1")
.termsOfServiceUrl(baseUrl)
.build();
}
}
```
> 注意上面Docket的apis方法只有一个会生效因此如果需要扫描不同包的controller的类及方法可使用withClassAnnotation方式
此刻直接访问localhost:8080/swagger-ui.html即可看到自动生成的说明文档但是这些说明文档是自动生成的可读性不高可通过在controller上增加注解的方式提高文档可读性
```
@ApiImplicitParams(
@ApiImplicitParam(name = "user", dataType = "User", allowEmptyValue = false, required = true)
)
@ApiOperation(value = "打印snapSwitch配置信息", notes = "注意:")
```
* 访问静态资源(图片、音频等文件)
> springboot中访问静态资源是按照如下顺序寻找的
> * classpath:/META-INF/resources/
> * classpath:/resources/
> * classpath:/static/
> * classpath:/public/
> * /
>
> 如果需要自定义静态资源位置可以在application.properties中增加配置
> * spring.resources.static-locations=classpath:/
> * spring.mvc.static-path-pattern=/**
* 关于跨域
解决跨域问题springboot中可以通过两种方式
1. 在请求上添加注解@CrossOrigin(value ="http://localhost:8081")
2. 通过下面的代码
```java
@Configuration
public class WebMvcConfig implements WebMvcConfigurer{
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("http://localhost:8081").allowedMethods("*").allowedHeaders("*");
}
}
```

View File

@@ -0,0 +1,44 @@
## 整合MyBatis
- 首先在pom.xml中配置mybatis的依赖,MySql连接库
```
<!--mybatis依赖-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!--mysql连接-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
```
- 然后在application.properties中添加数据库连接配置
```
spring.datasource.url=jdbc:mysql://localhost:3306/springboot2Test?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false
spring.datasource.password=root
spring.datasource.username=xiaoyan159
# 注意注意
mybatis.mapper-locations=classpath:com/xiaoxiao/springboot2/dao/*.xml
#mybatis.mapper-locations=classpath:mapper/*.xml #这种方式需要自己在resources目录下创建mapper目录然后存放xml
mybatis.type-aliases-package=com.xiaoxiao.springboot2.pojo #这里配置mybatis中别名自动生成的包名
# 驼峰命名规范 如:数据库字段是 order_id 那么 实体字段就要写成 orderId
mybatis.configuration.map-underscore-to-camel-case=true
```
> 注意! 如果配置的mybatis.mapper-locations同dao包一致在打包时默认可能不会将xml文件打包进jar中需要在pom.xml文件中特殊声明
```
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
```

17
springboot/lab1.md Normal file
View File

@@ -0,0 +1,17 @@
## spring-security
1. 首先在pom文件中增加依赖
```xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
```
2. 可选项在application.yml文件中配置默认的用户名密码
```yaml
spring:
security:
user:
name: xiaoyan159
password: xiaoyan159062
roles: admin
```

View File

@@ -0,0 +1,46 @@
## 定时任务
### spring自带定时任务Scheduled
* 首先在SpringBootApplication上添加启动定时任务的注解@EnableScheduling
* 然后编写定时任务,定时任务的类需要是一个@Component需要spring可以扫描到该组件
* 首先使用 @Scheduled 注解开启一个定时任务。
* fixedRate 表示任务执行之间的时间间隔,具体是指两次任务的开始时间间隔,即第二次任务开始时,第一次任务可能还没结束。
* fixedDelay 表示任务执行之间的时间间隔,具体是指本次任务结束到下次任务开始之间的时间间隔。
* initialDelay 表示首次任务启动的延迟时间。
* 所有时间的单位都是毫秒。
* 上面这是一个基本用法,除了这几个基本属性之外,@Scheduled 注解也支持 cron 表达式,使用 cron 表达式可以非常丰富的描述定时任务的时间。cron 表达式格式如下:
> \[秒\] \[分\] \[小时\] \[日\] \[月\] \[周\] \[年\]
|序号|说明|是否必填|允许填写的值|允许的通配符|
|:---:|:---:|:---:|:---:|:---:|
|1| 秒| 是| 0-59| - * /|
|2| 分| 是| 0-59| - * /|
|3| 时| 是| 0-23| - * /|
|4| 日| 是| 1-31| - * ? / L W|
|5| 月| 是| 1-12| or JAN-DEC - * /|
|6| 周| 是| 1-7| or SUN-SAT - * ? / L #|
|7| 年| 否| 1970-2099| - * /|
```
? 表示不指定值,即不关心某个字段的取值时使用。需要注意的是,月份中的日期和星期可能会起冲突,因此在配置时这两个得有一个是 ?
* 表示所有值,例如:在秒的字段上设置 *,表示每一秒都会触发
, 用来分开多个值,例如在周字段上设置 "MON,WED,FRI" 表示周一,周三和周五触发
- 表示区间,例如在秒上设置 "10-12",表示 10,11,12秒都会触发
/ 用于递增触发,如在秒上面设置"5/15" 表示从5秒开始每增15秒触发(5,20,35,50)
# 序号(表示每月的第几个周几),例如在周字段上设置"6#3"表示在每月的第三个周六,(用 在母亲节和父亲节再合适不过了)
周字段的设置,若使用英文字母是不区分大小写的 ,即 MON 与mon相同
L 表示最后的意思。在日字段设置上,表示当月的最后一天(依据当前月份,如果是二月还会自动判断是否是润年), 在周字段上表示星期六,相当于"7"或"SAT"(注意周日算是第一天)。如果在"L"前加上数字,则表示该数据的最后一个。例如在周字段上设置"6L"这样的格式,则表示"本月最后一个星期五"
W 表示离指定日期的最近工作日(周一至周五),例如在日字段上设置"15W"表示离每月15号最近的那个工作日触发。如果15号正好是周六则找最近的周五(14号)触发, 如果15号是周未则找最近的下周一(16号)触发如果15号正好在工作日(周一至周五),则就在该天触发。如果指定格式为 "1W",它则表示每月1号往后最近的工作日触发。如果1号正是周六则将在3号下周一触发。(注,"W"前只能设置具体的数字,不允许区间"-")
L 和 W 可以一组合使用。如果在日字段上设置"LW",则表示在本月的最后一个工作日触发(一般指发工资 )
```

View File

@@ -0,0 +1,66 @@
## 整合druid数据源
* 此处使用yml格式配置
```
spring:
profiles:
active: dev
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
url: jdbc:mysql://localhost:3306/springboot2Test?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false&serverTimezone=UTC
username: root
password: xiaoyan159
# 连接池初始化连接数
initial-size: 5
# 连接不够时新增连接数
min-idle: 5
# 连接池最大连接数
max-active: 30
# 最大等待时间
max-wait: 30000
# 配置检测可以关闭的空闲连接间隔时间
time-between-eviction-runs-millis: 60000
# 配置连接在池中的最小生存时间
min-evictable-idle-time-millis: 300000
validation-query: select '1' from dual
test-while-idle: true
test-on-borrow: false
test-on-return: false
# 打开PSCache并且指定每个连接上PSCache的大小
pool-prepared-statements: true
max-open-prepared-statements: 30
max-pool-prepared-statement-per-connection-size: 30
# 配置监控统计拦截的filters, 去掉后监控界面sql无法统计, 'wall'用于防火墙
filters: stat,wall
# Spring监控AOP切入点如x.y.z.service.*,配置多个英文逗号分隔
aop-patterns: com.xiaoxiao.springboot2.servie.*
# WebStatFilter配置
web-stat-filter:
enabled: true
# 添加过滤规则
url-pattern: /*
# 忽略过滤的格式
exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'
# StatViewServlet配置
stat-view-servlet:
enabled: true
# 访问路径为/druid时跳转到StatViewServlet
url-pattern: /druid/*
# 是否能够重置数据
reset-enable: false
# 需要账号密码才能访问控制台
login-username: druid
login-password: druid123
# IP白名单
# allow: 127.0.0.1
# IP黑名单共同存在时deny优先于allow
# deny: 192.168.1.218
# 配置StatFilter
filter:
stat:
log-slow-sql: true
```

View File

@@ -0,0 +1,72 @@
## 文件上传
* 首先介绍静态资源文件夹springboot中默认的静态资源文件目录
从上到下共5个当我们请求静态资源时spring会按照配置寻找指定
资源,我们可以在application配置文件中配置静态资源文件夹
```yaml
spring:
resources:
static-locations: classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,classpath:/,file:./upload/
mvc:
static-path-pattern: /** # 此配置使用Ant语法说明自动匹配/路径及其子路径
```
* 文件上传建议上传到resources静态资源目录下我们可以通过此方法获取到当前程序运行的文件目录
```java
public class PathUtils {
public static String getResourceBasePath() {
// 获取跟目录
File path = null;
try {
path = new File(ResourceUtils.getURL("classpath:").getPath());
} catch (FileNotFoundException e) {
// nothing to do
}
if (path == null || !path.exists()) {
path = new File("");
}
String pathStr = path.getAbsolutePath();
// 如果是在eclipse中运行则和target同级目录,如果是jar部署到服务器则默认和jar包同级
pathStr = pathStr.replace("\\target\\classes", "");
return pathStr;
}
}
```
* 如果需要控制上传文件的大小可以在application.yml中配置
```yaml
spring:
servlet:
multipart:
max-file-size: 1024MB
max-request-size: 1024MB
```
* 然后在Controller中增加上传文件接口即可
```java
@RestController
@RequestMapping("/upload")
public class FileUploadController {
@RequestMapping("/single")
public boolean uploadSingleFile(@RequestParam("file")/*此处注解应该也可以使用RequestBody*/ MultipartFile file/*上传文件均通过该参数获取即可*/){
if (file == null || file.getSize()<=0){
throw new ServiceException(ServiceException.ServiceExceptionEnum.FILE_IS_EMPTY);
}
try {
File destFolder = new File(PathUtils.getResourceBasePath(),"/upload");
if (!destFolder.exists()||!destFolder.isDirectory()){
destFolder.mkdirs();
}
// MultipartFile提供transferTo的工具方法可以将文件直接复制到指定位置
file.transferTo(new File(destFolder.getAbsolutePath()+"/"+file.getOriginalFilename()));
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
}
```

10
springboot/热部署.md Normal file
View File

@@ -0,0 +1,10 @@
## 热部署
* 首先在pom.xml中增加配置
```
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional> <!-- 可选 -->
</dependency>
```

View File

@@ -0,0 +1,203 @@
## 返回数据全局统一
* 首先定义格式化数据实体类
```
public class CommonResult<T> implements Serializable {
public static Integer CODE_SUCCESS = 0;
/**
* 错误码
*/
private Integer code;
/**
* 错误提示
*/
private String message;
/**
* 返回数据
*/
private T data;
/**
* 将传入的 result 对象,转换成另外一个泛型结果的对象
*
* 因为 A 方法返回的 CommonResult 对象,不满足调用其的 B 方法的返回,所以需要进行转换。
*
* @param result 传入的 result 对象
* @param <T> 返回的泛型
* @return 新的 CommonResult 对象
*/
public static <T> CommonResult<T> error(CommonResult<?> result) {
return error(result.getCode(), result.getMessage());
}
public static <T> CommonResult<T> error(Integer code, String message) {
Assert.isTrue(!CODE_SUCCESS.equals(code), "code 必须是错误的!");
CommonResult<T> result = new CommonResult<>();
result.code = code;
result.message = message;
return result;
}
public static <T> CommonResult<T> success(T data) {
CommonResult<T> result = new CommonResult<>();
result.code = CODE_SUCCESS;
result.data = data;
result.message = "";
return result;
}
@JsonIgnore // 忽略,避免 jackson 序列化给前端
public boolean isSuccess() { // 方便判断是否成功
return CODE_SUCCESS.equals(code);
}
@JsonIgnore // 忽略,避免 jackson 序列化给前端
public boolean isError() { // 方便判断是否失败
return !isSuccess();
}
public static Integer getCodeSuccess() {
return CODE_SUCCESS;
}
public static void setCodeSuccess(Integer codeSuccess) {
CODE_SUCCESS = codeSuccess;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
```
```
public class ServiceException extends RuntimeException{
/**
* 错误码
*/
private final Integer code;
public ServiceException(ServiceExceptionEnum serviceExceptionEnum) {
// 使用父类的 message 字段
super(serviceExceptionEnum.getMessage());
// 设置错误码
this.code = serviceExceptionEnum.getCode();
}
public Integer getCode() {
return code;
}
// ServiceExceptionEnum.java
public enum ServiceExceptionEnum {
// ========== 系统级别 ==========
SUCCESS(0, "成功"),
SYS_ERROR(2001001000, "服务端发生异常"),
MISSING_REQUEST_PARAM_ERROR(2001001001, "参数缺失"),
// ========== 用户模块 ==========
USER_NOT_FOUND(1001002000, "用户不存在"),
// ========== 订单模块 ==========
// ========== 商品模块 ==========
;
/**
* 错误码
*/
private int code;
/**
* 错误提示
*/
private String message;
ServiceExceptionEnum(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
}
```
* 然后通过@RestControllerAdvice(basePackages = {"com.xiaoxiao.springboot2.controller"})拦截controller的返回response统一处理返回值
```
// 此处增加basePackages配置只拦截自有controller的返回值不影响druid以及swagger的页面正常显示
@RestControllerAdvice(basePackages = {"com.xiaoxiao.springboot2.controller"})
public class CommonResultControllerAdvice implements ResponseBodyAdvice {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public boolean supports(MethodParameter methodParameter, Class aClass) {
return true;
}
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
if (o instanceof CommonResult){
return o;
}
return CommonResult.success(o);
}
/**
* 处理业务异常
* */
@ExceptionHandler(value = ServiceException.class)
public CommonResult serviceExceptionHandler(HttpServletRequest req, ServiceException ex){
return CommonResult.error(ex.getCode(),ex.getMessage());
}
/**
* 处理其他异常
* */
@ExceptionHandler(value = Exception.class)
public CommonResult exceptionHandler(HttpServletRequest req, Exception e) {
// 记录异常日志
logger.error("[exceptionHandler]", e);
// 返回 ERROR CommonResult
return CommonResult.error(ServiceException.ServiceExceptionEnum.SYS_ERROR.getCode(),
ServiceException.ServiceExceptionEnum.SYS_ERROR.getMessage());
}
}
```