在 Spring Boot 应用程序上保留 JPA(加上 Jackson)中的“计算”字段
Posted
技术标签:
【中文标题】在 Spring Boot 应用程序上保留 JPA(加上 Jackson)中的“计算”字段【英文标题】:Persist "computed" field in JPA (plus Jackson) on Spring Boot application 【发布时间】:2018-02-16 19:03:58 【问题描述】:我有一个 JPA 实体,看起来像:
public final class Item implements Serializable
@Column(name = "col1")
private String col1;
@Column(name = "col2")
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private String col2;
@Column(name = "col3")
private String col3;
Item()
public Item(String col1, String col2)
this.col1 = col1;
this.col2 = col2;
col3 = col1 + col2 + "some other stuff";
// getters, equals, hashCode and toString
我希望能够坚持col3
。我正在通过 POST 发送一个请求,如下所示:
"col1": "abc",
"col2": "def"
...我收到这样的信息:
[
"createdAt": "2017-09-07T19:18:17.04-04:00",
"updatedAt": "2017-09-07T19:18:17.04-04:00",
"id": "015e5ea3-0ad0-4703-af04-c0a3d46aa836",
"col1": "abc",
"col3": null
]
最终,col3
没有被持久化在数据库中。我没有任何二传手。
有什么办法可以做到吗?
更新
公认的解决方案是“侵入性较小”的解决方案。 Jarrod Roberson 提出的建议也完美无缺。最终,您可以通过在 col2
上使用 setter 并在那里设置 col3
的值来实现相同的效果——但我不喜欢这个……虽然是个人喜好。
【问题讨论】:
col3
这样的字段被称为计算字段是有原因的,您始终可以从其他字段“计算”它们。你能提供一个有效的用例吗?为什么你不能用一个简单的 getter 来解决这个问题?类似:public String getCol3() col1 + col2 + "some other stuff"
并删除 col3
属性。
@vl4d1m1r4 我一直都是这样,直到“需要将这个值持久化”也在数据库中。我之前在吸气剂中返回它。该字段实际上仅由 col2
组成:最后 3 个字符(并且它永远不会改变)。我实际上可以修改用户代理来发送它,但这没有任何意义,因为我已经有了整个数据,我可以拆分它并获取所需的部分。
【参考方案1】:
之所以不持久化,是因为您提供的带有col1
和col2
属性的构造函数从未真正被调用过。当spring从您发送到服务器的JSON进行映射(在jackson的帮助下)时,它使用默认构造函数来创建对象,然后调用setter(有时通过反射)来设置值。因此,col3
存储在数据库中的值始终为空。请参阅 Jarrod Roberson 的回答如何解决它:)。
【讨论】:
你说得对,我最初“倾向于”为col2
设置一个设置器(并在那里计算col3
),因为这是我最初使它工作的唯一方法......但我喜欢Jarrod Roberson 回答得更好。感谢您的提示!【参考方案2】:
你要找的是@JsonCreator
,它是这样使用的:
@JsonCreator()
public Item(@JsonProperty("col1") String col1, @JsonProperty("col2") String col2)
this.col1 = col1;
this.col2 = col2;
this.col3 = col1 + col2 + "some other stuff";
然后删除默认的无参数构造函数,Jackson 将使用这个构造函数,你想要的就会发生自动魔术。
这是一个非常古老的 feature 来自
1.x
时代。您还可以注释static
工厂方法 而是在需要的情况下创建构造函数private
使用更复杂的逻辑,比如用 a 构造的东西 建造者模式。
这是一个例子:
@JsonCreator()
public static Item construct(@JsonProperty("col1") String col1, @JsonProperty("col2") String col2)
return new Item(col1, col2, col1 + col2 + "some other stuff");
private Item(final String col1, final String col2, final String col3)
this.col1 = col1;
this.col2 = col2;
this.col3 = col3;
【讨论】:
这很酷。我以前使用过@JsonCreator
,但我没有想到在这里使用。一件事:我必须保留默认构造函数;看起来系统已经使用这个实体的方式(它实际上更复杂,因为它在一个集合中并且也包装在一个不同的对象中)。不过不确定。感谢您的提示!
我认为 JPA/Hibernate 是导致您必须保留默认构造函数的原因。 JPA/Hibernate 正在削弱实际的中/大型项目。啊!【参考方案3】:
虽然有一个公认的答案,但它似乎过于复杂,需要删除违反 JPA 规范的默认构造函数。
第 2.1 节
实体类必须有一个无参数的构造函数。实体类可以 还有其他构造函数。无参数构造函数必须是公共的 或受保护。
没有必要让杰克逊参与进来。您可以简单地使用 JPA 预持久侦听器来确保在刷新操作之前设置 col3
https://docs.jboss.org/hibernate/orm/4.0/hem/en-US/html/listeners.html
public final class Item implements Serializable
@Column(name = "col1")
private String col1;
@Column(name = "col2")
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private String col2;
@Column(name = "col3")
private String col3;
Item()
@PrePersist
public void updateCol3()
col3 = col1 + col2;
【讨论】:
我还需要检查这种方法。我认为它不起作用的唯一原因是因为这个类的层次结构很长。 “基”类中已经存在一个@PrePersist
,尽管先调用super
,然后这个可能会起作用。这是可能的,但我不知道这是否会产生“副作用”。稍后当我能够测试它时会更新它。感谢您的信息!
JPA 监听器将在类层次结构中以一致的顺序执行:***.com/a/26063003/1356423以上是关于在 Spring Boot 应用程序上保留 JPA(加上 Jackson)中的“计算”字段的主要内容,如果未能解决你的问题,请参考以下文章
在 Spring Boot 应用程序上添加 jpa 依赖项时,Okta Spring Boot 不起作用
关于如何使用 jpa-repositiroies 在 Spring-Boot 上持久保存数据的指南/教程
如何在 Spring Boot 应用程序的同一个域类上同时使用 Spring Data JPA 和 Spring Data Elasticsearch 存储库?