ℹ️ Skipped - page is already crawled
| Filter | Status | Condition | Details |
|---|---|---|---|
| HTTP status | PASS | download_http_code = 200 | HTTP 200 |
| Age cutoff | PASS | download_stamp > now() - 6 MONTH | 1.4 months ago |
| History drop | PASS | isNull(history_drop_reason) | No drop reason |
| Spam/ban | PASS | fh_dont_index != 1 AND ml_spam_score = 0 | ml_spam_score=0 |
| Canonical | PASS | meta_canonical IS NULL OR = '' OR = src_unparsed | Not set |
| Property | Value |
|---|---|
| URL | https://tapestry.apache.org/persistent-page-data.html |
| Last Crawled | 2026-03-04 08:57:01 (1 month ago) |
| First Indexed | 2014-05-10 10:56:50 (11 years ago) |
| HTTP Status Code | 200 |
| Meta Title | Persistent Page Data - Apache Tapestry |
| Meta Description | Apache Tapestry is a open-source component-oriented framework for creating dynamic, robust, highly scalable web applications in Java. Tapestry complements and builds upon the standard Java Servlet API, and so it works in any servlet container or application server. |
| Meta Canonical | null |
| Boilerpipe Text | The use of the term "persistence" here refers to
page-level
persistence, NOT database persistence.
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.
However, you often want to store some data on a
single
page, and have access to it in later requests 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:
@Persist
private int value;
Such annotated fields will 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 saved. On later requests to the same page, the value for the field is restored.
Persistence Strategies
The value for each field is the
strategy
used to store the field between requests.
Session Strategy
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.
@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.
@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).
Client persistence is somewhat expensive. It can bloat the size of the rendered pages by adding hundreds of characters to each link. There is extra processing on each request to de-serialize the values encoded into the query parameter.
Client persistence does not scale very well; as more information is stored into the query parameter, its length can become problematic. In many cases, web browsers, firewalls or other servers may silently truncate the URL which will break the application.
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.
@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.
@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).
@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).
In any case, if no component provides the meta data, then the ultimate default, "session", is used.
Default Values
Fields marked with @Persist may not have default values (whether set inline, or inside a constructor).
Clearing Persistent Fields
If you reach a point where you know that all data for a page can be discarded, you can do exactly that.
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.
Clustering Issues
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.
However, many web applications do not use the HttpSession this way, instead storing large, mutable objects in the session. This is not a problem 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 that serialization and distribution whenever HttpSession.setAttribute() is called. This creates a data consistency 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.
But while this solution solves the data consistency problem, it does so at the expense of performance, since all of those calls to setAttribute() result in extra session data being replicated needlessly if the internal state of the mutable object hasn't changed.
Tapestry has solutions to this, too:
@ImmutableSessionPersistedObject Annotation
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 (and thus not requiring session replication) using the
ImmutableSessionPersistedObject
annotation.
OptimizedSessionPersistedObject Interface
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 Service
The
SessionPersistedObjectAnalyzer
service is ultimately responsible for determining whether a session persisted object is dirty or not (dirty meaning in need of a restore into the session). This is an extensible service where new strategies, for new classes, can be introduced.
@Persist(HibernatePersistenceConstants.ENTITY)
private User user;
@Persist(JpaPersistenceConstants.ENTITY)
private Account account; |
| Markdown | [ apache tapestry](https://tapestry.apache.org/index.html)
- [Getting Started](https://tapestry.apache.org/getting-started.html)
- [Documentation](https://tapestry.apache.org/documentation.html)
- [Download](https://tapestry.apache.org/download.html)
- [Community](https://tapestry.apache.org/persistent-page-data.html)
[Mailing Lists](https://tapestry.apache.org/community.html) [StackOverflow](https://stackoverflow.com/questions/tagged/tapestry) [Support](https://tapestry.apache.org/support.html) [Getting Involved](https://tapestry.apache.org/community.html) [Edit this page](https://cwiki.apache.org/confluence/pages/editpage.action?pageId=22872122 "Edit this page (requires approval, just ask on the mailing list)")
[@ApacheTapestry](https://twitter.com/ApacheTapestry) [\#tapestry5](https://twitter.com/hashtag/tapestry5)
- [Development](https://tapestry.apache.org/persistent-page-data.html)
[Source Code](https://gitbox.apache.org/repos/asf?p=tapestry-5.git) [Issues](https://issues.apache.org/jira/browse/TAP5) [Roadmap](https://tapestry.apache.org/development-roadmap.html "Development Roadmap")
- [Apache](https://tapestry.apache.org/persistent-page-data.html)
[About Apache](https://www.apache.org/) [Events](https://apachecon.com/?ref=royale.apache.org) [Sponsorship](https://www.apache.org/foundation/sponsorship.html) [License](https://www.apache.org/licenses/LICENSE-2.0) [Security](https://www.apache.org/security/) [Thanks\!](https://www.apache.org/foundation/thanks.html)
[Apache Tapestry](https://tapestry.apache.org/index.html) \> [Documentation](https://tapestry.apache.org/documentation.html) \> [User Guide](https://tapestry.apache.org/user-guide.html) \> [Persistent Page Data](https://tapestry.apache.org/persistent-page-data.html)
# Persistent Page Data
The use of the term "persistence" here refers to *page-level* persistence, NOT database persistence.
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.
### Related Articles
- Page:
[Persistent Page Data](https://tapestry.apache.org/persistent-page-data.html)
- Page:
[Session Storage](https://tapestry.apache.org/session-storage.html)
- Page:
[Performance and Clustering](https://tapestry.apache.org/performance-and-clustering.html)
However, you often want to store some data on a *single* page, and have access to it in later requests to that same page, without having to store it in a database between requests. (To store values across multiple pages, see [Session Storage](https://tapestry.apache.org/session-storage.html).)
Making page data persist across requests to a single page is accomplished with the @[Persist](http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Persist.html) annotation. This annotation is applied to private instance fields of components:
```
@Persist
private int value;
```
Such annotated fields will 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 saved. On later requests to the same page, the value for the field is restored.
## Persistence Strategies
The value for each field is the *strategy* used to store the field between requests.
### Session Strategy
**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)
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.
**Example: Session Strategy**
```
@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.
**Example: Flash Strategy**
```
@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).
Client persistence is somewhat expensive. It can bloat the size of the rendered pages by adding hundreds of characters to each link. There is extra processing on each request to de-serialize the values encoded into the query parameter.
Client persistence does not scale very well; as more information is stored into the query parameter, its length can become problematic. In many cases, web browsers, firewalls or other servers may silently truncate the URL which will break the application.
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.
**Example: Client Strategy**
```
@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.
**"Hibernate Entity Strategy"**
```
@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).
**"Example: JPA Entity Strategy"**
```
@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](http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Meta.html) 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).
In any case, if no component provides the meta data, then the ultimate default, "session", is used.
## Default Values
Fields marked with @Persist may not have default values (whether set inline, or inside a constructor).
## Clearing Persistent Fields
If you reach a point where you know that all data for a page can be discarded, you can do exactly that.
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.
## Clustering Issues
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.
However, many web applications do not use the HttpSession this way, instead storing large, mutable objects in the session. This is not a problem 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 that serialization and distribution whenever HttpSession.setAttribute() is called. This creates a data consistency 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.
But while this solution solves the data consistency problem, it does so at the expense of performance, since all of those calls to setAttribute() result in extra session data being replicated needlessly if the internal state of the mutable object hasn't changed.
Tapestry has solutions to this, too:
### @ImmutableSessionPersistedObject Annotation
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 (and thus not requiring session replication) using the [ImmutableSessionPersistedObject](https://tapestry.apache.org/current/apidocs/org/apache/tapestry5/http/annotations/ImmutableSessionPersistedObject.html) annotation.
### OptimizedSessionPersistedObject Interface
The [OptimizedSessionPersistedObject](https://tapestry.apache.org/current/apidocs/org/apache/tapestry5/http/OptimizedSessionPersistedObject.html) 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](http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/BaseOptimizedSessionPersistedObject.html) base class.
### SessionPersistedObjectAnalyzer Service
The [SessionPersistedObjectAnalyzer](https://tapestry.apache.org/current/apidocs/org/apache/tapestry5/http/services/SessionPersistedObjectAnalyzer.html) service is ultimately responsible for determining whether a session persisted object is dirty or not (dirty meaning in need of a restore into the session). This is an extensible service where new strategies, for new classes, can be introduced.
**Example: Entity Session Strategy**
```
@Persist(HibernatePersistenceConstants.ENTITY)
private User user;
```
**"Example:JAP Session Strategy"**
```
@Persist(JpaPersistenceConstants.ENTITY)
private Account account;
```
Apache Tapestry, Tapestry, Apache, the Apache feather logo, and the Apache Tapestry project logo are trademarks of The Apache Software Foundation.
Apache Tapestry
- [Home](https://tapestry.apache.org/index.html)
- [Download](https://tapestry.apache.org/download.html)
- [Team](https://tapestry.apache.org/about.html)
- [License](https://www.apache.org/licenses/LICENSE-2.0)
Documentation
- [Introduction](https://tapestry.apache.org/introduction.html)
- [Principles](https://tapestry.apache.org/principles.html)
- [Getting Started](https://tapestry.apache.org/getting-started.html)
- [User Guide](https://tapestry.apache.org/user-guide.html)
- [Docs](https://tapestry.apache.org/documentation.html)
- [Component Reference](https://tapestry.apache.org/component-reference.html)
- [Apidocs](https://tapestry.apache.org/current/apidocs)
- [FAQ](https://tapestry.apache.org/frequently-asked-questions.html)
Community
- [Mailing Lists](https://tapestry.apache.org/community.html)
- [StackOverflow](https://stackoverflow.com/questions/tagged/tapestry)
- [Support](https://tapestry.apache.org/support.html)
- [Getting Involved](https://tapestry.apache.org/community.html)
- [Edit this page](https://cwiki.apache.org/confluence/pages/editpage.action?pageId=22872122 "Edit this page (requires approval, just ask on the mailing list)")
Social
- [@ApacheTapestry](https://twitter.com/ApacheTapestry)
- [\#tapestry5](https://twitter.com/hashtag/tapestry5)
Development
- [Source Code](https://gitbox.apache.org/repos/asf?p=tapestry-5.git)
- [Issues](https://issues.apache.org/jira/browse/TAP5)
Apache
- [About Apache](https://www.apache.org/)
- [Events](https://apachecon.com/?ref=royale.apache.org)
- [Sponsorship](https://www.apache.org/foundation/sponsorship.html)
- [License](https://www.apache.org/licenses/LICENSE-2.0)
- [Security](https://www.apache.org/security/)
- [Thanks\!](https://www.apache.org/foundation/thanks.html)
About us

[Apache Tapestry™](https://tapestry.apache.org/) is an open-source component-oriented framework for creating dynamic, robust, highly scalable web applications in Java.
Tapestry complements and builds upon the standard Java Servlet API, and so it works in any servlet container or application server.

[Apache Tapestry™](https://tapestry.apache.org/), [Apache™](https://www.apache.org/) and the [Apache feather logo™](https://www.apache.org/foundation/press/kit/) are trademarks of The Apache Software Foundation. All other marks mentioned may be trademarks or registered trademarks of their respective owners.
Copyright © 2020 The Apache Software Foundation, Licensed under the [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0). |
| Readable Markdown | null |
| Shard | 160 (laksa) |
| Root Hash | 12506625474657113760 |
| Unparsed URL | org,apache!tapestry,/persistent-page-data.html s443 |