【问题标题】:Minimize JSON to deserialize and persist complex POJO with JPA/Hibernate使用 JPA/Hibernate 最小化 JSON 以反序列化和持久化复杂的 POJO
【发布时间】:2023-03-11 17:17:01
【问题描述】:

我有一个相当复杂的 POJO,我需要从 JSON 字符串反序列化并保存在 MySQL 数据库中。下面是一个非常简化的示例类:

@Entity
@Table(name="a")
public class A{
    private Long id;
    private B b;
    private C c;
    private D d;
}

@ManyToOne
@JoinColumn(name="b_id")
public B getB(){
    return this.b;
}

public void setB(B b){ this.b = b; }

@ManyToOne
@JoinColumn(name="c_id")
public C getC(){ 
    return this.c;
}

public void setC(C c){ this.c = c; }

@ManyToOne
@JoinColumn(name="d_id")
public D getD(){ 
    return this.d;
}

public void setD(D d){ this.d = d; }

每个类 BCD 也有许多字段和对象(有些甚至需要更多的对象和字段)根据数据库架构不能为空,我可以' t 改变。我可以反序列化并坚持这个没问题,但是这样做所需的 JSON 真的很大。我只需要保留反序列化的A,所以我真的只需要来自BCD_id 字段。

现在我的 JSON 是这样的:

{
  "id":1, 
  "b":{"id":2, ...}, 
  "c":{"id":3, ...}, 
  "d":{"id":4, ...}
}

我必须填写所有不可为空的数据库字段。我想做的是读取一个 JSON 字符串,例如:

{
  "id":1,
  "b_id":2,
  "c_id":3,
  "d_id":4
}

只需让 Hibernate/JPA 更新数据库中的这些字段。我认为真正棘手的部分是我的应用程序中的其他类/方法将需要整个对象层次结构才能从数据库中读取。我唯一可以使用 _id 字段的时间是在 JSON 的反序列化期间。在这种情况下,我只需要更新最顶层的对象(在我的示例中为A)和其他一些琐碎的字段。在一个完美的世界里,我可以在我的课堂上添加一些注释来解决这个问题,但我还没有找到任何能够做到这一点的东西。

这甚至可能吗?如果是这样,可以用 JPA/Jackson 注释来完成吗?

谢谢!

【问题讨论】:

  • 您可以编写自定义反序列化器并使用@JsonDeserialize(using = ...)
  • 我知道自定义反序列化器(我在应用程序的其他地方使用它们)。您能否详细说明这可能如何解决我的问题?
  • 好吧,当你反序列化时,你可以访问JsonParser。而JsonParser 可以帮助您获得所需的信息(当前会员姓名等)。
  • 你控制传入的 JSON 吗?
  • @Jukka,在某种程度上,是的。另一个开发团队正在编写它并通过 RESTful 调用将其发送到我的应用程序。根据我们无法更改的现有数据库架构,我可以要求他们可能会或可能无法发送给我的某些字段。

标签: java json hibernate jpa jackson


【解决方案1】:

如果将 JSON 直接映射到您的实体是不正确的,我会简单地通过中间对象将传入的 JSON 字符串转换为您的实体(如果您愿意,可以将其称为 DTO)。

我也有一种直觉,如果你保存关联的对象引用(@ManyToOnes)只填充了 id,Hibernate 实际上会正确保存关联(这里的根对象代表 A):

{
    "id" : 1,
    "b" : {
        "id" : 2
    },
    ...
}

确保您没有级联 b、c 等字段的持久化操作。

【讨论】:

  • 哦,如果您真的不需要 getter 和 setter,请考虑使用公共字段。
  • “我也有一种直觉,如果你保存关联的对象引用(@ManyToOnes)只填充了 id,Hibernate 实际上会正确保存关联(这里的根对象代表 A):”即使我省略了@ManyToOnes 上的架构指定的不可为空的字段,您认为这会起作用吗?
  • 事实证明,您只需要填充 @ManyToOne ID 字段是对的。我什至不需要 DTO。感谢您的帮助!
【解决方案2】:

您应该创建只定义您想要反序列化的字段的新类。例如,如果您只想反序列化 ID 您的类可能如下所示:

class JsonEntity {

    private Long id;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @JsonAnySetter
    public void setUnknownProperties(String name, String value) {
        //do nothing
    }

    @Override
    public String toString() {
        return String.valueOf(id);
    }
}

在此示例中,注释 JsonAnySetter 可以解决问题。现在,您必须创建一些类来模拟您的A 类。例如:

class DtoA extends JsonEntity {

    private JsonEntity b;
    private JsonEntity c;
    private JsonEntity d;

    public JsonEntity getB() {
        return b;
    }

    public void setB(JsonEntity b) {
        this.b = b;
    }

    public JsonEntity getC() {
        return c;
    }

    public void setC(JsonEntity c) {
        this.c = c;
    }

    public JsonEntity getD() {
        return d;
    }

    public void setD(JsonEntity d) {
        this.d = d;
    }

    @Override
    public String toString() {
        return "A [id=" + getId() + ", b=" + b + ", c=" + c + ", d=" + d + "]";
    }
}

现在,当我们有了新的 JSON 数据模型时,我们可以对其进行测试。例如我们可以解析下面的 JSON:

{
  "id":1, 
  "b":{"id":2, "p1":"v1", "p2":"v2"}, 
  "c":{"id":3, "p3":"v3", "p4":"v4", "p5":"v5"}, 
  "d":{"id":4, "p6":"v6"}
}

反序列化示例:

ObjectMapper objectMapper = new ObjectMapper();
DtoA a = objectMapper.readValue(json, DtoA.class);
System.out.println(a);

上面的程序打印:

A [id=1, b=2, c=3, d=4]

现在,您必须在 DtoA 类中实现 toA() 方法,如下所示:

public A toA() {
    A a = new A(getId());
    a.setB(new B(getB().getId()));
    a.setC(new C(getC().getId()));
    a.setD(new D(getD().getId()));

    return a;
}

让我知道它是否适合你。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-04-09
    • 1970-01-01
    • 1970-01-01
    • 2013-11-12
    • 1970-01-01
    • 1970-01-01
    • 2011-05-09
    • 2017-05-13
    相关资源
    最近更新 更多