Versiones comparadas

Clave

  • Se ha añadido esta línea.
  • Se ha eliminado esta línea.
  • El formato se ha cambiado.

...

Llamadas a métodos de base de datos

Para llamar a métodos de base de datos utilizaremos JdbcTemplate. En primer lugar, declararemos la instancia en nuestra clase para el servicio REST:

Bloque de código
languagejava
import org.springframework.jdbc.core.JdbcTemplate;

@Autowired
private JdbcTemplate jdbcTemplate;

Tenemos que diferenciar entre funciones y procedimientos:

  • Las funciones (Function) siempre devuelven un valor utilizando return. Aún así, se pueden especificar parámetros de salida, pero no son recomendables. Las funciones se pueden utilizar en instrucciones típicas como SELECT, INSERT, UPDATE,  DELETE, MERGE, mientras que los procedimientos no. Las funciones se utilizan habitualmente para hacer cálculos.
  • Los procedimientos (Procedure) pueden devolver valores o no, pero si se devuelve algo, se tiene que especificar con parámetros de salida. Los procedimientos se suelen utilizar para ejecutar lógica de negocio.

Funciones

Para llamar a funciones de base de datos, podemos hacerlo directamente desde un JPA Repository, indicándo una custom query con SELECT FUNCION(?1, ?2) FROM DUALUn ejemplo simple de cómo se puede utilizar podría ser este:

return jdbcTemplate.execute( new CallableStatementCreator() { @Override public CallableStatement createCallableStatement( Connection con ) throws SQLException { // Definimos la llamada, dejando con ? lo que serán parámetros, tanto de salida como de entrada final CallableStatement cs = con.prepareCall( "{ ? = call PAQUETE_BD.METODO(?, ?) }" ); // Registramos los parámetros de salida cs.registerOutParameter( 1, Types.CLOB ); // Asignamos los parámetros de entrada cs.setInt( 2, id); cs.setString( 2, texto); return cs; } }, new CallableStatementCallback<Reader>() { @Override public Reader doInCallableStatement( CallableStatement cs ) throws SQLException { // Ejecutamos la llamada y devolvemos el valor correspondiente cs.execute(); try { return cs.getClob( 1 ).getCharacterStream(); } catch ( final Exception e ) { return null; } } } );
Bloque de código
languagejava
@Query(nativeQuery = true, value = "SELECT paquete.nombre_funcion(?1, ?2, ?3) FROM dual" )
String funcionJava(String param1, int param2, String param3)

En este caso, especificamos con ? y un número (empezando por 1) los parámetros que corresponderán a los parámetros de la función Java, que se mapean en orden de aparición.

Hay funciones que desde la propia base de datos se especifica que no se puedan llamar de esta forma, con SELECT FROM DUAL, y en estos casos deberemos hacer la llamada con JdbcTemplate, que podemos ver en el último apartado de esta sección.

Procedimientos

Para llamar a procedimientos desde un JPA Repository, podemos utilizar la anotación @Procedure, de esta manera:

Bloque de código
languagejava
@Procedure( "paquete.nombre_procedure" )
void funcionJava(@Param("curso") String curso, @Param("alumno") String alumno );

Como vemos, utilizamos @Param para indicar a qué parámetro del procedure corresponde cada parámetro de la función java, y se debe indicar el nombre definido en base de datos. En este caso, no se está devolviendo ningún valor. A falta de realizar más investigación y más pruebas en esta vía, podemos utilizar JdbcTemplate, que nos da más flexibilidad.

JdbcTemplate

Para llamar tanto a funciones como a procedimientos de base de datos, podemos utilizar JdbcTemplate. En primer lugar, declararemos la instancia en nuestra clase para el servicio REST:

Bloque de código
languagejava
import org.springframework.jdbc.core.JdbcTemplate;

@Autowired
private JdbcTemplate jdbcTemplate;

Un ejemplo simple de cómo se puede utilizar podría ser este:

Bloque de código
languagejava
jdbcTemplate.execute( new CallableStatementCreator() {

	@Override
	public CallableStatement createCallableStatement( Connection con ) throws SQLException {
		// Definimos la llamada, dejando con ? lo que serán parámetros, tanto de salida como de entrada
		final CallableStatement cs = con.prepareCall( "{ ? = call PAQUETE_BD.METODO(?, ?) }" );

		// Registramos los parámetros de salida
		cs.registerOutParameter( 1, Types.VARCHAR );

		// Asignamos los parámetros de entrada
		cs.setInt( 2, id );
		cs.setString( 2, texto );

		return cs;
	}
}, new CallableStatementCallback<String>() {

	@Override
	public String doInCallableStatement( CallableStatement cs ) throws SQLException {
		// Ejecutamos la llamada y devolvemos el valor correspondiente
		cs.execute();
		try {
			// Devolvemos el string correspondiente al primer parámetro
			return cs.getString( 1 );
		} catch ( final Exception e ) {
			return null;
		}
	}
} );

Podemos ver cómo se pone la llamada, especificando con ? cada uno de los parámetros, que pueden ser tanto de entrada  como de salida. En este caso, se llama a una función, y vemos cómo se registra el primer parámetro como salida con su tipo correspondiente, y en los dos siguientes se les indica su valor.

En la segunda parte del código, donde se define new CallableStatementCallback debemos poner el tipo de retorno, tanto entre <> como en el tipo de retorno de la función doInCallableStatement. Dentro ejecutamos la llamada y devolvemos el valor correspondiente. 

Hay que notar que todo esto es la instrucción jdbcTemplate.execute, que está devolviendo un String, en este caso. Aunque el código sea largo, esto es una función que, como cualquier otra, podemos asignar el valor de retorno a una variable.

Con esto no sólo podemos ejecutar una función o un procedimiento, podemos indicar bloques del tipo DECLARE ... BEGIN ... END que contengan varias instrucciones, especificándolo como un String en con.prepareCall.

En cuanto a la conexión que se crea, JdbcTemplate se encarga de la obtención y liberación de recursos, como dicha conexión. Por lo tanto, no hace falta cerrarla explícitamente, pues ya lo hace JdbcTemplate.