JPA-具有共享数据库列的两个复合可嵌入字段?

时间:2019-01-17 15:52:39

标签: java hibernate jpa embeddable

我正在尝试使用域驱动开发来实现基于JPA的解决方案,并且已经找到了一个我找不到任何解决方案的用例。我正在将JPA与Hibernate和Spring Boot结合使用。

这是一个简化的示例。假设您有一个值对象:

@Embeddable
public class AmountWithCurrency {
    private BigDecimal amount;

    @Embedded
    @AttributeOverride(name="id", column=@Column(name = "CURRENCY_ID"))
    private CurrencyId currencyId;

    protected AmountWithCurrency() {
        //for JPA
    }

    public AmountWithCurrency(BigDecimal amount, CurrencyId currencyId) {
        this.amount = amount;
        this.currencyId = currencyId;
    }

其中CurrencyId是另一个@Embeddable,仅包含长ID字段。

然后我有一个包含两个字段的实体:

@Embedded
@AttributeOverrides({
        @AttributeOverride(name="amount", column=@Column(name = "AMOUNT1")),
})
private AmountWithCurrency amount1;

@Embedded
@AttributeOverrides({
        @AttributeOverride(name="amount", column=@Column(name = "AMOUNT2")),
})
private AmountWithCurrency amount2;

请注意,我希望这两个字段在表中共享相同的DB列CURRENCY_ID。显然,如果尝试使用此代码,则会出现以下异常:

Caused by: javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.MappingException: Repeated column in mapping for entity: com.MyEntity column: currency_id (should be mapped with insert="false" update="false")
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:402)
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:377)
    at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:341)

我尝试过在线搜索,但没有找到任何解决方案。

可能的解决方案: 只需在表中创建两个不同的currency1Id和currency2Id列,然后将每个值对象映射到该列即可-可以,但是在两个字段上都无法使用共享货币。我可以在实体级别上处理共享货币验证,但是我认为如果我不希望在此处添加额外的db列,这只是一个不必要的设计。另外,如果我在涉及列的表上有诸如PRIMARY KEY或UNIQUE约束之类的东西,那一切就会失去控制。

也许我没有想到过与实体生命周期挂钩相关的魔术?

感谢您的评论,感谢您的宝贵时间。

1 个答案:

答案 0 :(得分:0)

仅供参考,我确实找到了一种方法来实现此目的,方法是将第二个字段更改为裸机金额-因此在内部,currencyId只有一个字段。 接下来,我为amount2作了一个吸气剂,它将使用第一个金额中的currencyId来重建整个实例。 像这样:

"legend": {
    "valueWidth": 100,
    "valueAlign": "left",
    "valueText": "[[close]]",
    "periodValueText": "[[value.close]]",
},

这仍然不是一个完美的解决方案,因为它仍然可能导致查询等问题。但是目前看来,这对我来说是有用的。希望对别人有帮助。