Info |
---|
The use of the term "persistence" here refers to page-level persistence, NOT database persistence. |
Wiki Markup |
---|
{float:right|background=#eee}
{contentbylabel:title=Related Articles|showLabels=false|showSpace=false|space=@self|labels=persistence}
{float} |
Persistent Page Data
Most instance variables in Tapestry are automatically cleared at the end of each request. This is important, as it pertains to how Tapestry pages are shared, over time, by many users.
Div | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||
|
However, you often want to store some persistent data on a single page, and have access to it in later requests . Long term storage of data should go in a database of some form, but server-side state for the duration of the as user's interaction with the application should go in the HttpSession (though Tapestry provides a few other options as well).
Note: To store values that may be accessed across multiple pages, uses a session state object.
Making a field persistent is accomplished with the Persist annotation. Again, this does not refer to database persistence, it refers to session persistence.
to that same page, without having to store it in a database between requests. (To store values across multiple pages, see Session Storage.)
Making page data persist across requests to a single page is accomplished with the @Persist annotation. This annotation is applied to private instance fields of components.:
Code Block | ||
---|---|---|
| ||
@Persist
private int value;
|
Annotated Such annotated fields will store retain their state between requests. Generally, speaking, this means that the value is stored into the session (but other approaches are possible).
Whenever you make a change to a persistent field, its value is storedsaved. On later requests to the same page, the value for such persistent fields is reloaded from storagethe field is restored.
Persistence Strategies
The value for each field is the strategy used to store the field between requests.
Session Strategy
Wiki Markup |
---|
{float:right|background=#eee|padding=0 1em}
*JumpStart Demo:*
[Storing Data in a Page|https://tapestry-jumpstart.org/jumpstart/examples/state/storingdatainapage]
[Passing Data Between Pages|https://tapestry-jumpstart.org/jumpstart/examples/state/passingdatabetweenpages]
{float} |
The session strategy stores field changes into the session; the session is created as necessary. Session strategy is the default strategy used unless otherwise overridden.
A suitably long session attribute name is used; it incorporates the name of the page, the nested component id, and the name of the field.
Code Block | ||
---|---|---|
|
...
| ||
@Persist
private int value;
|
Flash Strategy
The flash strategy stores information in the session as well, just for not very long. Values are stored into the session, but then deleted from the session as they are first used to restore a page's state.
The flash is typically used to store temporary messages that should only be displayed to the user once.
Code Block | ||||
---|---|---|---|---|
| ||||
@Persist(PersistenceConstants.FLASH) private int value; |
Client Strategy
The field is persisted onto the client; you will see an additional query parameter in each URL (or an extra hidden field in each form).
...
Use client persistence with care, and store a minimal amount of data. Try to store the identity (that is, primary key) of an object, rather than the object itself.
Code Block | ||||
---|---|---|---|---|
| ||||
@Persist(PersistenceConstants.CLIENT)
private int value;
|
Hibernate Entity Strategy
Entity persistence is provided by the tapestry-hibernate module (which extends Tapestry with new features).
In Entity persistence, the field should store a Hibernate entity instance.
Code Block | ||||
---|---|---|---|---|
| ||||
@Persist(HibernatePersistenceConstants.ENTITY)
private User user; |
The value stored in the HttpSession is a token for the entity: its Java class name and primary key. When the field is restored in a later request, the entity is re-instantiated using that data.
What is not stored is any changes to the persistent entity that are not committed to the external datastore (the database).
Starting in Tapestry 5.4, it is possible to store a non-persistent entity (a transient entity). A transient entity is stored directly into the HttpSession, and should be Serializable if the application is clustered.
JPA Entity Strategy
The tapestry-jpa module uses a similar strategy. However, at the current time it can only store a persisted entity (one that has been saved to the database and has a primary key).
Code Block | ||||
---|---|---|---|---|
| ||||
@Persist(JpaPersistenceConstants.ENTITY) private Account account; |
Persistence Strategy Inheritance
By default the value for the Persist annotation is the empty string. When this is true, then the actual strategy to be used is determined by a search up the component hierarchy.
For each component, the meta-data property tapestry.persistence-strategy
is checked. This can be specified using the @Meta annotation.
If the value is non-blank, then that strategy is used. This allows a component to control the persistence strategy used inside any sub-components (that don't explicitly use a different strategy).
...
The method discardPersistentFieldChanges()
of ComponentResources will discard all persistent fields for the page, regardless of which strategy is used to store the property. This will not affect the page in memory, but takes effect for subsequent requests.
Include Page | ||
---|---|---|
|
The Servlet API was designed with the intention that there would be only a modest amount of server-side state, and that the stored values would be individual numbers and strings, and thus, immutable.
Many web frameworks do not use the HttpSession this way, and store large and mutable objects in the session.
This is not an issue for single servers, but in a cluster, anything stored in the session must be serialized to a bytestream and distributed to other servers within the cluster, and restored there.
Most application servers perform the serialization and distribution as part of HttpSession.setAttribute().
This creates a problem for mutable objects, because if you read a mutable session object, change its state, but don't invoke setAttribute(), the changes will be isolated to just a single server in the cluster.
Tapestry attempts to solve this: any session persisted object that is read during a request will be re-stored back into the HttpSession at the end of the request. This ensures that changed internal state of those mutable objects is properly replicated around the cluster.
This can be a problem in a cluster as all those calls to setAttribute() may impact performance, as often the internal state of the mutable object don't have changed.
Tapestry has solutions to this.
Immutable Objects
Tapestry knows that Java's String, Number and Boolean classes are immutable. Immutable objects do not require a re-store into the session.
You can mark your own session objects as immutable using the ImmutableSessionPersistedObject annotation.
OptimizedSessionPersistedObject
The OptimizedSessionPersistedObject interface allows an object to control this behavior. An object with this interface can track when its mutable state changes. Typically, you should extend from the BaseOptimizedSessionPersistedObject base class.
SessionPersistedObjectAnalyzer
...
|
Code Block | ||||
---|---|---|---|---|
| ||||
@Persist(HibernatePersistenceConstants.ENTITY)
private User user; |
Code Block | ||||
---|---|---|---|---|
| ||||
@Persist(JpaPersistenceConstants.ENTITY)
private Account account; |