首页 文章

Jackson JSON序列化JPA实体

提问于
浏览
0

我有一个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 . 但该公司当然只需要包含他们的 idname 字段而不是完整的用户列表 . 更重要的是内部公司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 回答

  • 2

    您的服务层(以及您的REST控制器层)必须公开DTO(数据传输对象)而不是@Entity对象 .

    Example :

    对于服务1(专注于用户管理):

    public class UserDto {
        private Long id;
        private String name;
        private CompanyDtoLight company;
    }
    
    public class CompanyDtoLight {
        private Long id;
        private String name;
    }
    

    对于服务2(专注于公司管理):

    public class CompanyDto {
        private Long id;
        private String name;
        List<UserDtoLight > users;
        List<ProductDtoLight > products;
    }
    
    public class UserDtoLight {
        private Long id;
        private String name;
    }
    
    class ProductDtoLight {
        private Long id;
        private String name;
    }
    

    (您的DTO的命名是你的)

    How to :

    您将需要Mappers将您的 @Entity 转换为DTO并将其转换为DTO . 有些lib存在,如DozerMapStruct(还有很多其他的) .

相关问题