The org.springframework.jdbc.object package
contains classes that allow you to access the database in a more
object-oriented manner. As an example, you can execute queries and get the
results back as a list containing business objects with the relational
column data mapped to the properties of the business object. You can also
execute stored procedures and run update, delete, and insert
statements.
![]() | Note |
|---|---|
Many Spring developers believe that the various RDBMS operation
classes described below (with the exception of the However, if you are getting measurable value from using the RDBMS operation classes, continue using these classes. |
SqlQuery is a reusable, threadsafe class
that encapsulates an SQL query. Subclasses must implement the
newRowMapper(..) method to provide a
RowMapper instance that can create one
object per row obtained from iterating over the
ResultSet that is created during the
execution of the query. The SqlQuery class is
rarely used directly because the MappingSqlQuery
subclass provides a much more convenient implementation for mapping rows
to Java classes. Other implementations that extend
SqlQuery are
MappingSqlQueryWithParameters and
UpdatableSqlQuery.
MappingSqlQuery is a reusable query in
which concrete subclasses must implement the abstract
mapRow(..) method to convert each row of the
supplied ResultSet into an object of the
type specified. The following example shows a custom query that maps the
data from the t_actor relation to an instance of the
Actor class.
public class ActorMappingQuery extends MappingSqlQuery<Actor> { public ActorMappingQuery(DataSource ds) { super(ds, "select id, first_name, last_name from t_actor where id = ?"); super.declareParameter(new SqlParameter("id", Types.INTEGER)); compile(); } @Override protected Actor mapRow(ResultSet rs, int rowNumber) throws SQLException { Actor actor = new Actor(); actor.setId(rs.getLong("id")); actor.setFirstName(rs.getString("first_name")); actor.setLastName(rs.getString("last_name")); return actor; } }
The class extends MappingSqlQuery
parameterized with the Actor type. The
constructor for this customer query takes the
DataSource as the only parameter. In this
constructor you call the constructor on the superclass with the
DataSource and the SQL that should be
executed to retrieve the rows for this query. This SQL will be used to
create a PreparedStatement so it may
contain place holders for any parameters to be passed in during
execution.You
must declare each parameter using the
declareParameter method passing in an
SqlParameter. The
SqlParameter takes a name and the JDBC type as
defined in java.sql.Types. After you define all
parameters, you call the compile() method so the
statement can be prepared and later executed. This class is thread-safe
after it is compiled, so as long as these instances
are created when the DAO is initialized they can be kept as instance
variables and be reused.
private ActorMappingQuery actorMappingQuery; @Autowired public void setDataSource(DataSource dataSource) { this.actorMappingQuery = new ActorMappingQuery(dataSource); } public Customer getCustomer(Long id) { return actorMappingQuery.findObject(id); }
The method in this example retrieves the customer with the id that
is passed in as the only parameter. Since we only want one object
returned we simply call the convenience method findObject
with the id as parameter. If we instead had a query the returned a list
of objects and took additional parameters then we would use one of the
execute methods that takes an array of parameter values passed in as
varargs.
public List<Actor> searchForActors(int age, String namePattern) { List<Actor> actors = actorSearchMappingQuery.execute(age, namePattern); return actors; }
The SqlUpdate class encapsulates an SQL
update. Like a query, an update object is reusable, and like all
RdbmsOperation classes, an update can have
parameters and is defined in SQL. This class provides a number of
update(..) methods analogous to the
execute(..) methods of query objects. The
SQLUpdate class is concrete. It can be
subclassed, for example, to add a custom update method, as in the
following snippet where it's simply called
execute. However,
you don't have to subclass the SqlUpdate class
since it can easily be parameterized by setting SQL and declaring
parameters.
import java.sql.Types; import javax.sql.DataSource; import org.springframework.jdbc.core.SqlParameter; import org.springframework.jdbc.object.SqlUpdate; public class UpdateCreditRating extends SqlUpdate { public UpdateCreditRating(DataSource ds) { setDataSource(ds); setSql("update customer set credit_rating = ? where id = ?"); declareParameter(new SqlParameter("creditRating", Types.NUMERIC)); declareParameter(new SqlParameter("id", Types.NUMERIC)); compile(); } /** * @param id for the Customer to be updated * @param rating the new value for credit rating * @return number of rows updated */ public int execute(int id, int rating) { return update(rating, id); } }
The StoredProcedure class is a superclass
for object abstractions of RDBMS stored procedures. This class is
abstract, and its various
execute(..) methods have protected
access, preventing use other than through a subclass that offers tighter
typing.
The inherited sql property will be the name of
the stored procedure in the RDBMS.
To define a parameter for the
StoredProcedure class, you use an
SqlParameter or one of its subclasses. You must
specify the parameter name and SQL type in the constructor like in the
following code snippet. The SQL type is specified using the
java.sql.Types constants.
new SqlParameter("in_id", Types.NUMERIC), new SqlOutParameter("out_first_name", Types.VARCHAR),
The first line with the SqlParameter
declares an IN parameter. IN parameters can be used for both stored
procedure calls and for queries using the
SqlQuery and its subclasses covered in the
following section.
The second line with the SqlOutParameter
declares an out parameter to be used in the stored
procedure call. There is also an
SqlInOutParameter for
InOut parameters, parameters that provide an
in value to the procedure and that also return a
value.
For in parameters, in addition to the
name and the SQL type, you can specify a scale for numeric data or a
type name for custom database types. For out parameters you
can provide a RowMapper to handle mapping of rows
returned from a REF cursor. Another option is to specify an
SqlReturnType that enables you to define
customized handling of the return values.
Here is an example of a simple DAO that uses a
StoredProcedure to call a function,
sysdate(),which comes with any Oracle database. To
use the stored procedure functionality you have to create a class that
extends StoredProcedure. In this example, the
StoredProcedure class is an inner class, but if
you need to reuse the StoredProcedure you declare
it as a top-level class. This example has no input parameters, but an
output parameter is declared as a date type using the class
SqlOutParameter. The execute()
method executes the procedure and extracts the returned date from the
results Map. The results
Map has an entry for each declared output
parameter, in this case only one, using the parameter name as the
key.
import java.sql.Types; import java.util.Date; import java.util.HashMap; import java.util.Map; import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.SqlOutParameter; import org.springframework.jdbc.object.StoredProcedure; public class StoredProcedureDao { private GetSysdateProcedure getSysdate; @Autowired public void init(DataSource dataSource) { this.getSysdate = new GetSysdateProcedure(dataSource); } public Date getSysdate() { return getSysdate.execute(); } private class GetSysdateProcedure extends StoredProcedure { private static final String SQL = "sysdate"; public GetSysdateProcedure(DataSource dataSource) { setDataSource(dataSource); setFunction(true); setSql(SQL); declareParameter(new SqlOutParameter("date", Types.DATE)); compile(); } public Date execute() { // the 'sysdate' sproc has no input parameters, so an empty Map is supplied... Map<String, Object> results = execute(new HashMap<String, Object>()); Date sysdate = (Date) results.get("date"); return sysdate; } } }
The following example of a StoredProcedure
has two output parameters (in this case, Oracle REF cursors).
import oracle.jdbc.OracleTypes; import org.springframework.jdbc.core.SqlOutParameter; import org.springframework.jdbc.object.StoredProcedure; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; public class TitlesAndGenresStoredProcedure extends StoredProcedure { private static final String SPROC_NAME = "AllTitlesAndGenres"; public TitlesAndGenresStoredProcedure(DataSource dataSource) { super(dataSource, SPROC_NAME); declareParameter(new SqlOutParameter("titles", OracleTypes.CURSOR, new TitleMapper())); declareParameter(new SqlOutParameter("genres", OracleTypes.CURSOR, new GenreMapper())); compile(); } public Map<String, Object> execute() { // again, this sproc has no input parameters, so an empty Map is supplied return super.execute(new HashMap<String, Object>()); } }
Notice how the overloaded variants of the
declareParameter(..) method that have been used in
the TitlesAndGenresStoredProcedure constructor
are passed RowMapper implementation
instances; this is a very convenient and powerful way to reuse existing
functionality. The code for the two
RowMapper implementations is provided
below.
The TitleMapper class maps a
ResultSet to a
Title domain object for each row in the supplied
ResultSet:
import org.springframework.jdbc.core.RowMapper; import java.sql.ResultSet; import java.sql.SQLException; import com.foo.domain.Title; public final class TitleMapper implements RowMapper<Title> { public Title mapRow(ResultSet rs, int rowNum) throws SQLException { Title title = new Title(); title.setId(rs.getLong("id")); title.setName(rs.getString("name")); return title; } }
The GenreMapper class maps a
ResultSet to a
Genre domain object for each row in the supplied
ResultSet.
import org.springframework.jdbc.core.RowMapper; import java.sql.ResultSet; import java.sql.SQLException; import com.foo.domain.Genre; public final class GenreMapper implements RowMapper<Genre> { public Genre mapRow(ResultSet rs, int rowNum) throws SQLException { return new Genre(rs.getString("name")); } }
To pass parameters to a stored procedure that has one or more
input parameters in its definition in the RDBMS, you can code a strongly
typed execute(..) method that would delegate to the
superclass' untyped execute(Map parameters) method
(which has protected access); for
example:
import oracle.jdbc.OracleTypes; import org.springframework.jdbc.core.SqlOutParameter; import org.springframework.jdbc.core.SqlParameter; import org.springframework.jdbc.object.StoredProcedure; import javax.sql.DataSource; import java.sql.Types; import java.util.Date; import java.util.HashMap; import java.util.Map; public class TitlesAfterDateStoredProcedure extends StoredProcedure { private static final String SPROC_NAME = "TitlesAfterDate"; private static final String CUTOFF_DATE_PARAM = "cutoffDate"; public TitlesAfterDateStoredProcedure(DataSource dataSource) { super(dataSource, SPROC_NAME); declareParameter(new SqlParameter(CUTOFF_DATE_PARAM, Types.DATE); declareParameter(new SqlOutParameter("titles", OracleTypes.CURSOR, new TitleMapper())); compile(); } public Map<String, Object> execute(Date cutoffDate) { Map<String, Object> inputs = new HashMap<String, Object>(); inputs.put(CUTOFF_DATE_PARAM, cutoffDate); return super.execute(inputs); } }