Take the Example HR module for instance. An Employee has a home address and a mailing address. The following code is the O/R mappings between Employee and Address entities:
@Entity(name="Employee")
@Table(name="Employee")
public class Employee extends PersistenceEntityBase implements NormativeId {
private Address homeAddress;
private Address mailingAddress;
@OneToOne
@JoinColumn(name="homeAddressId")
public Address getHomeAddress() {
return homeAddress;
}
public void setHomeAddress(Address homeAddress) {
this.homeAddress = homeAddress;
}
@OneToOne
@JoinColumn(name="mailingAddressId")
public Address getMailingAddress() {
return mailingAddress;
}
public void setMailingAddress(Address mailingAddress) {
this.mailingAddress = mailingAddress;
}
}
@Entity(name="Address")
@Table(name="Address")
public class Address extends PersistenceEntityBase implements EntityChoiceSupport, ActiveAware {
private Employee employee;
private String choice;
@Override
public void copyFromSameAsEntity(EntityChoiceSupport entity) {
...
}
@Transient
@Override
public String getChoice() {
return choice;
}
public void setChoice(String choice) {
this.choice = choice;
}
@ManyToOne
@JoinColumn(name="employeeId")
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee = employee;
}
}
Address implements EntityChoiceSupport. In addition to being created
in place inside Employee bean, home and mailing addresses can be selected
from the employee's existing addresses. When creating an employee, no
addresses are available for selection for the employee. But mailing address
can be set as the same as home address.
public class EmployeeBean extends EntityFormSupportBean<Employee> {
}
// list home address before mailing address since mailing address
// can be the same as home address
@Property(name=Employee.PROPERTY_HOME_ADDRESS, view={ViewType.ENTITY},
entityPropertyType=EntityFormBeanProperty.class),
@Property(name=Employee.PROPERTY_MAILING_ADDRESS, view={ViewType.ENTITY},
entityPropertyType=EntityFormBeanProperty.class)
@Override
protected List<String> canFormEntityBeSameAs(Employee entity, String entityFormProperty) {
if (entityFormProperty.equals(Employee.PROPERTY_MAILING_ADDRESS))
return Employee.PROPERTY_HOME_ADDRESS;
return null;
}
@Override
protected List<CriteriaElement> getChoicePropertyQueryElements(Employee entity,
EntityFormBeanProperty<Employee> property) throws SystemException {
String propertyName = property.getName();
if (propertyName.equals(Employee.PROPERTY_HOME_ADDRESS) ||
propertyName.equals(Employee.PROPERTY_MAILING_ADDRESS)) {
if (isCreating()) // employee not created, no choice
return null;
// employee addresses for selection
List<CriteriaElement> propertyQueryElements = new ArrayList<CriteriaElement>();
propertyQueryElements.add(DetachedCriteria.eq(Address.PROPERTY_EMPLOYEE, entity));
propertyQueryElements.add(DetachedCriteria.desc(Address.PROPERTY_ID));
return propertyQueryElements;
}
return super.getChoicePropertyQueryElements(entity, property);
}
The list of CriteriaElement(s) returned will be used to build
query criteria. Return null if entity selection is not available.
Based on the O/R mapping between Employee and Address, an Employee's home and mailing addresses are nullable, but an Address' employee is not nullable. During prePersist or preMerge, prepare the address for persistence by setting employee and active properties. Return false to tell the framework to persist or merge the Address entity before persisting or merging the Employee entity. The framework will take care of the circular reference between the Employee and Address entity. During postPersist or postMerge, doing nothing. It does not matter to return true or false since the framework will not persist or merge the Address again if it has already persisted or merged in persistence.
@Override
protected <M extends PersistenceEntity> boolean prepareFormEntityBeforeCommit(
Employee entity, String entityFormProperty, M formEntity, boolean prePersistOrMerge,
PersistenceEntityManager peManager, QueryDescriptor qd) throws SystemException {
boolean createdOrMerged = super.prepareFormEntityBeforeCommit(entity, entityFormProperty, formEntity,
prePersistOrMerge, peManager, qd);
if (!(entityFormProperty.equals(Employee.PROPERTY_HOME_ADDRESS) ||
entityFormProperty.equals(Employee.PROPERTY_MAILING_ADDRESS)))
return createdOrMerged;
if (prePersistOrMerge) { // avoid setting twice
Address addr = (Address) formEntity;
if (addr.getEmployee() == null)
addr.setEmployee(entity);
if (addr.getActive() == null)
addr.setActive(true);
return false;
}
return createdOrMerged;
}
What if an Employee's home and mailing addresses are not nullable,
but an Address' employee is nullable. In this case, both Address entities must
be created before creating the Employee entity. If the Employee is not persisted,
any Address' reference to the employee must be removed during prePersist phase,
and restored during postPersist phase. An entity can not reference to any transient
entities when the entity is persisted or merged in persistence.
@Override
protected <M extends PersistenceEntity> boolean prepareFormEntityBeforeCommit(
Employee entity, String entityFormProperty, M formEntity, boolean prePersistOrMerge,
PersistenceEntityManager peManager, QueryDescriptor qd) throws SystemException {
boolean createdOrMerged = super.prepareFormEntityBeforeCommit(entity, entityFormProperty, formEntity,
preCreateOrApplyChage, peManager, qd);
if (!(entityFormProperty.equals(Employee.PROPERTY_HOME_ADDRESS) ||
entityFormProperty.equals(Employee.PROPERTY_MAILING_ADDRESS)))
return createdOrMerged;
if (prePersistOrMerge) {
// break the reference to employee if it is transient(not persisted)
Address addr = (Address) formEntity;
Employee employee = addr.getEmployee();
if (employee != null && !employee.isPersisted())
addr.setEmployee(null);
if (addr.getActive() == null)
addr.setActive(true);
return false; // tell framework to create the address
}
else {
// the employee and address have been created/merged
Address addr = (Address) formEntity;
if (addr.getEmployee() == null) {
addr.setEmployee(entity);
addr.setChanged(true); // set change flag
return false; // tell framework to merge the address
}
}
return createdOrMerged;
}