The best thing is use the Action as a thin adaptor between the
web/presentation-tier and your business classes (including those that
access a database).
So you first design a business API that uses plain Java classes.
The best thing is to use objects that take ordinary Java types and return
a JavaBean or collection of JavaBeans.
The Action then calls these objects and passes the result back to the
web/presentation tier.
A common approach is to create an Action class for each of the business
API methods/classes that you need to call.
Ideally, all the database access code should be encapsulated in the
business API classes, so Struts doesn't know what persistent layer you
are using (or even if there is a persistence layer).
It just passes a key or search String and gets back a bean or collection
of beans.
This lets you use the same business API classes in other environments,
and also to run unit tests against your business API outside of Struts or
a HTTP environment.
To get started, it's simplest to setup a 1:1 correspondence between the
Actions and the entry-points to your business API.
As you gain experience, you will find ways to combine your Actions, say
by using the DispatchAction.
It's even possible to use a single "framework" Action to call all of your
business classes, as is done with Scaffold ProcessAction in the contrib
folder.
Using fewer Actions does require a deeper understanding of how Struts and
MVC frameworks operate.
Don't hesitate to err on the side of creating more Actions at first.
The Struts configuration makes it easy to refactor your Actions later,
since you can change the Action type without changing anything else in the
application.
Ideally, the business logic layer should encapsulate the data access
details, including acquiring a database connection.
However, some application designs expect that the caller be able to provide a
database connection or DataSource instance.
When this is the case, the Struts DataSource manager can make it easy for
your Action to produce these resources on demand.
The Struts DataSource manager is configured as an element in the
Struts configuration file (struts-config.xml).
The manager can be used to deploy any connection pool that implements the
javax.sql.DataSource
interface and is configurable totally
from JavaBean properties.
If your DBMS or container provides a connection pool that meets these
requirements, then that component might be your first choice.
The Jakarta Commons dbcp's BasicDataSource
[org.apache.commons.dbcp.BasicDataSource
] also works well
with the DataSource manager, if a native component is not available.
The Struts distribution includes a Generic DataSource class in its util
package, but this is a very simple implementation.
The Generic DataSource is deprecated and will be removed in a future
release in favor of plugging in the BasicDataSource, or another
DataSource implementation.
This is how you would specify a default data source for your application
from the struts-config.xml:
<!-- configuration for GenericDataSource wrapper -->
<data-source>
<set-property
property="autoCommit"
value="false"/>
<set-property
property="description"
value="Example Data Source Configuration"/>
<set-property
property="driverClass"
value="org.postgresql.Driver"/>
<set-property
property="maxCount"
value="4"/>
<set-property
property="minCount"
value="2"/>
<set-property
property="password"
value="mypassword"/>
<set-property
property="url"
value="jdbc:postgresql://localhost/mydatabase"/>
<set-property
property="user"
value="myusername"/>
</data-source>
</data-sources>
This is how you would specify a DBCP BasicDataSource for your application:
<data-sources>
<!-- configuration for commons BasicDataSource -->
<data-source type="org.apache.commons.dbcp.BasicDataSource">
<set-property
property="driverClassName"
value="org.postgresql.Driver" />
<set-property
property="url"
value="jdbc:postgresql://localhost/mydatabase" />
<set-property
property="username"
value="me" />
<set-property
property="password"
value="test" />
<set-property
property="maxActive"
value="10" />
<set-property
property="maxWait"
value="5000" />
<set-property
property="defaultAutoCommit"
value="false" />
<set-property
property="defaultReadOnly"
value="false" />
<set-property
property="validationQuery"
value="SELECT COUNT(*) FROM market" />
</data-source>
</data-sources>
Note that you can define as many datasource objects as your application
requires and refer to each using a logical name.
This can be useful in providing better security or scalability, or even
to test datasource implementations against each other.
After a DataSource is defined, here is an example of using the
manager to establish a connection from within an Action's
execute
method.
public ActionForward
execute(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception
{
javax.sql.DataSource dataSource;
java.sql.Connection myConnection;
try {
dataSource = getDataSource(request);
myConnection = dataSource.getConnection();
// do what you wish with myConnection
} catch (SQLException sqle) {
getServlet().log("Connection.process", sqle);
} finally {
//enclose this in a finally block to make
//sure the connection is closed
try {
myConnection.close();
} catch (SQLException e) {
getServlet().log("Connection.close", e);
}
}
}
Note: If you use the Commons BasicDataSource with Struts,
the query you provide for the pingQuery attribute (if you choose to
include it) must return at least one row.
Example: SELECT COUNT(*) FROM VALIDTABLE
Just be sure you to replace "VALIDTABLE" with the name of a valid table
in your database.