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)
}
}
}
}
GO TO FULL VERSION