Customising Reads using entity context

If you've customized an entity Insert to store additional information, you'll typically want to also customize the Read operation to retrieve the additional attributes. This is very much like the Insert operation in reverse. You'll do the following:

Note that in the sample code that follows, the facade and listener classes can be the same classes as from our Insert example. We're just looking at different methods. By the same token, if you just have a single Listener class to handle both Insert and Read then you only have to do the Listener registration once. Here's how it all looks in practice. As before, we're assuming that you've already declared a new "classic" entity called MyAdditionalEntity and have extended the façade parameters to take the new details.

Here's the original façade:

Figure 1. A façade which reads MyEntity
...
public class MyFacade {
  @Inject
  protected MyEntityDAO myEntityDAO;

  public MyEntityDetails readMyEntity(final MyEntityKey key)
      throws AppException, InformationalException {

    MyEntityDetails details = new MyEntityDetails();
    MyEntity myEntity = myEntityDAO.get(key.id);
    getDetails(myEntity, details.dtls);
    return details;
  }

  protected void getDetails(final MyEntity myEntity,
      final MyEntityDtls dtls)
      throws AppException, InformationalException {
    dtls.firstname = myEntity.getFirstname();
    dtls.surname = myEntity.getSurname();
  }

}

Here's our override of the façade:

Figure 2. A façade subclass which uses entity context
...
public class MyCustomFacade extends
  curam.custom.facade.impl.MyFacade {

  @Override
  public MyEntityDetails readMyEntity(final MyEntityKey key)
      throws AppException, InformationalException {
    MyEntityDetails details = new MyEntityDetails();
    MyEntity myEntity = myEntityDAO.get(key.id);
    getDetails(myEntity, details.dtls);

    /*
     * Retrieve additional details from entity context
     */
    details.additionalDtls = myEntity.getContextContainer().get(
        MyAdditionalEntityDtls.class);

    return details;
  }

}

Here's our listener for reads on the original entity. Note, we're assuming that there will always be a corresponding record on the new entity. Your design may have to cater for the situation where this is not the case.

Figure 3. A listener for reads on MyEntity
@Singleton
class MyEntityListener extends PersistenceEvent<MyEntity> {

  /**
   * After MyEntity is read, also read MyAdditionalEntity.
   */
  @Override
  public void postRead(final MyEntity e) throws AppException,
      InformationalException {
    /*
     * Read additional details from database
     */
    MyAdditionalEntity additionalEntity = MyAdditionalEntityFactory
        .newInstance();
    MyAdditionalEntityKey key = new MyAdditionalEntityKey();
    key.id = e.getID();
    MyAdditionalEntityDtls dtls = additionalEntity.read(key);
    /*
     * Store additional details in entity context
     */
    e.getContextContainer().put(MyAdditionalEntityDtls.class, dtls);
  }
}

Here's how we register our listener. Note that if you combined the listener from the Insert example and the Read example into a single listener class, you won't need this step. You only register each listener class once:

Figure 4. A Guice module to register the listener in the previous listing
public class MyModule extends AbstractModule {

  @Override
  protected void configure() {

    /*
     * Get the listener set
     */
    Multibinder<PersistenceEvent<MyEntity>> myEventListeners =
      Multibinder.newSetBinder(binder(),
        new TypeLiteral<PersistenceEvent<MyEntity>>() {/**/});

    /*
     * Add a listener
     */
    myEventListeners.addBinding().to(MyEntityListener.class);
  }
}

In summary, we've created a listener which receives read events for one entity and performs reads on another, supplemental entity. The data from the supplemental entity is piggybacked on "entity context", and is available to a façade method which returns the details to a client.