在上一篇中提到的解决方案是不完善的。很多情况下,你需要面对的接口文档是不可理喻的,他们并不知道什么是OpenAPI,甚至连我们上一篇中提到的统一格式返回也不愿意接受,这一篇中,我会为您展示Message定制化的小技巧。
我们以一下应用场景为例:
仍然,我们需要返回这样的一个对象:
{ "success": true, "data": {}, "message": "error message or success message" }
如果您看过我的上一篇博客,就会发现两者的区别,我们需要返回的字符串,现在并不是”errorMessage”,而是”message”。
无疑,这个改变是灾难性的,这就意味着,我们需要真对不同的接口,返回不同的message。
毫无疑问,通过controllerAdvice对所有的返回处理是无法做到这一点的,当然,有些情况下也可以,比如说,您可以创建一个超类,使他具有一个message属性,再让所有的返回都继承这个超类,进而,在controllerAdvice中,我们去判断返回体的类型,进而读取这一属性,将其设置为message的内容。
我相信您读到这里的时候一定和我一样倒吸了一口凉气,因为这样的写法不仅会使您的程序变得无比的复杂,更会破坏了您代码的优雅,更要命的是,这种写法更破坏了解耦,这无疑是对一个后端程序员尊严的挑战!
幸好,天无绝人之路,我为您提供了一种方法,是您不需要那么难看地去做这件事。
我们需要定义一个注解,像这样:
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface MessageBody { String value(); }
如您所见,这是一个普通到不能再普通的注解,我们能够用它来做什么呢?
答案是我们用它来标注方法,像这样:
@MessageBody( read more
依据网络请求 - Ant Design Pro中提出的后端接口规范,后端返回的数据至少应遵循以下模板
{ "success": true, "data": {}, "errorMessage": "error message" }
为了帮助前端更好地完成工作,后端应强化接口规范。
首先创建一个工具包,再设想中,只要导入工具包,就会自动完成接口的转化。
pom中我们只引入spring-boot-starter-web:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
然后我们定义要统一返回的Message类,这里使用泛型:
public class Message<T> implements Serializable { boolean success; String errorMessage; T data; Timestamp timestamp; public Message read more
Dao层
JPA的增删改查是无比简单的,它基于一个约定大于配置的逻辑。 加入我们要对User类进行查询,首先需要创建以下接口
public interface UserRepository extends JpaRepository<User, Integer> { }
其中,User应该填写对应的实体类,Integer应该填写主键的类型。 JpaRepository提供了基础的方法,使用
@Autowired private UserRepository userRepository;
注入后,就可以调用以下方法:
userRepository.findById()//按照主键查找 userRepository.findAll()//查找全部 userRepository.save()//储存 userRepository.saveAndFlush()//储存并重新读取 userRepository.deleteById()//按照主键查询 userRepository.deleteAll()//全部删除
更多方法请参照JPA文档。
其中,findById并不会直接返回一个User对象,由于查找结果可能为空,JPA选择返回一个Optional
userRepository.findById read more
JPA
springBoot整合了json转化插件Jackson,配合这一插件可以完成Json的序列化与反序列化。 定义一个类
@Data @NoArgsConstructor public Class Message{ private String name; private String sex; }
在一个Controller中,可以以Message作为接收参数和返回内容:
public Message messageResponse(@RequestBody message){ return message; }
如在以上的例子中,springBoot会将发送的json字符串中的name保存到Message的name属性中,并把sex保存到message的sex属性中(没有则保存空值). 了解了以上就能解决对象序列化与反序列化过程中遇到的大部分问题,仍然有几点值得强调:
(以下的部分需要在pom.xml中引入以下依赖):
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-json</artifactId> </dependency>
一. 一对多关系的风险及处理
在一对多关系中,以我们在Spring-boot data JPA(一)建库与一对多、多对多关系 - 荆凉凉 (stpjing.github.io)中创建的User与Admin类为例,User序列化时会序列化该User指向的Admin,同时Admin会序列化所有的下辖User对象,陷入无限迭代。
万幸Jackson提供了以下一对注解:@JsonBackReference与 @JsonManagedReference
通常,Admin与User有明显的父子关系,Admin是“父”,User是“子”。
@JsonBackReference用于“子”的一方,被该字段标注的属性不会被序列化。
@JsonManagedReference用于“父”的一方,其意义在于序列化时的自动注入。
@JsonManagedReference注解可以省略,但会造成被@JsonBackReference标注的属性不会被序列化,仍然以User与Admin为例(这里我们省略了User中Role的部分,多对多关系见下面的讨论)。整合了注释的实体类如下所示:
@Data read more
JPA只是一个简化对象关系映射来管理Java应用程序中的关系数据的规范。 它提供了一个平台,可以直接使用对象而不是使用SQL语句。
对于SpringBoot而言,整合JPA是容易的,只需要在maven中引入以下依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>
选取MySQL为例,引入mysql驱动:
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>
同时在application.yml中做如下配置:
spring: datasource: url: jdbc:mysql://127.0.0.1:3306/database?serverTimezone = GMT%2B8 & useUnicode=true #最后一项为使用utf-8编码 username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver jpa: database: MySQL database-platform: org.hibernate.dialect.MySQL5InnoDBDialect show-sql: true #是否自动显示sql语句 hibernate: ddl-auto: update
需要注意的是,ddl-auto有五个属性:
ddl-auto | 功能 |
---|---|
create | 程序每次时,检查标注的实体类,并依据实体类重新建表,会导致数据丢失 |
create-drop | 程序运行时,检查标注的实体类,并依据实体类重新建表,并在程序结束时销毁表 |
update | 程序运行时,检查标注的实体类,如果没有表就建表,如果当前表内字段属性与实体类中不符,则会更新表 |
validate | 程序运行时,检查标注的实体类,如果当前表内字段属性与实体类中不符,或不存在对应的表,则会更新表 |
none | 禁用 |
JPA可以协助使用者依据实体类创建表,这要求使用者提供标注,以下为实例:
@Entity //实体 @Table read more
JPA的增删改查是无比简单的,它基于一个约定大于配置的逻辑。 加入我们要对User类进行查询,首先需要创建以下接口
public interface UserRepository extends JpaRepository<User, Integer> { }
其中,User应该填写对应的实体类,Integer应该填写主键的类型。 JpaRepository提供了基础的方法,使用
@Autowired private UserRepository userRepository;
注入后,就可以调用以下方法:
userRepository.findById()//按照主键查找 userRepository.findAll()//查找全部 userRepository.save()//储存 userRepository.saveAndFlush()//储存并重新读取 userRepository.deleteById()//按照主键查询 userRepository.deleteAll()//全部删除
更多方法请参照JPA文档。
其中,findById并不会直接返回一个User对象,由于查找结果可能为空,JPA选择返回一个Optional
userRepository.findById read more
Json
springBoot整合了json转化插件Jackson,配合这一插件可以完成Json的序列化与反序列化。 定义一个类
@Data @NoArgsConstructor public Class Message{ private String name; private String sex; }
在一个Controller中,可以以Message作为接收参数和返回内容:
public Message messageResponse(@RequestBody message){ return message; }
如在以上的例子中,springBoot会将发送的json字符串中的name保存到Message的name属性中,并把sex保存到message的sex属性中(没有则保存空值). 了解了以上就能解决对象序列化与反序列化过程中遇到的大部分问题,仍然有几点值得强调:
(以下的部分需要在pom.xml中引入以下依赖):
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-json</artifactId> </dependency>
一. 一对多关系的风险及处理
在一对多关系中,以我们在Spring-boot data JPA(一)建库与一对多、多对多关系 - 荆凉凉 (stpjing.github.io)中创建的User与Admin类为例,User序列化时会序列化该User指向的Admin,同时Admin会序列化所有的下辖User对象,陷入无限迭代。
万幸Jackson提供了以下一对注解:@JsonBackReference与 @JsonManagedReference
通常,Admin与User有明显的父子关系,Admin是“父”,User是“子”。
@JsonBackReference用于“子”的一方,被该字段标注的属性不会被序列化。
@JsonManagedReference用于“父”的一方,其意义在于序列化时的自动注入。
@JsonManagedReference注解可以省略,但会造成被@JsonBackReference标注的属性不会被序列化,仍然以User与Admin为例(这里我们省略了User中Role的部分,多对多关系见下面的讨论)。整合了注释的实体类如下所示:
@Data read more
RestControllerAdvice
在上一篇中提到的解决方案是不完善的。很多情况下,你需要面对的接口文档是不可理喻的,他们并不知道什么是OpenAPI,甚至连我们上一篇中提到的统一格式返回也不愿意接受,这一篇中,我会为您展示Message定制化的小技巧。
我们以一下应用场景为例:
仍然,我们需要返回这样的一个对象:
{ "success": true, "data": {}, "message": "error message or success message" }
如果您看过我的上一篇博客,就会发现两者的区别,我们需要返回的字符串,现在并不是”errorMessage”,而是”message”。
无疑,这个改变是灾难性的,这就意味着,我们需要真对不同的接口,返回不同的message。
毫无疑问,通过controllerAdvice对所有的返回处理是无法做到这一点的,当然,有些情况下也可以,比如说,您可以创建一个超类,使他具有一个message属性,再让所有的返回都继承这个超类,进而,在controllerAdvice中,我们去判断返回体的类型,进而读取这一属性,将其设置为message的内容。
我相信您读到这里的时候一定和我一样倒吸了一口凉气,因为这样的写法不仅会使您的程序变得无比的复杂,更会破坏了您代码的优雅,更要命的是,这种写法更破坏了解耦,这无疑是对一个后端程序员尊严的挑战!
幸好,天无绝人之路,我为您提供了一种方法,是您不需要那么难看地去做这件事。
我们需要定义一个注解,像这样:
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface MessageBody { String value(); }
如您所见,这是一个普通到不能再普通的注解,我们能够用它来做什么呢?
答案是我们用它来标注方法,像这样:
@MessageBody( read more
依据网络请求 - Ant Design Pro中提出的后端接口规范,后端返回的数据至少应遵循以下模板
{ "success": true, "data": {}, "errorMessage": "error message" }
为了帮助前端更好地完成工作,后端应强化接口规范。
首先创建一个工具包,再设想中,只要导入工具包,就会自动完成接口的转化。
pom中我们只引入spring-boot-starter-web:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
然后我们定义要统一返回的Message类,这里使用泛型:
public class Message<T> implements Serializable { boolean success; String errorMessage; T data; Timestamp timestamp; public Message read more
Spring-boot
springBoot整合了json转化插件Jackson,配合这一插件可以完成Json的序列化与反序列化。 定义一个类
@Data @NoArgsConstructor public Class Message{ private String name; private String sex; }
在一个Controller中,可以以Message作为接收参数和返回内容:
public Message messageResponse(@RequestBody message){ return message; }
如在以上的例子中,springBoot会将发送的json字符串中的name保存到Message的name属性中,并把sex保存到message的sex属性中(没有则保存空值). 了解了以上就能解决对象序列化与反序列化过程中遇到的大部分问题,仍然有几点值得强调:
(以下的部分需要在pom.xml中引入以下依赖):
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-json</artifactId> </dependency>
一. 一对多关系的风险及处理
在一对多关系中,以我们在Spring-boot data JPA(一)建库与一对多、多对多关系 - 荆凉凉 (stpjing.github.io)中创建的User与Admin类为例,User序列化时会序列化该User指向的Admin,同时Admin会序列化所有的下辖User对象,陷入无限迭代。
万幸Jackson提供了以下一对注解:@JsonBackReference与 @JsonManagedReference
通常,Admin与User有明显的父子关系,Admin是“父”,User是“子”。
@JsonBackReference用于“子”的一方,被该字段标注的属性不会被序列化。
@JsonManagedReference用于“父”的一方,其意义在于序列化时的自动注入。
@JsonManagedReference注解可以省略,但会造成被@JsonBackReference标注的属性不会被序列化,仍然以User与Admin为例(这里我们省略了User中Role的部分,多对多关系见下面的讨论)。整合了注释的实体类如下所示:
@Data read more
JPA只是一个简化对象关系映射来管理Java应用程序中的关系数据的规范。 它提供了一个平台,可以直接使用对象而不是使用SQL语句。
对于SpringBoot而言,整合JPA是容易的,只需要在maven中引入以下依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>
选取MySQL为例,引入mysql驱动:
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>
同时在application.yml中做如下配置:
spring: datasource: url: jdbc:mysql://127.0.0.1:3306/database?serverTimezone = GMT%2B8 & useUnicode=true #最后一项为使用utf-8编码 username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver jpa: database: MySQL database-platform: org.hibernate.dialect.MySQL5InnoDBDialect show-sql: true #是否自动显示sql语句 hibernate: ddl-auto: update
需要注意的是,ddl-auto有五个属性:
ddl-auto | 功能 |
---|---|
create | 程序每次时,检查标注的实体类,并依据实体类重新建表,会导致数据丢失 |
create-drop | 程序运行时,检查标注的实体类,并依据实体类重新建表,并在程序结束时销毁表 |
update | 程序运行时,检查标注的实体类,如果没有表就建表,如果当前表内字段属性与实体类中不符,则会更新表 |
validate | 程序运行时,检查标注的实体类,如果当前表内字段属性与实体类中不符,或不存在对应的表,则会更新表 |
none | 禁用 |
JPA可以协助使用者依据实体类创建表,这要求使用者提供标注,以下为实例:
@Entity //实体 @Table read more
JPA的增删改查是无比简单的,它基于一个约定大于配置的逻辑。 加入我们要对User类进行查询,首先需要创建以下接口
public interface UserRepository extends JpaRepository<User, Integer> { }
其中,User应该填写对应的实体类,Integer应该填写主键的类型。 JpaRepository提供了基础的方法,使用
@Autowired private UserRepository userRepository;
注入后,就可以调用以下方法:
userRepository.findById()//按照主键查找 userRepository.findAll()//查找全部 userRepository.save()//储存 userRepository.saveAndFlush()//储存并重新读取 userRepository.deleteById()//按照主键查询 userRepository.deleteAll()//全部删除
更多方法请参照JPA文档。
其中,findById并不会直接返回一个User对象,由于查找结果可能为空,JPA选择返回一个Optional
userRepository.findById read more
swagger
在上一篇中提到的解决方案是不完善的。很多情况下,你需要面对的接口文档是不可理喻的,他们并不知道什么是OpenAPI,甚至连我们上一篇中提到的统一格式返回也不愿意接受,这一篇中,我会为您展示Message定制化的小技巧。
我们以一下应用场景为例:
仍然,我们需要返回这样的一个对象:
{ "success": true, "data": {}, "message": "error message or success message" }
如果您看过我的上一篇博客,就会发现两者的区别,我们需要返回的字符串,现在并不是”errorMessage”,而是”message”。
无疑,这个改变是灾难性的,这就意味着,我们需要真对不同的接口,返回不同的message。
毫无疑问,通过controllerAdvice对所有的返回处理是无法做到这一点的,当然,有些情况下也可以,比如说,您可以创建一个超类,使他具有一个message属性,再让所有的返回都继承这个超类,进而,在controllerAdvice中,我们去判断返回体的类型,进而读取这一属性,将其设置为message的内容。
我相信您读到这里的时候一定和我一样倒吸了一口凉气,因为这样的写法不仅会使您的程序变得无比的复杂,更会破坏了您代码的优雅,更要命的是,这种写法更破坏了解耦,这无疑是对一个后端程序员尊严的挑战!
幸好,天无绝人之路,我为您提供了一种方法,是您不需要那么难看地去做这件事。
我们需要定义一个注解,像这样:
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface MessageBody { String value(); }
如您所见,这是一个普通到不能再普通的注解,我们能够用它来做什么呢?
答案是我们用它来标注方法,像这样:
@MessageBody( read more
依据网络请求 - Ant Design Pro中提出的后端接口规范,后端返回的数据至少应遵循以下模板
{ "success": true, "data": {}, "errorMessage": "error message" }
为了帮助前端更好地完成工作,后端应强化接口规范。
首先创建一个工具包,再设想中,只要导入工具包,就会自动完成接口的转化。
pom中我们只引入spring-boot-starter-web:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
然后我们定义要统一返回的Message类,这里使用泛型:
public class Message<T> implements Serializable { boolean success; String errorMessage; T data; Timestamp timestamp; public Message read more