【问题标题】:Can I map one single field to two columns in the database using Hibernate?我可以使用 Hibernate 将一个字段映射到数据库中的两列吗?
【发布时间】:2021-05-16 14:00:41
【问题描述】:

考虑以下 Java Hibernate Entity 示例(代码 sn-p):

...

@Column
@Converter(...)
private String columnOne;

public String getcolumnOne() {
    return columnOne;
}

public setColumnOne(String columnOne) {
    this.columnOne = columnOne;
}

...

这会生成一个COLUMN_ONE是数据库。

我如何/应该如何在数据库表中生成第二列COLUMN_TWO,它是从同一字段columnOne 字段生成的?此第二列在使用此实体的 Java 代码中并不重要,因此我的实体不应将此第二列导出给此实体的用户。 COLUMN_TWO 将用于数据库本身(用于某些索引)和其他应用程序。

【问题讨论】:

  • 一个非常不寻常的情况,在第一列的setter中为两列设置值或者在数据库中创建一个生成的列。如果您不想获取列,则可以使用 @Query 选择列
  • @GabrielPetrovay 也许this 是您想要实现的目标。

标签: java hibernate jpa


【解决方案1】:

免责声明:我没有尝试过,但我相信 Hibernate 可以访问私有方法,因为它可以访问私有字段。

对于这个例子,假设你有一个 LocalDateTime 类型的属性,正如类的用户所看到的,但是你想将值存储为 ISO-8601 格式的字符串 (VARCHAR),使用日期在一列,时间在另一列。

所以你有正常的private 字段和正常的public getter/setter 方法。你不要把@Column放在上面,而是使用@Transient让JPA忽略这个属性。

然后,您创建两个派生的 getter/setter 方法对,将它们设为 private,这样类的用户就不会看到它们,然后用 @Column 注释这两对方法,让 JPA 在持久化时使用它们在数据库中。

private LocalDateTime dateTime;

@Transient // Do not persist in database
public LocalDateTime getDateTime() {
    return this.dateTime;
}

public void setDateTime(LocalDateTime dateTime) {
    this.dateTime = dateTime;
}

@Column(name = "FOO_DATE", nullable = true, length = 10)
private String getDate() {
    if (this.dateTime == null)
        return null;
    return this.dateTime.toLocalDate().toString();
}

private void setDate(String date) {
    if (date == null) {
        this.dateTime = null;
    } else {
        LocalDate date = LocalDate.parse(date);
        LocalTime time = (this.dateTime == null ? LocalTime.MIDNIGHT
                          : this.dateTime.toLocalTime());
        this.dateTime = LocalDateTime.of(date, time);
    }
}

@Column(name = "FOO_TIME", nullable = true, length = 12)
private String getTime() {
    if (this.dateTime == null)
        return null;
    return this.dateTime.toLocalTime()
               .format(DateTimeFormatter.ofPattern("HH:mm:ss.SSS"));
}

private void setTime(String time) {
    if (time == null) {
        this.dateTime = null;
    } else {
        LocalTime time = LocalTime.parse(time);
        LocalDate date = (this.dateTime == null ? LocalDate.ofEpochDay(0)
                          : this.dateTime.toLocalDate());
        this.dateTime = LocalDateTime.of(date, time);
    }
}

【讨论】:

  • 我不认为在 getter 上使用 @Transient 可以按照这个答案在数据库中存储 stackoverflow.com/questions/21477034/… @Transient on getters has the same meaning as @Transient on fields
  • @Eklavya 链接中的答案明确表示@Transient 确实适用于抑制/禁用持久性的方法。
【解决方案2】:

您必须决定是希望您的数据库设计由您的实体设计驱动,还是希望将两者分离。

如果您不需要实体中的列,那么就不要映射它。如果您使用这种方法的“问题”是,hbm2ddl 不会生成您必须决定实体模型是否真的应该成为数据库设计驱动程序的列。您可以(并且 IMO 应该)使用像 Liquibase 或 Flyway 这样的工具来进行数据库模式迁移。这样,您可以在数据库中拥有列,同时不在实体中映射它。

我知道这对于你的小用例来说可能太多了,但我建议你最终这样做,因为你会在某些时候需要它。无论如何,您也可以将其映射为私有字段而不暴露该字段。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-10-28
    • 2018-10-11
    • 2019-03-27
    • 1970-01-01
    • 2023-03-06
    • 1970-01-01
    • 2016-10-21
    相关资源
    最近更新 更多