Отображение расчетных свойств с помощью JPA

Есть ли способ сопоставить вычисленное свойство с помощью JPA?

Предполагая, что у меня есть объект Invoice с одним или несколькими InvoiceLineItems внутри него, я хочу иметь постоянное вычисляемое свойство в классе Invoice, которое дает мне общую сумму:

class Invoice {
    ...

    @Column(name = "TOTAL_AMOUNT")
    public BigDecimal getTotalAmount() {
        BigDecimal amount = BigDecimal.ZERO;
        for (InvoiceLineItem lineItem : lineItems) {
            amount = amount.add(lineItem.getTotalAmount());
        }
        return amount;
    }
}

Теперь я мог бы создать защищенный метод no-op setTotalAmount, чтобы сделать JPA счастливым, но мне было интересно, есть ли способ дать JPA знать, что сопоставление является только одним способом и чтобы избежать создания избыточного метода setter.

Спасибо, Aleks

Ответ 1

То, что вы описали, не является рассчитанным свойством в смысле JPA. Вы сами вычисляете его в своем методе - просто отметьте этот метод как @Transient, и JPA проигнорирует его.

Если вам действительно нужно вычисленное свойство (где "вычисленное" означает "вычисленное через выражение SQL" ), вам нужно будет аннотировать его в соответствии с вашим поставщиком JPA. Для Hibernate вы можете сделать это через аннотацию @Formula:

@Formula("col1 * col2")
public int getValue() {
 ...
}

У других поставщиков могут быть свои собственные способы настройки; нет стандарта JPA.

Ответ 2

Я знаю, что я не занимаюсь этой нитью, но, возможно, это может помочь кому-то.

Если вы хотите вычислить значение при чтении, аннотация @PostLoad может быть тем, что вы хотите:

@Transient
private BigDecimal totalAmount;

@PostLoad
public void onPostLoad() {
    BigDecimal amount = BigDecimal.ZERO;
    for (InvoiceLineItem lineItem : lineItems) {
        amount = amount.add(lineItem.getTotalAmount());
    }
    this.totalAmount = amount;
}

Ответ 3

Возможно, для этого можно использовать аннотацию PrePersist.

@Column(name = "TOTAL_AMOUNT")
private BigDecimal totalAmount;

@PrePersist
public void updateTotalAmount() {
    BigDecimal amount = BigDecimal.ZERO;
    for (InvoiceLineItem lineItem : lineItems) {
        amount = amount.add(lineItem.getTotalAmount());
    }
    this.totalAmount = amount;
}