Files
spring-integration/src/docbkx/jdbc.xml
2010-06-24 08:24:25 +00:00

178 lines
9.2 KiB
XML

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="jdbc">
<title>JDBC Support</title>
<para>Spring Integration provides Channel Adapters for receiving and sending
messages via database queries.</para>
<section id="jdbc-inbound-channel-adapter">
<title>Inbound Channel Adapter</title>
<para>The main function of an inbound Channel Adapter is to execute a SQL
<code>SELECT</code> query and turn the result set into a message. The
message payload is the whole result set, expressed as a
<classname>List</classname>, and the types of the items in the list
depends on the row-mapping strategy that is used. The default strategy is
a generic mapper that just returns a <classname>Map</classname> for each
row i nthe query. Optionally this can be changed by adding a reference to
requires a reference to a <classname>RowMapper</classname> instance (see
the <ulink
url="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/jdbc.html">Spring
JDBC</ulink> documentation for more detailed information about row
mapping).<note>
<para>If you want to convert rows in the SELECT query result to
individual messages you can use a downstream splitter.</para>
</note></para>
<para>The inbound adapter also requires a reference to either
<classname>JdbcTemplate</classname> instance or
<interfacename>DataSource</interfacename>. The following example defines
an inbound Channel Adapter with a <classname>DataSource</classname>
reference. <programlisting language="xml"><![CDATA[<jdbc:inbound-channel-adapter query="select * from item where status=2"
channel="target" data-source="dataSource"
update="update item set status=10 where id in (:idList)" />]]></programlisting>
<note>
The parameters in the update query are specified with a colon (:) prefix to the name of a map key. This is a standard feature of the named parameter JDBC support in Spring JDBC.
</note></para>
<para>As well as the <code>SELECT</code> statement to generate the
messages, the adapter above also has an <code>UPDATE</code> statement that
is being used to mark the records as processed, so they don't show up in
the next poll. The update is parameterised by the list of ids from the
original select. This is done through a naming convention by default (a
column in the input result set called "id" is translated into a list in
the parameter map for the update called "idList"). To change the parameter
generation strategy you can inject a
<classname>SqlParameterSourceFactory</classname> into the adapter to
override the default behaviour (the adapter has a
<code>sql-parameter-source-factory</code> attribute).</para>
<section>
<title>Polling and Transactions</title>
<para>The inbound adapter accepts a regular Spring Integration poller as
a sub element, so for instance the frequency of the polling can be
controlled. A very important feature of the poller for JDBC usage is the
option to wrap the poll operation in a transaction, for example:</para>
<programlisting><![CDATA[<jdbc:inbound-channel-adapter query="..."
channel="target" data-source="dataSource"
update="...">
<poller>
<interval-trigger interval="1000"/>
<transactional/>
</poller>
</jdbc:inbound-channel-adapter>]]></programlisting>
<para><note>
If a poller is not explicitly specified a default value will be used (and as per normal with Spring Integration can be defined as a top level bean)
</note> In this example the database is polled every 1000
milliseconds, and the update and select queries are both executed in the
same transaction. The transaction manager configuration is not shown,
but as long as it is aware of the data source then the poll is
transactional. A common use case is for the downstream channels to be
direct channels (the default), so that the endpoints are invoked in the
same thread, and hence the same transaction. then if any of them fails,
the transaction rolls back and the input data are reverted to their
original state.</para>
</section>
</section>
<section id="jdbc-outbound-channel-adapter">
<title>Outbound Channel Adapter</title>
<para>The outbound Channel Adapter is the inverse of the inbound: its role
is to handle a message and use it to execute a SQL query. The message
payload and headers are available by default as input parameters to the
query, for instance: <programlisting language="xml"><![CDATA[<jdbc:outbound-channel-adapter
query="insert into foos (id, status, name) values (:headers[$id], 0, :payload[foo])"
channel="input" data-source="dataSource"/>]]></programlisting> In the
example above, messages arriving on the channel "input" have a payload of
a map with key "foo", so the <code>[]</code> operator dereferences that
value from the map. The headers are also accessed as a map. <note>
The parameters in the query above are bean paths in the incoming message (they are not Spring EL expressions). This behaviour is part of the
<classname>MapSqlParameterSource</classname>
in Spring JDBC, which is the default source created by the outbound adapter. Other behaviour is possible in the adapter, and only requires the user to inject a different
<classname>SqlParameterSourceFactory</classname>
.
</note></para>
<para>The outbound adapter requires a reference to either a DataSource or
a JdbcTemplate. It can also have a
<classname>SqlParameterSourceFactory</classname> injected to control the
binding of incoming message to the query.</para>
<para>If the input channel is a direct channel then the outbound adapter
runs its query in the same thread, and therefor ethe same transaction (if
there is one) as the sender of the message.</para>
</section>
<section>
<title>Message Store</title>
<para>The JDBC module provides an implementation of the Spring Integration
<classname>MessageStore</classname> (important in the Claim Check pattern)
and <classname>MessageGroupStore</classname> (important in stateful
patterns like Aggregator) backed by a database. Both interfaces are
implemented by the JdbcMessageStore and there is also support for
configuring store instances in XML. For example:</para>
<programlisting><![CDATA[<jdbc:message-store id="messageStore" data-source="dataSource"/>]]></programlisting>
<para>A <classname>JdbcTemplate</classname> can be specified instead of a
<classname>DataSource</classname>.</para>
<para>Other optional attributes are show in the next example:</para>
<para><programlisting><![CDATA[<jdbc:message-store id="messageStore" data-source="dataSource"
lob-handler="lobHandler" table-prefix="MY_INT_"/>]]></programlisting>Here we
have specified a <classname>LobHandler</classname> for dealing with
messages as large objects (e.g. often necessary if using Oracle) and a
prefix for the table names in the queries generated by the store. The
table name prefix defaults to "INT_".</para>
<section>
<title>Initializing the Database</title>
<para>Spring Integration ships with some sample scripts that can be used
to initialize a database. In the spring-integration-jdbc JAR file you
will find scripts in the
<classname>org.springframework.integration.jdbc</classname> package:
there is a create and a drop script example for a range of common
database platforms. A common way to use these scripts is to reference
them in a <ulink
url="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/jdbc.html#d0e24182">Spring
JDBC data source initializer</ulink>. Note that the scripts are provided
as samples or specifications of the the required table and column names.
You may find that you need to enhance them for production use (e.g. with
index declarations).</para>
</section>
<section>
<title>Partitioning a Message Store</title>
<para>It is common to use a <classname>JdbcMessageStore</classname> as a
global store for a group of applications, or nodes in the same
application. To provide some portection against name clashes, and to
give control over the database meta-data configuration, the message
store allows the tables to be partitioned in two ways. One is to use
separate table names, by changing the prefix as described above, and the
other is to specify a "region" name for partitioning data within a
single table. An important use case for this is using the store to
manage persistent queues backing a Spring Integration channel. The
message data for a persistent channel is keyed in the store on the
channel name, so if the channel names are not globally unique then there
is the danger of channels picking up data that was not intended for
them. To avoid this the message store region can be used to keep data
separate for different physical channels that happen to have the same
logical name.</para>
</section>
</section>
</chapter>