Especificar información de tipo SQL para parámetros
Normalmente, Spring determina el tipo SQL de parámetros según el tipo de parámetro pasado. Puede especificar
explícitamente el tipo de SQL que se utilizará al configurar los valores de los parámetros. A veces, esto es
necesario para establecer los valores NULL
correctamente.
Puede especificar información de tipo SQL de varias maneras:
Muchas Los métodos de actualización y consulta
JdbcTemplate
aceptan un parámetro adicional en forma de matrizint
. Esta matriz se utiliza para especificar el tipo SQL del parámetro correspondiente utilizando valores constantes de la clasejava.sql.Types
. Especifique una entrada para cada parámetro.Puede utilizar la clase
SqlParameterValue
para ajustar el valor de un parámetro que requiere esta información adicional. Para hacer esto, cree una nueva instancia para cada valor y pase el tipo SQL y el valor del parámetro en el constructor. También puede especificar un parámetro de escala opcional para valores numéricos.Para métodos que funcionan con parámetros con nombre, puede usar las clases
SqlParameterSource
,BeanPropertySqlParameterSource
oMapSqlParameterSource
. Ambos tienen métodos para registrar el tipo SQL para cualquiera de los valores de parámetros nombrados.
Manejo de objetos BLOB y CLOB
La base de datos puede almacenar imágenes, otros datos binarios y
grandes fragmentos de texto. En el caso de datos binarios, estos objetos de gran tamaño se denominan objetos grandes
binarios, o abreviadamente BLOB (Binary Large OBject), y objetos grandes de caracteres, o CLOB (Character Large
OBject), en el caso de datos de caracteres. En Spring, puede trabajar con objetos tan grandes usando JdbcTemplate
directamente, así como usando abstracciones de nivel superior proporcionadas por objetos RDBMS y clases SimpleJdbc
.
Todos estos enfoques utilizan una implementación de la interfaz LobHandler
para gestionar realmente los
datos LOB (Large OBject). LobHandler
proporciona acceso a la clase LobCreator
a través del
método getLobCreator
, que se utiliza para crear nuevos objetos LOB para su inserción.
LobCreator
y LobHandler
proporcionan el siguiente soporte para entrada y salida LOB:
BLOB
byte[]
:getBlobAsBytes
ysetBlobAsBytes
InputStream
:getBlobAsBinaryStream
ysetBlobAsBinaryStream
CLOB
String
:getClobAsString
ysetClobAsString
InputStream
:getClobAsAsciiStream
ysetClobAsAsciiStream
Reader
:getClobAsCharacterStream
ysetClobAsCharacterStream
El siguiente ejemplo muestra cómo crear e insertar un BLOB. Más adelante demostraremos cómo volver a leerlo desde la base de datos.
Este ejemplo utiliza JdbcTemplate
y la implementación
AbstractLobCreatingPreparedStatementCallback
. Implementa un método, setValues
. Este método
proporciona un LobCreator
que se utiliza para establecer valores para las columnas LOB en la
instrucción de inserción SQL.
En este ejemplo, asumimos que la variable lobHandler
existe, que
ya está configurado para la instancia DefaultLobHandler
. Normalmente, este valor se establece mediante
inyección de dependencia.
El siguiente ejemplo muestra cómo crear e insertar un BLOB:
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();
- Pasamos
lobHandler
, que (en este ejemplo) es unDefaultLobHandler
normal. - Uso del método
setClobAsCharacterStream
para pasar el contenido del CLOB. - Usar el método
setBlobAsBinaryStream
para pasar el contenido de un BLOB.
val blobIn = File("spring2004.jpg")
val blobIs = FileInputStream(blobIn)
val clobIn = File("large.txt")
val clobIs = FileInputStream(clobIn)
val clobReader = InputStreamReader(clobIs)
jdbcTemplate.execute(
"INSERT INTO lob_table (id, a_clob, a_blob) VALUES (?, ?, ?)",
object: AbstractLobCreatingPreparedStatementCallback(lobHandler) {
override fun setValues(ps: PreparedStatement, lobCreator: LobCreator) {
ps.setLong(1, 1L)
lobCreator.setClobAsCharacterStream(ps, 2, clobReader, clobIn.length().toInt())
lobCreator.setBlobAsBinaryStream(ps, 3, blobIs, blobIn.length().toInt())
}
}
)
blobIs.close()
clobReader.close()
- Pasamos
lobHandler
, que (en este ejemplo) es el habitualDefaultLobHandler
. - Usar el método
setClobAsCharacterStream
para pasar el contenido de un CLOB. - Usar el
setBlobAsBinaryStream
método para pasar el contenido de un BLOB.
Si llama al método setBlobAsBinaryStream
, setClobAsAsciiStream
o setClobAsCharacterStream
Para el LobCreator
devuelto por DefaultLobHandler.getLobCreator()
,
usted Opcionalmente, puede establecer el argumento contentLength
en un valor negativo. Si la longitud
del contenido especificada es negativa, DefaultLobHandler
utiliza variantes de los métodos de
configuración de subprocesos JDBC 4.0 sin un parámetro de longitud. De lo contrario, pasa la longitud especificada
al controlador.
Consulte la documentación del controlador JDBC que está utilizando para asegurarse de que admita la transmisión LOB sin especificar la longitud del contenido.
Ahora es el momento de leer los datos LOB de la
base de datos. Nuevamente, JdbcTemplate
se usa con la misma variable de instancia
lobHandler
y una referencia a DefaultLobHandler
. El siguiente ejemplo muestra cómo hacer
esto:
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;
}
});
- Uso Método
getClobAsString
para obtener el contenido de un CLOB. - Utilice el método
getBlobAsBytes
para obtener el contenido de un BLOB.
val l = jdbcTemplate.query("select id, a_clob, a_blob from lob_table") { rs, _ ->
val clobText = lobHandler.getClobAsString(rs, "a_clob")
val blobBytes = lobHandler.getBlobAsBytes(rs, "a_blob")
mapOf("CLOB" to clobText, "BLOB" to blobBytes)
}
- Usando el método
getClobAsString
para obtener el contenido de un CLOB. - Usando el
getBlobAsBytes
método para obtener el contenido de un BLOB.
Pasar listas de valores para la expresión "IN"
El estándar SQL le permite seleccionar filas basándose en un Expresión que
incluye una lista de valores para una variable. Un ejemplo típico es select * from T_ACTOR donde id está en
(1, 2, 3
). Esta lista de variables no está directamente respaldada por el estándar JDBC para
declaraciones preparadas. Puede declarar un número variable de marcadores de posición. Debe preparar varias opciones
con la cantidad requerida de marcadores de posición, o debe generar la cadena SQL dinámicamente una vez que sepa
cuántos marcadores de posición se requieren. El soporte de parámetros con nombre proporcionado en NamedParameterJdbcTemplate
y JdbcTemplate
adopta el último enfoque. Puede pasar valores como una lista de objetos primitivos
java.util.List
. Esta lista se utiliza para insertar los marcadores de posición necesarios y pasar
valores durante la ejecución de la declaración.
in
. Varias bases de datos
le permiten superar esta cifra, pero normalmente tienen un límite estricto en la cantidad de valores válidos. Por
ejemplo, el límite de Oracle es 1000.
Además de los valores primitivos en una lista de valores, puede crear java.util.List
a partir de
matrices de objetos. Esta lista puede admitir múltiples expresiones definidas para la expresión in
, por
ejemplo select * from T_ACTOR donde (id, apellido) en ((1, 'Johnson'), (2, 'Harrop' ))
. Esto, por
supuesto, requiere que su base de datos admita esta sintaxis.
Manejo de tipos complejos para llamadas a procedimientos almacenados
Al llamar a procedimientos almacenados, a veces puede usar tipos complejos
específicos de la base de datos. Para trabajar con estos tipos, Spring proporciona SqlReturnType
para
manejarlos si se devuelven desde una llamada a un procedimiento almacenado, y SqlTypeValue
si se pasan
como parámetro al procedimiento almacenado.
La interfaz SqlReturnType
tiene un método
único (llamado getTypeValue
) que debe implementarse. Esta interfaz se utiliza como parte de la
declaración del parámetro SqlOutParameter
. El siguiente ejemplo muestra el retorno del valor de un
objeto Oracle llamado STRUCT
de tipo ITEM_TYPE
declarado por el usuario:
public class TestItemStoredProcedure extends StoredProcedure {
public TestItemStoredProcedure(DataSource dataSource) {
// ...
declareParameter(new SqlOutParameter("item", OracleTypes.STRUCT, "ITEM_TYPE",
(CallableStatement cs, int colIndx, int sqlType, String typeName) -> {
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;
}));
// ...
}
class TestItemStoredProcedure(dataSource: DataSource) : StoredProcedure() {
init {
// ...
declareParameter(SqlOutParameter("item", OracleTypes.STRUCT, "ITEM_TYPE") { cs, colIndx, sqlType, typeName ->
val struct = cs.getObject(colIndx) as STRUCT
val attr = struct.getAttributes()
TestItem((attr[0] as Long, attr[1] as String, attr[2] as Date)
})
// ...
}
}
Puede utilizar SqlTypeValue
para pasar el valor de un objeto Java (como TestItem
) a
un procedimiento almacenado. La interfaz SqlTypeValue
tiene un único método (llamado createTypeValue
)
para implementar. La conexión activa se pasa y puede usarla para crear objetos específicos de la base de datos, como
instancias StructDescriptor
o ArrayDescriptor
código de instancias. El siguiente
ejemplo crea una instancia de un StructDescriptor
:
final TestItem 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;
}
};
val (id, description, expirationDate) = TestItem(123L, "A test item",
SimpleDateFormat("yyyy-M-d").parse("2010-12-31"))
val value = object : AbstractSqlTypeValue() {
override fun createTypeValue(conn: Connection, sqlType: Int, typeName: String?): Any {
val itemDescriptor = StructDescriptor(typeName, conn)
return STRUCT(itemDescriptor, conn,
arrayOf(id, description, java.sql.Date(expirationDate.time)))
}
}
Ahora puede agregar este valor SqlTypeValue
al Map
que contiene los parámetros de
entrada para llamar al procedimiento almacenado execute
.
Otro uso de SqlTypeValue
es pasar una matriz de valores a un procedimiento almacenado de Oracle. Oracle tiene su propia clase interna ARRAY
que debe usarse en este caso, puede usar SqlTypeValue
para crear una instancia de ARRAY
de Orcale y completarla con valores de ARRAY
de Java, como se muestra a continuación. ejemplo:
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;
}
};
class TestItemStoredProcedure(dataSource: DataSource) : StoredProcedure() {
init {
val ids = arrayOf(1L, 2L)
val value = object : AbstractSqlTypeValue() {
override fun createTypeValue(conn: Connection, sqlType: Int, typeName: String?): Any {
val arrayDescriptor = ArrayDescriptor(typeName, conn)
return ARRAY(arrayDescriptor, conn, ids)
}
}
}
}