polish
This commit is contained in:
@@ -6,23 +6,14 @@
|
||||
<para>
|
||||
Most applications access data in some way.
|
||||
Many modify data shared by multiple users and therefore require transactional data access properties.
|
||||
This chapter shows you how to use flows to manage data access in a web application.
|
||||
They often transform relational data sets into domain objects to support application processing.
|
||||
Web Flow offers "flow managed persistence" where a flow can create, commit, and close a object persistence context for you.
|
||||
Web Flow integrates both Hibernate and JPA object persistence technologies.
|
||||
</para>
|
||||
</sect1>
|
||||
<sect1 id="flow-managed-data-access-patterns">
|
||||
<title>Data access patterns</title>
|
||||
<para>
|
||||
There are essentially three flow-managed data access patterns:
|
||||
</para>
|
||||
<orderedlist>
|
||||
<listitem><para>The FlowScoped PersistenceContext</para></listitem>
|
||||
<listitem><para>The ConversationScoped PersistenceContext</para></listitem>
|
||||
<listitem><para>The ViewState PersistenceContext</para></listitem>
|
||||
</orderedlist>
|
||||
<para>
|
||||
Apart from these three patterns, there is the pattern of fully encapsulating PersistenceContext management within the service layer of your application.
|
||||
Apart from flow-managed persistence, there is the pattern of fully encapsulating PersistenceContext management within the service layer of your application.
|
||||
In that case, the web layer does not get involved with persistence, instead it works entirely with detached objects that are passed to and returned by your service layer.
|
||||
This chapter will focus on the flow-managed persistence patterns, exploring how and when to use them.
|
||||
This chapter will focus on the flow-managed persistence, exploring how and when to use this feature.
|
||||
</para>
|
||||
</sect1>
|
||||
<sect1 id="flowScopedPersistenceContext">
|
||||
@@ -82,32 +73,4 @@
|
||||
Such data access operations should always execute non transactionally or in read-only transactions to maintain isolation of intermediate edits.
|
||||
</para>
|
||||
</sect1>
|
||||
<sect1 id="conversationScopedPersistenceContext">
|
||||
<title>ConversationScoped PersistenceContext</title>
|
||||
<para>
|
||||
This pattern creates a <code>PersistenceContext</code> in <code>conversationScope</code> on flow startup, and uses that context for data access until the conversation ends.
|
||||
This pattern generally employs a manual flush mode; that is, the application decides when to flush changes to the database.
|
||||
A convention for flow-managed flushing may be employed, such as always flushing after a transition from one view-state to another.
|
||||
This pattern provides no flow-level isolation of intermediate edits since flushing synchronizes persistent object state with the database.
|
||||
With this pattern, all data access generally occurs non-transactionally.
|
||||
</para>
|
||||
<para>
|
||||
An implementation of this flow-managed persistence pattern is not yet available in the Web Flow distribution, and will be considered for future releases.
|
||||
</para>
|
||||
</sect1>
|
||||
<sect1 id="viewScopedPersistenceContext">
|
||||
<title>ViewState PersistenceContext</title>
|
||||
<para>
|
||||
This pattern creates a <code>PersistenceContext</code> with a read-only transaction before view rendering.
|
||||
The context is used to load the model for the view. It stays open through view rendering and commits after rendering completes.
|
||||
Then on the occurrence of an event, this pattern creates a fresh <code>PersistenceContext</code> with a read-write transaction.
|
||||
The model is merged with the new context and any changes are committed after event handling completes.
|
||||
Therefore, this pattern results in edits being saved to the DB after each event. Any rollback of intermediate flow steps requires compensating transactions.
|
||||
This pattern is often used in conjunction with an optimistic locking strategy to protect the integrity of data modified in parallel by multiple users.
|
||||
This pattern stores much less flow state, but generally increases the amount of data access since persistent entities are fetched and flushed more often.
|
||||
</para>
|
||||
<para>
|
||||
An implementation of this flow-managed persistence pattern is not yet available in the Web Flow distribution, and will be considered for future releases.
|
||||
</para>
|
||||
</sect1>
|
||||
</chapter>
|
||||
@@ -319,63 +319,88 @@ public class StringToMonetaryAmount extends StringToObject {
|
||||
<sect1 id="view-validate">
|
||||
<title>Validating a model</title>
|
||||
<para>
|
||||
Model validation is driven by constraints specified against the model object.
|
||||
These constraints may be specified declaratively, or enforced using a programmatic validation routine or external <code>Validator</code>.
|
||||
Model validation is driven by constraints specified against a model object.
|
||||
Web Flow supports enforcing such constraints programatically.
|
||||
</para>
|
||||
<sect2 id="view-validation-programmatic">
|
||||
<title>Programmatic validation</title>
|
||||
<para>
|
||||
There are two ways to perform model validation programatically.
|
||||
Both options use a <code>ValidationContext</code> parameter for recording validation error messages in addition to the current <code>Principal</code> and user event that triggered validation.
|
||||
The user event is the same value that matches against the 'on' attribute of a transition.
|
||||
</para>
|
||||
<para>
|
||||
Prior to Web Flow 2.0.4, a <code>MessageContext</code> was required instead of a ValidationContext.
|
||||
This style validator is still supported, however, the ValidationContext provides access to the MessageContext and additional information.
|
||||
The first is to implement validation logic in your model object.
|
||||
The second is to implement an external <code>Validator</code>.
|
||||
Both ways provide you with a <code>ValidationContext</code> to record error messages and access information about the current user.
|
||||
</para>
|
||||
<sect3 id="view-validation=programmatic-validate-method">
|
||||
<title>Implementing a model validate method</title>
|
||||
<para>
|
||||
The first way is to define a validate method on the model object class.
|
||||
To do this, create a public method with the name <code>validate${state}</code>, where <code>state</code> is the id of your view-state.
|
||||
Defining validation logic in your model object is the simplest way to validate its state.
|
||||
Once such logic is structured according to Web Flow conventions, Web Flow will automatically invoke that logic during the view-state postback lifecycle.
|
||||
Web Flow conventions have you structure model validation logic by view-state, allowing you to easily validate the subset of model properties that are editable on that view.
|
||||
To do this, simply create a public method with the name <code>validate${state}</code>, where <code>${state}</code> is the id of your view-state where you want validation to run.
|
||||
For example:
|
||||
</para>
|
||||
<programlisting language="java"><![CDATA[
|
||||
public void validateEnterBookingDetails(ValidationContext context) {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
if (checkinDate.before(today())) {
|
||||
context.getMessageContext().addMessage(new MessageBuilder().error().source(
|
||||
"checkinDate").defaultText("Check in date must be a future date").build());
|
||||
} else if (!checkinDate.before(checkoutDate)) {
|
||||
context.getMessageContext().addMessage(new MessageBuilder().error().source(
|
||||
"checkoutDate").defaultText("Check out date must be later than check in date")
|
||||
.build());
|
||||
public class Booking {
|
||||
private Date checkinDate;
|
||||
private Date checkoutDate;
|
||||
...
|
||||
|
||||
public void validateEnterBookingDetails(ValidationContext context) {
|
||||
MessageContext messages = context.getMessages();
|
||||
if (checkinDate.before(today())) {
|
||||
messages.addMessage(new MessageBuilder().error().source("checkinDate").
|
||||
defaultText("Check in date must be a future date").build());
|
||||
} else if (!checkinDate.before(checkoutDate)) {
|
||||
messages.addMessage(new MessageBuilder().error().source("checkoutDate").
|
||||
defaultText("Check out date must be later than check in date").build());
|
||||
}
|
||||
}
|
||||
}]]>
|
||||
}
|
||||
]]>
|
||||
</programlisting>
|
||||
<para>
|
||||
In the example above, when a transition is triggered in a <code>enterBookingDetails</code> view-state that is editing a <code>Booking</code> model,
|
||||
Web Flow will invoke the <code>validateEnterBookingDetails(ValidationContext)</code> method automatically unless validation has been suppressed for that transition.
|
||||
An example of such a view-state is shown below:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<view-state id="enterBookingDetails" model="booking">
|
||||
<transition on="proceed" to="reviewBooking">
|
||||
</view-state>]]>
|
||||
</programlisting>
|
||||
<para>
|
||||
Any number of validation methods are defined. Generally, a flow edits a model over a series of views. In that case, a validate method would be defined
|
||||
for each view-state where validation needs to run.
|
||||
</para>
|
||||
</sect3>
|
||||
<sect3 id="view-validation=programmatic-validator">
|
||||
<title>Implementing a Validator</title>
|
||||
<para>
|
||||
The second way is to define a separate object, called a <emphasis>Validator</emphasis>, which validates your model object.
|
||||
To do this, create a class that defines a public method with the name <code>validate${state}</code>, where <code>state</code> is the id of your view-state.
|
||||
To do this, first create a class whose name has the pattern ${model}Validator, where <code>${model}</code> is the capitialized form of the model expression, such as <code>booking</code>.
|
||||
Then define a public method with the name <code>validate${state}</code>, where <code>${state}</code> is the id of your view-state, such as <code>enterBookingDetails</code>.
|
||||
The class should then be deployed as a Spring bean. Any number of validation methods can be defined.
|
||||
For example:
|
||||
</para>
|
||||
<programlisting language="java"><![CDATA[
|
||||
@Component
|
||||
public class BookingValidator {
|
||||
public void validateEnterBookingDetails(Booking booking, ValidationContext context) {
|
||||
MessageContext messages = context.getMessages();
|
||||
if (booking.getCheckinDate().before(today())) {
|
||||
context.getMessageContext().addMessage(new MessageBuilder().error().source(
|
||||
"checkinDate").defaultText("Check in date must be a future date").build());
|
||||
} else if (!booking.getCheckinDate().before(checkoutDate)) {
|
||||
context.getMessageContext().addMessage(new MessageBuilder().error().source(
|
||||
"checkoutDate").defaultText("Check out date must be later than check in date")
|
||||
.build());
|
||||
messages.addMessage(new MessageBuilder().error().source("checkinDate").
|
||||
defaultText("Check in date must be a future date").build());
|
||||
} else if (!booking.getCheckinDate().before(booking.getCheckoutDate())) {
|
||||
messages.addMessage(new MessageBuilder().error().source("checkoutDate").
|
||||
defaultText("Check out date must be later than check in date").build());
|
||||
}
|
||||
}
|
||||
}]]>
|
||||
</programlisting>
|
||||
<para>
|
||||
In the example above, when a transition is triggered in a <code>enterBookingDetails</code> view-state that is editing a <code>Booking</code> model,
|
||||
Web Flow will invoke the <code>validateEnterBookingDetails(Booking, ValidationContext)</code> method automatically unless validation has been suppressed for that transition.
|
||||
</para>
|
||||
<para>
|
||||
A Validator can also accept a Spring MVC <code>Errors</code> object, which is required for invoking existing Spring Validators.
|
||||
</para>
|
||||
@@ -386,12 +411,13 @@ public class BookingValidator {
|
||||
</para>
|
||||
</sect3>
|
||||
</sect2>
|
||||
<sect2 id="view-validation-declarative">
|
||||
<title>Declarative validation</title>
|
||||
<sect2 id="view-validation-context">
|
||||
<title>ValidationContext</title>
|
||||
<para>
|
||||
Spring Web Flow does not yet ship integration with a declarative validation framework such as Hibernate Validator.
|
||||
It is expected that integration will be provided in a future Web Flow release.
|
||||
This integration will allow declarative validation constraints to be defined against model properties.
|
||||
A ValidationContext allows you to obtain a <code>MessageContext</code> to record messages during validation.
|
||||
It also exposes information about the current user, such as the signaled <code>userEvent</code> and the current user's <code>Principal</code> identity.
|
||||
This information can be used to customize validation logic based on what button or link was activated in the UI, or who is authenticated.
|
||||
See the API Javadocs for <code>ValidationContext</code> for more information.
|
||||
</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
Reference in New Issue
Block a user