我有一个JPA持久层,有许多@Entity类,它们有许多OneToMany和ManyToMany关系 . 我想通过RestEasy将Jackson2作为序列化程序公开给JSON作为REST服务 .
我知道@JsonIdentityInfo用于解析循环引用 . 问题在于我需要暴露实体字段的不同子集的不同REST服务 . 此外,我需要为集合公开不同级别的depts(OneToMany,OneToOne等) . 例如,对于这个简单的实体:
class User {
Long id;
String name;
Company company;
}
class Company {
Long id;
String name;
List<User> users;
List<Product> products;
}
class Product {
Long id;
String name;
List<User> users;
}
这个REST服务:
class MyResource {
User getUser() { //... }
List<User> getUsers() { //... }
Company getCompany() { //... }
List<Company> getComanies() { //... }
}
在方法 getUser()
中,我需要返回包含内部Company对象的完整User对象的JSON . 但该公司当然只需要包含他们的 id
和 name
字段而不是完整的用户列表 . 更重要的是内部公司JSON不得包含 products
!这是合乎逻辑的 . 如果我们得到用户,我们不需要与该用户相关的公司产品 . 如果我们需要它们,我们将发送另一个REST请求 .
但是在方法 getCompany()
中,我需要返回带有完整Company对象的JSON,包括User和Product对象的内部JSON数组 . 当然,这次User对象不需要包含Company对象的内部JSON .
因此我无法使用@JsonIgnore . 在一种情况下,我们需要一些领域而在另一种情况下我们不需要
现在我想出了使用Jackson视图的方法(@JsonView注释) . 我为每个MyResource getter提供了具有不同视图的View类 .
public class Views {
public static class User {}
public static class Users {}
public static class Company {}
public static class Companies {}
// etc...
}
和MyResoruce类一样
class MyResource {
@JsonView(Views.User.class)
User getUser() { //... }
@JsonView(Views.Users.class)
List<User> getUsers() { //... }
@JsonView(Views.Company.class)
Company getCompany() { //... }
@JsonView(Views.Companies.class)
List<Company> getComanies() { //... }
}
并为每个实体都有一个MixIn类,每个字段都注释为
public abstract class UserMixIn {
@JsonView({ Views.User.class, Views.Users.class, Views.Company.class, Views.Companies.class })
public abstract Long getId();
@JsonView({ Views.User.class, Views.Users.class, Views.Company.class, Views.Companies.class })
public abstract String getName();
@JsonView({ Views.User.class, Views.Users.class })
public abstract Company getCompany();
}
public abstract class CompanyMixIn {
@JsonView({ Views.Company.class, Views.Companies.class, Views.User.class, Views.Users.class })
public abstract Long getId();
@JsonView({ Views.Company.class, Views.Companies.class, Views.User.class, Views.Users.class })
public abstract String getName();
@JsonView({ Views.Company.class, Views.Companies.class })
public abstract List<User> getUsers();
@JsonView({ Views.Company.class, Views.Companies.class })
public abstract List<Product> getProducts();
}
public abstract class ProductMixIn {
@JsonView({ Views.Company.class, Views.Companies.class })
public abstract Long getId();
@JsonView({ Views.Company.class, Views.Companies.class })
public abstract String getName();
public abstract List<User> getUsers();
}
支持案例的复数,其中 getUsers()
不需要每个用户的完整内部公司对象(性能) . 当然只有示例类 . 真正的课程更大更复杂 .
我不喜欢这种方法,因为我担心将来它可能是一场噩梦(太多不可管理的观点) . 也许有将JPA实体暴露为REST服务的通用方法?我相信这是一项相当普遍的任务 . 但无法找到关于其他人如何做到这一点的任何可理解的信息 . 也许是一些最佳实践 .
1 回答
您的服务层(以及您的REST控制器层)必须公开DTO(数据传输对象)而不是@Entity对象 .
Example :
对于服务1(专注于用户管理):
对于服务2(专注于公司管理):
(您的DTO的命名是你的)
How to :
您将需要Mappers将您的
@Entity
转换为DTO并将其转换为DTO . 有些lib存在,如Dozer或MapStruct(还有很多其他的) .