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
LobCreator
class,
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
AbstractLobCreatingPreparedStatementCallbac
k
.
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,
lobHandle
r
, 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
l
obHandler
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
StructDescriptor
s, as shown in the following
example, or ArrayDescriptor
s.
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; } };