1

I am trying to implement a JPA-based solution using Domain Driven Development and I have encoutered a use case that I did not find any solution to. I am using JPA with Hibernate and Spring Boot.

Here is a simplified example. Lets say you have a value object:

@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;
    }

Where CurrencyId is another @Embeddable with just an Long id field.

Then I have an entity with two fields:

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

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

Note that I would like the two fields to share the same DB Column CURRENCY_ID in the table. Obviously, if try to use this code, I would get this exception:

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)

I have tried searching online and did not find any solution.

Possible solutions: Just create two distinct currency1Id and currency2Id columns in the table and map each value object to it - works, but defeats the purpose of a shared currency on both fields. I can handle the shared currency validation on entity-level, but I think this is just an unnecessary design to add extra db column if I don't want it there. Also, if I have something like PRIMARY KEY or UNIQUE constraints on the table with the column involved, things get out of control.

Perhaps some magic with entity lifecycle hooks I did not think about?

Thank you for your comments, I appreciate your time.

  • The repeated mapping of a column is only a problem for JPA because it does not know which of the two values to actually save in the database should they conflict. Try using `@AttributeOverride(name ="currencyId.id", column =@Column(name="CURRENCY_ID", insertable=false, updatable=false))` on either one of the embedded fields – crizzis Jan 17 '19 at 23:27
  • Hi, I tried that and still the same error message. I tried that on both fields. I also tried using the latest spring boot, did not help. Thanks for the suggestion though. – Josef Bodnar Jan 18 '19 at 10:05

1 Answers1

0

FYI, I did find one way to implement this, by changing the second field to the bare amount - so internally there is only one field for currencyId. Next I made a getter for amount2 that will re-construct the full instance using the currencyId from the first amount. Something like that:

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

@Column
private BigDecimal amount2;

public AmountWithCurrency getAmount2(){
    return new AmountWithCurrency(amount1.getCurrencyId(), amount2);
}

This is still not perfect solution since this still can cause issues in queries, etc. But seems to be working for me at the moment. Hope it helps someone.