views:

1361

answers:

2

Hi,

Is there a way to map a calculated property using JPA?

Assuming I have an Invoice object with one or more InvoiceLineItems within it, I want to have a persistent calculated property on the Invoice class that gives me the total amount:

class Invoice {
    ...

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

Now, I could create a protected no-op setTotalAmount method to make JPA happy, but I was wondering if there is a way to let JPA know that the mapping is one way only and to avoid creating a superfluous setter method.

Thanks, Aleks

+2  A: 

What you've described is not a calculated property in JPA sense. You're calculating it yourself within your method - just mark that method as @Transient and JPA will ignore it.

If you truly need a calculated property (where "calculated" means "calculated via SQL expression"), you'll need to annotate it according to your JPA provider. For Hibernate you'd do that via @Formula annotation:

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

Other providers may have their own ways to configure this; there's no JPA standard.

ChssPly76
Marking property as @Transient will not help me. I don't want JPA to ignore it -- I want the total amount persisted in a database so I can query on it directly without having to aggregate information from the child table.So what I'm asking is how to persist a calculated property (in Java sense, completely unrelated to persistence) without having to implement unnecessary setter for it.
Aleksandar Seovic
Your example is rather strange then. If you're **storing** this value and querying on it, why are you recalculating it in your getter? The bottom line, however, is that if you're annotating getter method (as opposed to property) you must have a corresponding setter (it may be private) in order for JPA to populate that value on your entity.
ChssPly76
The example I gave is gross oversimplification of the domain model I'm working with, which have several levels of one to many relationships, with calculated property at each level that depends on children.Calculating value in Java as necessary is significantly easier than trying to update the total as child objects in a (deep) hierarchy are added, removed or modified, which is why I am recalculating it.
Aleksandar Seovic
However, having the total persisted makes queries based on the amount against the table that stores root objects significantly simpler, which is I want to persist it when I save my objects, but don't really want to load it ever again (which is why I don't need and couldn't care less about the setter).This is why I was hoping there is a way to tell JPA to treat property as write-only from a persistence perspective.In any case, thanks for your answer, I guess I'll have to leave with setters that serve no other purpose other than to make JPA happy :-(
Aleksandar Seovic
A: 

Perhaps the PrePersist annotation can be used for this.

@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;
}
Jörn Horstmann
This is definitely nice to know, thanks.However, I need the total to be updated whenever I look at it, not only before it gets persisted, so it won't work in my particular case.
Aleksandar Seovic