Data view design can be quite tricky. This section documents a very subtle pattern, injected aggregation, that should be omitted.
This problem occurs when you create beans that link to other beans that could be data views. Doing so, you will lose data view dependencies, because the data views are loaded outside of your bean.
Example
Take a
Page
bean, created in a controller and inject another content bean of type Linkable
, called childBean.
The
Page
bean has a getter method
getTitle()
that accesses the
Linkable
bean. The return value of
the getter should be cached.
public class Page implements AssumesIdentity { private Linkable childBean; public void setLinkable(Linkable child) { this.childBean = child; } // not cached in dataview public Linkable getLinkable() { return this.childBean; } // cached in dataview!!! public String getTitle() { return this.childBean.getTitle(); } public boolean equals(Object o) {...} public int hashCode() { ... } public void assumeIdentity(Object bean) { this.childBean = ((Page) bean).getLinkable(); } }
When the
Page
bean is created, it might be that the
Linkable
bean itself is a data view. If not, everything is fine. If you call
Page#getTitle()
a property dependency for
Linkable
is created.
But, if the
Linkable
is a data view, no dependency is tracked:
The
Page
bean then acts like a data view that aggregates the
Linkable
. As a result, no property dependencies are generated if you call
Page#getTitle()
. Also, the
Linkable
is injected into the
Page
bean and therefore no data view dependency for the
Linkable
exists. As a result, the cached
Page
is not invalidated if the
Linkable
changes!
Solution
Do not access cached methods from a cached method or do not store the
Linkable
bean but the corresponding content object. Another method would be,
to unwrap the
Linkable
data view into a normal LinkableImpl
. You
can use
DataViewHelpers
methods
#isDataView()
and
#getOriginal()
for that.