user3310115
user3310115

Reputation: 1460

Calculated Property wrt JPA. Does this work in my case?

I have a scenario where I have 2 labels that need to be configured. The names of the labels are 'Out Date' and 'In Date'. I only have one field in the database called 'Date'. Whether it is 'Out' or 'In' is decided at the runtime by the value of an Enum 'Scenario'. However, I need to actually show the user Out Date&In Date so that he can select 1 or both of them. I heard that calculated field concept it JPA will assist in this. Is this true or is there some other way that I can achieve this. Below is some sample code.

Date

@Override
@Convert("DateTimeConverter")
@Column(name = "DATE")
public DateTime getDate() {
    return date;
}

Scenario

@Override
@Convert("EnumConverter")
@Column(name = "SCENARIO")
public Scenario getScenario() {
    return scenario;
}

Scenario is any enum with the values OUT(1),IN(2)

Upvotes: 1

Views: 618

Answers (2)

Rémi Bantos
Rémi Bantos

Reputation: 1967

To compute properties within JPA entities, you can use JPA callbacks.

See this Hibernate JPA Callbacks documentation. (Note: JPA callbacks are not specific to hibernate, it's part of latest JPA 2.1 specification). And also this OpenJpa JPA Calbacks one.

Following entity life-cycle categories have a Pre and Post event which can be intercepted by the entity manager to invoke methods:

So let's say you want to compute a complexLabel label from two persisted entity fields label1 and label2 in an entity titled MyEntity:

@Entity
public class MyEntity {

    private String label1;

    private String label2;

    @Transient
    private String complexLabel;

    @PostLoad
    @PostUpdate // See EDIT
    // ...
    public void computeComplexLabel(){
        complexLabel = label1 + "::" + label2;
    }
}

As @Dawid wrote, you have to annotate complexLabel with @Transient in order to make them ignored by persistence. If you don't do this, persistence fails because there is no such column in MyEntity corresponding table.

With @PostLoad annotation, computeComplexLabel() method is called by entity manager just after the loading of any instance of MyEntity from persistence. Thus, @PostLoad annotated method is best suited to put your post loading entity properties enhancement code.

Bellow is an extract from JPA 2.1 specification about PostLoad:

The PostLoad method for an entity is invoked after the entity has been loaded into the current persistence context from the database or after the refresh operation has been applied to it. The PostLoad method is invoked before a query result is returned or accessed or before an association is traversed.

EDIT

As pointed out by @Dawid, you could also use @PostUpdate in case you want to compute this transient field just after the entity update, and use other callbacks when needed.

Upvotes: 1

Dawid Pytel
Dawid Pytel

Reputation: 2810

There are no calculated properties in JPA.

You can use @Transient annotation to create properties that are not persisted but calculated based on other fields:

@Transient
public DateTime getInDate() {
    if (scenario == Scenario.IN) {
        return date;
    }
    return null;
}

@Transient
public DateTime getOutDate() {
    if (scenario == Scenario.OUT) {
        return date;
    }
    return null;
}

Alternatively, if you are using Hibernate you can use proprietary annotation @Formula:

@Formula("case when SCENARIO = 2 then DATE else NULL end")
@Convert("DateTimeConverter")
private DateTime inDate;

@Formula("case when SCENARIO = 1 then DATE else NULL end")
@Convert("DateTimeConverter")
private DateTime outDate;

I prefer the first option because:

  • it is easier to test with unit tests
  • it is easier to use the entity in unit tests
  • it does not require proprietary extensions
  • generally there might be some problems with portability of SQL, although in this problem case when is SQL 92 compatible so it does not apply here

The only problem I can is is that in simplest approach is that we abandon encapsulation by exposing to clients internals of the entity (scenario and date properties). But you can always hide these properties with accessor protected, JPA will still handle that.

Upvotes: 2

Related Questions