Common problems with parameters and data values exist in the different approaches provided by the Spring Framework JDBC.
Usually Spring determines the SQL type of the parameters based on the type of parameter passed in. It is possible to explicitly provide the SQL type to be used when setting parameter values. This is sometimes necessary to correctly set NULL values.
You can provide SQL type information in several ways:
Many update and query methods of the
JdbcTemplate take an additional parameter in
the form of an int array. This array is used to
indicate the SQL type of the coresponding parameter using constant
values from the java.sql.Types class. Provide
one entry for each parameter.
You can use the SqlParameterValue class
to wrap the parameter value that needs this additional information.
Create
a new instance for each value and pass in the SQL type and parameter
value in the constructor. You can also provide an optional scale
parameter for numeric values.
For methods working with named parameters, use the
SqlParameterSource classes
BeanPropertySqlParameterSource or
MapSqlParameterSource. They both have methods
for registering the SQL type for any of the named parameter
values.
You can store images, other binary objects, and large chunks of
text. These large object are called BLOB for binary data and CLOB for
character data. In Spring you can handle these large objects by using
the JdbcTemplate directly and also when using the higher abstractions
provided by RDBMS Objects and the SimpleJdbc classes. All
of these approaches use an implementation of the
LobHandler interface for the actual management of
the LOB data. The LobHandler provides access to a
LobCreatorclass,
through the getLobCreator method, used for
creating new LOB objects to be inserted.
The LobCreator/LobHandler provides the
following support for LOB input and output:
BLOB
byte[] – getBlobAsBytes and setBlobAsBytes
InputStream – getBlobAsBinaryStream and setBlobAsBinaryStream
CLOB
String – getClobAsString and setClobAsString
InputStream – getClobAsAsciiStream and setClobAsAsciiStream
Reader – getClobAsCharacterStream and setClobAsCharacterStream
The next example shows how to create and insert a BLOB. Later you will see how to read it back from the database.
This example uses a JdbcTemplate and an
implementation of the
AbstractLobCreatingPreparedStatementCallback.
It implements one method, setValues. This method provides a
LobCreator that you use to set the values for the LOB
columns in your SQL insert statement.
For this example we assume that there is a variable,
lobHandler, that already is set to an instance
of a DefaultLobHandler. You typically set this
value through dependency injection.
final File blobIn = new File("spring2004.jpg"); final InputStream blobIs = new FileInputStream(blobIn); final File clobIn = new File("large.txt"); final InputStream clobIs = new FileInputStream(clobIn); final InputStreamReader clobReader = new InputStreamReader(clobIs); jdbcTemplate.execute( "INSERT INTO lob_table (id, a_clob, a_blob) VALUES (?, ?, ?)", new AbstractLobCreatingPreparedStatementCallback(lobHandler) {protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException { ps.setLong(1, 1L); lobCreator.setClobAsCharacterStream(ps, 2, clobReader, (int)clobIn.length());
lobCreator.setBlobAsBinaryStream(ps, 3, blobIs, (int)blobIn.length());
} } ); blobIs.close(); clobReader.close();
| Pass in the lobHandler that in this example is a plain
|
| Using the method
|
| Using the method
|
Now it's time to read the LOB data from the database. Again, you
use a JdbcTempate with the same instance variable
lobHandler and a reference to a
DefaultLobHandler.
List<Map<String, Object>> l = jdbcTemplate.query("select id, a_clob, a_blob from lob_table", new RowMapper<Map<String, Object>>() { public Map<String, Object> mapRow(ResultSet rs, int i) throws SQLException { Map<String, Object> results = new HashMap<String, Object>(); String clobText = lobHandler.getClobAsString(rs, "a_clob");results.put("CLOB", clobText); byte[] blobBytes = lobHandler.getBlobAsBytes(rs, "a_blob");
results.put("BLOB", blobBytes); return results; } });
| Using the method |
| Using the method |
The SQL standard allows for selecting rows based on an expression
that includes a variable list of values. A typical example would be
"select * from T_ACTOR where id in (1, 2, 3). This variable
list is not directly supported for prepared statements by the JDBC
standard; you cannot declare a variable number of placeholders. You need
a number of variations with the desired number of placeholders prepared,
or you need to generate the SQL string dynamically once you know how
many placeholders are required. The named parameter support provided in
the NamedParameterJdbcTemplate and
SimpleJdbcTemplate takes the latter approach.
Pass in the values as a java.util.List of
primitive objects. This list will be used to insert the required
placeholders and pass in the values during the statement
execution.
![]() | Note |
|---|---|
Be careful when passing in many values. The JDBC standard does
not guarantee that you can use more than 100 values for an
|
In addition to the primitive values in the value list, you can
create a java.util.List of object arrays. This
list would support multiple expressions defined for the in
clause such as "select * from T_ACTOR where (id, last_name) in
((1, 'Johnson'), (2, 'Harrop'))". This
of course requires that your database supports this syntax.
When you call stored procedures you can sometimes use complex
types specific to the database. To accommodate these types, Spring
provides a SqlReturnType for handling them when
they are returned from the stored procedure call and
SqlTypeValue when they are passed in as a
parameter to the stored procedure.
Here is an example of returning the value of an Oracle
STRUCT object of the user declared type
ITEM_TYPE. The SqlReturnType
interface has a single method named getTypeValue
that must be implemented. This interface is used as part of the
declaration of an SqlOutParameter.
final TestItem - new TestItem(123L, "A test item", new SimpleDateFormat("yyyy-M-d").parse("2010-12-31");); declareParameter(new SqlOutParameter("item", OracleTypes.STRUCT, "ITEM_TYPE", new SqlReturnType() { public Object getTypeValue(CallableStatement cs, int colIndx, int sqlType, String typeName) throws SQLException { STRUCT struct = (STRUCT)cs.getObject(colIndx); Object[] attr = struct.getAttributes(); TestItem item = new TestItem(); item.setId(((Number) attr[0]).longValue()); item.setDescription((String)attr[1]); item.setExpirationDate((java.util.Date)attr[2]); return item; } }));
You use the SqlTypeValue to
pass in the value of a Java object like TestItem
into a stored procedure. The
SqlTypeValue interface has a single method named
createTypeValue that you must implement. The
active connection is passed in, and you can use it to create
database-specific objects such as
StructDescriptors, as shown in the following
example, or ArrayDescriptors.
final TestItem - new TestItem(123L, "A test item", new SimpleDateFormat("yyyy-M-d").parse("2010-12-31");); SqlTypeValue value = new AbstractSqlTypeValue() { protected Object createTypeValue(Connection conn, int sqlType, String typeName) throws SQLException { StructDescriptor itemDescriptor = new StructDescriptor(typeName, conn); Struct item = new STRUCT(itemDescriptor, conn, new Object[] { testItem.getId(), testItem.getDescription(), new java.sql.Date(testItem.getExpirationDate().getTime()) }); return item; } };
This SqlTypeValue can now be added
to the Map containing the input parameters for the execute call of the
stored procedure.
Another use for the SqlTypeValue is passing
in an array of values to an Oracle stored procedure. Oracle has its own
internal ARRAY class that must be used in this
case, and you can use the SqlTypeValue to create
an instance of the Oracle ARRAY and populate it
with values from the Java ARRAY.
final Long[] ids = new Long[] {1L, 2L}; SqlTypeValue value = new AbstractSqlTypeValue() { protected Object createTypeValue(Connection conn, int sqlType, String typeName) throws SQLException { ArrayDescriptor arrayDescriptor = new ArrayDescriptor(typeName, conn); ARRAY idArray = new ARRAY(arrayDescriptor, conn, ids); return idArray; } };