spring - 使用 Hibernate/JPA 持久化复杂的 @Embeddable

标签 spring hibernate jpa spring-data-jpa

我正在使用 Spring Boot 和 Hibernate 开发 REST 服务,并使用 JPA 进行持久化。 UML 人员定义了一组类,看起来很难实现,而且我还不知道如何实现。

这些类用于存储类型化数据。一组类派生自名为 XBRLUnitType 的类,并包括一个名为currencyUnitType 的类,该类保存货币代码。另一组类保存一个数量,它是一个“值”加上数据类型。这些数量之一是 Monetary,它有一个 Float 来保存该值,并且有 MoneyUnitType,如下所示:

@Embeddable
public class Monetary extends Quantity {

    @JsonProperty("itemUnit")
    @Embedded
    private monetaryItemType itemUnit = null;

}

@Embeddable
public class Quantity {

    @JsonProperty("uncertainty")
    private Float uncertainty = null;

    @JsonProperty("value")
    private Float value = null;

    @JsonProperty("itemUnit")
    @Embedded
    private XBRLItemType itemUnit = null;
}

@Embeddable
public class monetaryItemType extends XBRLItemType {
}

@Embeddable
public class XBRLItemType {

    @JsonProperty("symbol")
    protected String symbol = null;

    @JsonProperty("unitID")
    private String unitID = null;

    @JsonProperty("unitName")
    private String unitName = null;
}

然后在其中一个类中我声明了这个字段:

@Entity
public class Device {

    ... many other fields 

    @Embedded
    private Monetary currency = null;

    ... many other fields 

}

为了创建一个项目,我发布了一些 JSON,我发现 JSON 已被正确解释,并且生成的 Java 对象中的货币字段已正确填写。

但是...在 Hibernate 调试输出中 Hibernate 不会在数据库中创建与货币列匹配的列。

create table device (dtype varchar(31) not null, deviceid binary not null, description varchar(255), meta varchar(255), name varchar(255), documentation varchar(255), type integer, cost float, manual varchar(255), manufacture_date_time binary(255), manufacturer varchar(255), model_number varchar(255), purchase_date_time binary(255), serial_number varchar(255), warranty_promise varchar(255), has_built_in_meter boolean, inverter_type integer, phase_type integer, ground_coverage_ratio float, hasdcoptimizer boolean, has_micro_inverter boolean, module_type integer, firmwareid integer, nameplateid binary, pv_arrayid binary, sub_arrayid binary, pv_stringid binary, systemid binary, primary key (deviceid))

此外,数据库表没有匹配的列。

当我使用 GET 请求检索对象时,货币字段为空。大概这是因为它没有数据库列。

在这本在线电子书中我发现了Java Persistence不支持复杂@Embedded对象的声明:https://en.wikibooks.org/wiki/Java_Persistence/Embeddables

那就是“JPA 规范不允许在可嵌入对象中继承”......显然我有继承。并且,“JPA 1.0 规范仅允许可嵌入对象中的基本关系,因此不支持嵌套嵌入”,并且我相信我拥有的是嵌套嵌入。

我研究的一件事是转换器:

@Embedded
@Column(name="currency", columnDefinition="VARCHAR")
@Convert(converter = MonetaryConverter.class, attributeName="currency")
private Monetary currency = null;

但是,MonetaryConverter 类上的方法从未被调用。

还有这个:

@Type(type="com.amzur.orangebuttonapi.model.primitives.Monetary")
@Columns(columns = { 
        @Column(name="value"), @Column(name="uncertainty"), @Column(name="currencyCode")
})
@ColumnTransformers(value = {
        @ColumnTransformer(
                forColumn="value",
                read="value",
                write="?"
        ),
        @ColumnTransformer(
                forColumn="uncertainty",
                read="uncertainty",
                write="?"
        ),
        @ColumnTransformer(
                forColumn="currencyCode",
                read="itemUnit.symbol",
                write="?"
        )
})
private Monetary currency = null;

它应该创建一些列来存储一些可以解决问题的值。

基本上我正在寻找方法来持久化一个必须很复杂的Embeddable。

否则,我需要将这些类转换为@Entity,但需要更多的表?

更新:上面做了一些澄清。

在 javax.persistence 注释 ( https://javaee.github.io/javaee-spec/javadocs/ ) 中,我看到 @Convert 适用于基本属性。因此 @Convert 不适用于我正在处理的非 Basic 属性。

最佳答案

当将数据存储为静态数据时,您希望将 Monetary 类的实例转换为浮点型,以便将 value 存储在 device 表的 cost 列中数据库表中不存在属性。加载时,您希望将 cost 转换为 Monetary 实例。我稍后会在回答中谈到这一点。

在此之前,我对您有一些观察:

  1. 使用 JPA 2 规范,因为它比 JPA 1 规范有很多增强功能。使用最新版本的 Hibernate,例如 5.2.10。
  2. 您不需要使用 null 初始化任何实体的属性,因为您使用的是 Float 等包装类或 Monetary 等您自己的自定义类。默认情况下它们将为空。
  3. 您已声明 @Embeddable 类。因此,在使用该类存储属性时,您不再需要在实体内使用 @Embedded 注释。两者都没有伤害,但没有必要。它们是彼此的替代品。将组件类标记为 @Embeddable 或将所属实体类中的属性标记为 @Embedded
  4. 要将实体的属性名称映射到列名称,请使用 @Column 注释。

  5. @ColumnTransformer 应用于对任何列中存储的值进行某种转换。例如,将摄氏度转换为华氏度,反之亦然。它们应该用于嵌入式实体类的单个细粒度属性(数据库中具有映射列),而不是嵌入式类。 Hibernate 将在运行时使用它们将应用程序层中显示的列值转换为使用 readwrite 中编写的表达式的值。

终于来到转换器了!!!

转换器用于将应用层实体转换为数据库层实体。与 ORM 几乎相同,嗯。不。您的应用程序层有一个货币实体,它具有 value、unitId、symbol 和其他此类属性。但是您的数据库表只有成本列,这是使用 AttributeConverter 但没有 @Embeddable 的经典案例。

现在您应该删除所有 @Embeddable@Embedded 注释 Monetary、Quantity、XBRLItemType 和currencyItemType(类名在 Java 中应始终以大写字母开头,提示!!!)类,因为它们没有映射到数据库列的所有属性。它们应该被视为实现 Serialized 接口(interface)的简单 POJO 类。然后,您应该编写 MonetaryConverter.classMonetary 转换为成本,反之亦然。

稍后,当您的 DBA 升级数据库表以在 device 表中包含符号、不确定性、unitId 等列时,您可以从以下位置删除 MonetaryConverter.class您的项目并创建Monetary、Quantity、XBRLItemType 和moneyItemType 类@Embeddable。它将自动映射到您的数据库列。如果属性名称和数据库列名称不同,您只需在属性上添加 @Column 并映射它们即可。简单!!!

假设您将通过实现 javax.persistence.AttributeConverter 接口(interface)并覆盖这两个方法来正确编写 MonetaryConverter.class convertToEntityAttribute()convertToDatabaseColumn()。您的代码应如下所示:

@Convert(converter = MonetaryConverter.class, disableConversion=false)
@Column(name="cost")
private Monetary currency;

这会将您的货币属性(property)转换为成本,反之亦然。

关于spring - 使用 Hibernate/JPA 持久化复杂的 @Embeddable,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46508926/

相关文章:

java - 使用 Spring MVC 的简单 REST 服务,配置问题

java - 序列化为 json 响应时避免 hibernate lazy init 异常的更好方法

java - Hibernate 的 Transformers.aliasToBean() 方法

java - 具有 DESC 顺序的 Hibernate 索引

JPA:级联删除不会删除子项

java - 测试 Spring boot Camel 应用程序时出现 NullPointerException

java - Spring boot - 在请求参数 'null' 或 header '_csrf' 上发现无效的 CSRF token 'X-CSRF-TOKEN'

java - JPA/EclipseLink 惰性集合未加载

java - Spring Activity 与方面

java - 我使用 Hibernate 创建了一个基本的 CRUD GUI,但我得到 "index out of bound exception"。可能是什么问题呢?