简体   繁体   中英

The correct way to update the fields of an @Entity that depend on other entities' state

We ran across a problem where we have to maintain a date field - and preferably persist its value - where the date field is dependent on other entities' state. What is the correct way to address this problem with JPA2 + Hibernate + Spring?

Basically, we have three entities, let's call them Parent , Child , and Conf . The dependentDate is calculated by complex business rules that depend on other entities' state. Specifically, it should be Parent#date minus Conf#delay skipping all days that are weekends or holidays (persistent data).

The problem is that whenever the Parent#date changes, all the children's dependentDate s should be recalculated. This should happen also whenever the Child object's conf is changed. Currently, there is a service method for calculating the new date based on the Child object's relations (let's call it calculateDate() ).

We are faced with the problem of where the field's value should be calculated. I came up with three possible solutions. Which of these - or possibly something else - is the preferred way of updating entities' fields that depend on other entities' state?

  1. A JPA EntityListener
    • this is discouraged in the JPA2 spec, since the EntityListeners should not access other entities
    • the field would always be correct
  2. An Spring AOP aspect that updates
    • the logic is hidden in some other class and is not easily seen
    • a risk that the pointcut does not catch all persist/update events
  3. Every developer for himself
    • always, when updating an instance of either Parent or Child class, the developer must remember to call the service's calculateDate() method
    • should this be in dao or service layer?
    • risk of the developer forgetting to add the call, resulting in erroneous state

The following example shows the entity relations with omitted annotation configuration.

The entities

public class Parent {
     LocalDate date;
     Set<Child> children;     // one-to-many
}

public class Conf {
     int delay;
}

public class Child {
     LocalDate dependentDate;
     Conf conf;               // many-to-one
     Parent parent;           // many-to-one
}

Complex business rules that depend on several entities' state belong to the service layer. There shouldn't be so many places in the application where the parent's date and the child's conf are changed, so you shouldn't have so many places where the new date calculation must be done.

If you really have so many places where this is done, you could make it clear that the date must be recomputed by passing a ChildDateUpdater instance (ChildDateUpdater being an interface) to the setter of the parent date and the setter of the child conf. This would make it obvious that something must be done every time these two fields are changed:

In Parent:

public void setDate(Date date, ChildDateUpdater childDateUpdater) {
    this.date = date;
    for (Child child : children) {
        childDateUpdater.updateChildDate(child);
    }
}

In Child:

public void setConf(Conf conf, ChildDateUpdater childDateUpdater) {
    this.conf = conf;
    childDateUpdater.updateChildDate(this);
}

Encapsulate your data access in a DAO. When a Parent is going to be saved, check if there has been any changes to its values and, if so, update the children accordingly.

The trigger option of realsim is also feasible (basically it is the same idea implemented at a different level) but then you would lose portability (and also there will be the issue with instances of Children already loaded not being updated).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM