JPA - 保存后自动生成的字段为空

Posted

技术标签:

【中文标题】JPA - 保存后自动生成的字段为空【英文标题】:JPA - Auto-generated field null after save 【发布时间】:2018-05-14 02:08:47 【问题描述】:

我有一个Account 实体,我正在尝试使用save 函数来持久化它。我的代码:

@Override
public Account createAccount(String pin) 

    Account account = new Account();
    account.setBalance(0L);
    account.setPin(pin);

    return accountRepository.save(account);


现在我的实体类有一个名为accountNumber 的自动生成字段。我的实体类:

@Entity
@Table(name = "accounts")
@Data
public class Account 

    @Column(name = "account_number", length = 32, insertable = false)
    private String accountNumber;

    private Long balance;



现在调用save 后,返回的实体有accountNumbernull,但我可以在intellij 数据库视图中看到它实际上不为空。所有其他自动生成的字段(如 id 等)都存在于返回的实体中,只是 accountNumber 为空。 accountNumber 的默认值在 sql 文件中设置:

ALTER TABLE accounts
  ALTER COLUMN account_number SET DEFAULT DefaultValueSerializer(TRUE, TRUE, 12);

这里,DefaultValueSerializer 是生成帐号的函数。

我已经尝试过其他可用的解决方案,例如使用 saveAndFlush() 等,但在我的情况下没有任何效果。可能是什么问题?

【问题讨论】:

因为 hibernate 不是这样工作的,所以它对在您的数据库中自动生成列一无所知,并且不会像这样自动更新值。您需要从数据库中重新检索 Account 以获取该字段。 @M.Deinum 也试过了。保存后,我使用findOne 函数检索帐户。令人惊讶的是,返回的 accountNumber 仍然是 null。由于某种原因,findOne 不会导致选择查询,因为我看不到任何由于findOne 函数调用而记录的休眠查询。 因为,正如您所注意到的,它不会从数据库中检索它。使用 ORM 工具,如果具有请求 id 的所需实体已存在于缓存中,则该工具将首先检查一级缓存,并且您可以获得该实例。您首先必须清除缓存 (entitymanager.clear()) 并重新加载它或 refresh (entityManager.refresh) 实体。 你确实是对的。我最终为accountNumber 字段使用了@Generated(value = INSERT) 注释。它让 JPA 知道该值是在插入期间生成的,因此它将在保存后再次从数据库中查询该值以获取更新的值。感谢您为我指明正确的方向。 注解@Generated 似乎是hibernate 特定的所以不是JPA 【参考方案1】:

正如评论中提到的,Hibernate 不知道数据库引擎级别发生了什么,因此它看不到生成的值。

将帐号的生成转移到 JPA 级别而不是使用数据库默认值是明智的。

我建议你研究注解@GeneratedValue 和相关的东西,比如@SequenceGenerator。这样生成帐号的控制在JPA级别,保存后不需要刷新实体之类的东西。

一个起点:Java - JPA - Generators - @SequenceGenerator

对于非 id 字段,可以在使用 @PrePersist 注释的方法中生成值,正如其他答案所建议的那样,但您可以在 Accounts 构造函数中进行初始化。

另请参阅this answer 了解选项。

【讨论】:

这不起作用,因为 @GeneratedValue 仅适用于 @Id 字段。 好的,你还有其他的@ID 字段吗?帐号是否不唯一或无法用作 ID?【参考方案2】:

您可以在将其字段设置为其默认值的实体内创建一个带注释的@PrePersist 方法。

这样 jpa 就会知道默认值。

对于不同的实体生命周期事件https://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/listeners.html,还有其他此类注释可用

附:如果您决定采用这种方式,请记住删除insertable = false

【讨论】:

【参考方案3】:

使用

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)

为您的 ID。并将您的储蓄留给saveAndFlush,这样您就可以立即看到更改(如果有)。我还建议将 ID 和帐号分开。它们不应该相同。尝试调试您的程序并查看值停止传递的位置。

【讨论】:

以上是关于JPA - 保存后自动生成的字段为空的主要内容,如果未能解决你的问题,请参考以下文章

hibernate JPA:可选值生成

jpa如何设置某个字段存到数据库后不能修改,当然整个对象删除除外!

Jpa + Spring - 从数据库读取后自动设置瞬态字段值

从联系人中选择自动填充地址后,iOS UITextField 文本字段为空

持久化后 JPA 实体 CreateTimestamp 为空

JPA JPQL 怎么判断某个字段是不是为空