Mostrando entradas con la etiqueta SQL. Mostrar todas las entradas
Mostrando entradas con la etiqueta SQL. Mostrar todas las entradas

viernes, 11 de septiembre de 2020

Apache Camel: CRUD con JPA

 Ya hemos visto anteriormente otros posts de Apache Camel. Hoy veremos como hacer las operaciones de BBDD con JPA.

Como siempre empezaremos por la configuración. En cuanto a librerías añadimos las siguientes:

  • El componente camel-jpa-starter de Apache Camel que nos permitirá usar el componente JPA. 
  • La librería hibernate-entitymanager que nos permitirá utilizar las anotaciones de hibernate.
Debemos recordar un poco cual es el fundamento de JPA y es la cualidad de poder manejar datos relacionales como objetos Java. Permitiéndonos manejar más fácilmente dentro de clases Java la información de una BBDD. Estas clases serán las denominadas Entidades. Y como parte de la API de JPA se encuentra JPQL, un lenguaje que nos permitirá realizar consultas sobre esas entidades. 

Gracias a Spring boot y los starter no tendremos que realizar mucha configuración, pero a nivel de la clase Java deberemos añadir una cuantas anotaciones:

@Entity(name = "BOOK")
@Table(name = "BOOK")
@NamedQuery(name = "findAll", query = "SELECT b FROM BOOK b")
public class Book {

    @JsonProperty
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @JsonProperty
    private String name;
    @JsonProperty
    private String author;
}

@Entity nos permitirá indicar que es una clase a ser gestionada a través de JPA. Con @Id indicaremos que cual es su primary key y con @GeneratedValue se gestionará su generación, en el caso de MySQL será Auto. 

En el primer ejemplo que veremos será muy similar al del componente SQL solo que usaremos JPQL y debemos indicar cual es la entidad sobre la que vamos a realizar la consulta. 

rest().get("book/{id}").description("Details of an book by id")
   .outType(Book.class).produces(MediaType.APPLICATION_JSON_VALUE).route()
   .toD("jpa://" + Book.class.getName() + "?query=select b from " + Book.class.getName()+ " b where b.id= ${header.id}", 5)
   .log("--- select a book ${body} ---");

Lo primero que tenemos que hacer es indicar cual es la entidad sobre la cual vamos a trabajar. Luego si queremos hacer una consulta JPQL debemos añadir el parámetro 'query'. 

Además en este caso concreto queremos obtener un libro en base a un identificador que viene como parámetro de la request, a través del lenguaje de expresión. Para poder conseguir esto utilizaremos el método ToD en vez de To. El cual nos permitirá enrutar de forma dinámica el flujo y que sea interpretado el lenguaje de expresión.

Otra de las cualidades de JPA son las namedQuery, las cuales nos permiten definir consultas JPQL a nivel de la clase. En el siguiente ejemplo veremos como utilizar las namedQueries a través del componente JPA. La query a utilizar es findAll.

rest().get("book").produces(MediaType.APPLICATION_JSON_VALUE).route()
      .to("jpa://"+ Book.class.getName() + "?resultClass="+ Book.class.getName() + "&namedQuery=findAll")
      .log("---select all books---");

En este segundo ejemplo podemos ver la diferencia de usar el método outType para indicar el tipo de objeto a la salida. O si por lo contrario podemos indicarlo a través del query parameter resultClass del propio componente JPA. 

También podemos hacer operaciones que nos permitan guardar o persistir dichos objetos en la BBDD. Como podemos ver en el ejemplo, en el caso de almacenar el valor, el funcionamiento es muy sencillo. Solo debemos indicar el objeto a persistir y utilizar el query parameter usePersist para indicar que debe almacenar el objeto. Por supuesto, en el flujo deben ir los datos del objeto a guardar. 

rest().post("book").produces(MediaType.APPLICATION_JSON_VALUE).type(Book.class)
      .route().routeId("postBookRoute").log("--- binded ${body} ---")
      .to("jpa:" + Book.class.getName() + "?usePersist=true")
      .setHeader(Exchange.HTTP_RESPONSE_CODE, constant(201)).setBody(constant(null));

Para el caso de las actualizaciones y borrado de datos, necesitamos utilizar el query parameter useExecuteUpdate. En este ejemplo haremos uso del método process para crear el objeto a actualizar a partir de los datos de la cabecera y el cuerpo del mensaje. 

rest().put("book/{id}").produces(MediaType.APPLICATION_JSON_VALUE).type(Book.class).route().choice()
      .when().simple("${header.id} < 1").bean(BookRouter.class, "negativeId").otherwise()
      .process(new Processor() {
          @Override
          public void process(final Exchange exchange) {
              Book book = exchange.getIn().getBody(Book.class);
              Integer id = Integer.valueOf(exchange.getIn().getHeader("id").toString());
              book.setId(id);
              exchange.getIn().setBody(book, Book.class);
          }
      }).to("jpa:" + Book.class.getName() + "?useExecuteUpdate=true")
      .setHeader(Exchange.HTTP_RESPONSE_CODE, constant(200)).setBody(constant(null));

Por último veremos el ejemplo asociado al borrado de datos. Y de paso veremos como realizar consultas nativas. Es decir utilizaremos lenguaje SQL común y no JPQL. 

rest().delete("book/{id}").produces(MediaType.APPLICATION_JSON_VALUE).route()
      .toD("jpa:com.example.home.ApacheCamelRestExample.pojo.Book"
            + "?nativeQuery=DELETE FROM library.BOOK where id = ${header.id}&useExecuteUpdate=true")
      .setHeader(Exchange.CONTENT_TYPE, constant(MediaType.APPLICATION_JSON_VALUE))
      .setHeader(Exchange.HTTP_RESPONSE_CODE, constant(200)).setBody(constant(null));

Como veis algunas consultas pueden ser algo más difíciles que usar simple SQL. Pero también otras se simplifican muchísimo y siempre estamos trabajando con objetos. Lo cual, al programar en un lenguaje orientado a objetos es muy de agradecer. 

viernes, 7 de febrero de 2020

WSO2 EI: Como crear un servicio con acceso a BBDD

Hoy vamos a ver algo muy facil de hacer y muy util. Vamos ver como crear un data service, asociarle un data source y crear una operación asociada. 

Los datasources son los elementos de configuración que nos permiten conectarnos con diferentes origenes de datos. Desde base de datos tradicionales, a hojas de excel o google sheet, CSV o base de datos NoSQL como Cassandra o MongoDB. 

Otra cosa que debemos saber de estos Datasources, es que podemos configurarlos de distintas formas:
  • Desde el fichero custom-datasources.xml
  • Desde la consola de administración. Accediendo a la pestaña Configure y la  opción de menú Datasources.
  • Desde el propio Data service.  
Los data services serán aquellos servicios que teniendo como origen esos datasources, expondrán mediante operaciones o recursos los datos del mismo. Para exponer esos datos deberremos realizar los siguientes pasos:
  • Crear el data service e indicar los protocolos de acceso al mismo. 
  • Asociar al menos un datasource. 
  • Crear un consulta SQL.
  • Crear una operación o recurso asociado a la consulta. 
 Al crear una consulta debemos realizar tres pasos fundamentales:

  • Escribir la propia consulta SQL. 
  • Indicar los parámetros de entrada
  • Indicar los parámetros de salida y su formato. 
Tanto la creación de parámetros de entrada y salida se pueden crear de forma automática con los botones Generate Input Mappings y Generate Response. A ambos se les puede indicar el tipo de elemento (por defecto, String). En los parámetros de entrada se pueden incluir validaciones y respecto a la salida, por defecto será en formato XML y podemos indicarle el nombre de los objetos que envuelvan esos registros. 

El siguiente paso es la creación de la operación o recurso. En ambos casos es relativamente sencillo, solo debemos indicar la consulta que estará asociada a estos y de forma automática se creará el propio mapeo del propio recurso u operación. La diferencia principal, es que una operación habilitará un servicio de tipo SOAP para realizar la consulta asociada. Y si creamos un recurso, el servicio será de tipo REST. 


Si queremos probarlo, en el listado de servicios hay un botón denominado Try this service que abrirá una nueva ventana para realizar las pruebas. En el caso de las operaciones para poder invocarlo realizaremos la siguiente llamada:

curl --insecure \
--request POST 'https://localhost:8243/services/sampleDataservice.SecureSOAP12Endpoint' \
--header 'Content-Type: application/xml' \
--data-raw '<p:SELECT_OP xmlns:p="http://ws.wso2.org/dataservice">
      <xs:ID xmlns:xs="http://ws.wso2.org/dataservice">1</xs:ID>
   </p:SELECT_OP>'

La ruta del servicio estará compuesta por el nombre del servidor, el puerto (8280 para HTTP y 8243 para HTTPS, por defecto). De contexto /services/ y el nombre del data service, y por último el sufijo en función del tipo de endpoint SOAP que vayamos a usar.

El cuerpo del mensaje tendra como raiz el elemento con nombre de la operación (SELECT_OP en este caso) y compuesto por los distintos atributos de la consulta.
asdfad

miércoles, 15 de agosto de 2018

Flyway: Como migrar la base de datos

Hoy vamos a hablar de Flyway, un software que nos facilitará la migración de bases de datos. Sobre todo es util para la migración de los esquemas a lo largo de los distintos entornos de prueba, preproducción y producción. Ya que es facil que en el pase de un desarrollo de un entorno a otro podamos fallar y/o olvidar ejecutar algún script necesario.
  • Funcionamiento
Su funcionamiento se basa en la creación de un tabla de gestión que almacenará la información de los distintos script de migración que contenga la aplicación. En función de estos scripts, su orden de ejecución y la tabla de gestión, tendremos la hoja de ruta para poder pasar una base de datos de un entorno a otro. 
  • Ejecución
Flyway, al comenzar buscará la tabla de ejecución, denominada 'flyway_schema_history, y en caso de no encontrala la creará. Posteriormente, buscará los scripts en formato .sql o .java en una ruta configurable y comprobará si se han ejecutado ya o no, en caso negativo los ejecutará y almacenará la información asociada a dichos scripts en la tabla de gestión.

Dentro de esta información asociada se encuentra el nombre del fichero, la versión o el checksum del mismo. Al contener el checksum, en caso de que modifiquemos el fichero, flyway se daría cuenta de que la versión actual es diferente a la ejecutada y lanzaría una excepción. Por tanto si queremos modificar un script ya ejecutado, es obligatorio crear un nuevo script ya que no es modificable el anterior.

A tener en cuenta respecto a lo indicado anteriormente, que ejecutará el script siempre y cuando no se haya ejecutado anteriormente tal como hemos dicho y la versión del mismo sea posterior a la última versión registrada en la tabla de gestión.  
  • Configuración
Vamos a ver un ejemplo con gradle donde podemos ver como funciona. Lo configuramos indicando que vamos a usar el plugin de flyway y creando las variables de configuración necesarias.

plugins {
    id "org.flywaydb.flyway" version "5.1.4"
}
flyway {
    url = 'jdbc:mysql://localhost:3306/'
    user = 'root'
    password = 'root'
    schemas = ['flywayExample']
}

Lo siguiente será crear los ficheros tipo 'v${number}__${script_name}.sql' dentro de la carpeta 'src/main/resources/db/migration', que son el formato y ubicación por defecto.

Por último ejecutarlo a través del comando './gradlew flywayMigrate'. Y ya podremos acceder a la base de datos y ver como se han ejecutado los scripts. Aquí teneis un ejemplo que podeis usar para comprobar su funcionamente, como base de datos usa un docker de MySQL.

domingo, 24 de septiembre de 2017

Como realizar consultas jerarquicas en Oracle


He encontrado por ahí un viejo cuaderno donde me apuntaba cosillas del curro. Echandole un vistazo, tristemente esta todo desfasado y ya es de poca utilidad. Pero he encontrado una cosilla que no esta del todo mas. Y es como hacer consultas jerarquicas o recursivas en Oracle.

Vamos a partir de un esquema muy básico para que podamos explicarlo. Y para poder obtener los datos de forma jerarquica necesitaremos de dos comandos principales: 'start with' y 'connect by'. Si solo queremos hacerlas jerarquicas usaremos 'connect by prior'. Ejemplo:


select * from jerarquia start with ID=1 connect by prior ID = PARENT_ID;

   ID   |   PARENT_ID   |
   01   |
   03   |      01
   05   |      01

Además podemos añadirle clausulas al 'connect by' para filtrar aún más la búsqueda jerarquica:

select * from jerarquia start with ID=1 connect by ID != 3 AND prior ID = PARENT_ID;

   ID   |   PARENT_ID   |
   01   |
   05   |      01

También podemos mostrar el nivel de profundidad en el que se encuentran los registros:

select ID, PARENT_ID, LEVEL from jerarquia start with ID=1 connect by prior ID = PARENT_ID;

   ID   |   PARENT_ID   | LEVEL
   01   |               |   1
   03   |      01       |   2
   05   |      01       |   2

También podemos hacer busquedas inversas, simplemente cambiando el orden de la clausula:

select ID, PARENT_ID, LEVEL from jerarquia start with ID=5 connect by ID prior PARENT_ID = ID;

   ID   |   PARENT_ID   | LEVEL
   05   |      01       |   1
   01   |               |   2

También podemos hacer que se muestre el camino de la jerarquía

SELECT ID, PARENT_ID, LEVEL, SYS_CONNECT_BY_PATH(ID,'->') ARBOL from jerarquia start with ID=1 connect by ID prior ID = PARENT_ID;

   ID  |  PARENT_ID   | LEVEL | ARBOL
   01  |              |   1   |  ->1
   03  |     01       |   2   |  ->1->3
   05  |     01       |   2   |  ->1->5

También permite ordenar los registros en base a un tercer campo. El ejemplo sería algo así:

select * from jerarquia start with ID=1 connect by prior ID = PARENT_ID ORDER SIBLINGS BY TERCER_CAMPO;

Podeis ver más información en la página oficial.

domingo, 27 de agosto de 2017

Web Service con Spring Boot, JPA y H2

Bueno, vamos a dar un paso más en el ejemplo de los web services con spring boot. Ya los hicimos básico, ahora lo haremos obteniendo los datos de BBDD. Y además no de una BBDD relacional corriente, sino H2 que será almacenada en memoria.

Empezamos con la configuración básica y es que debemos añadir las librerias necesarias para el uso de JPA y H2.

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
 <groupId>com.h2database</groupId>
 <artifactId>h2</artifactId>
</dependency>

Para este ejemplo añadiremos una pequeña configuración extra, la parte de la configuración a BBDD la realizaremos directamente sobre el fichero 'application.properties'. Sin necesidad de crear un fichero 'persistence.xml'. La parte más importante es la correspondiente a la propiedad 'javax.persistence.sql-load-script-source', que nos permite indicar un fichero SQL que ejecutar al levantar la BBDD.


## DATASOURCE PROPERTIES

spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=sa
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.platform=org.hibernate.dialect.H2Dialect

## JPA PROPERTIES

spring.jpa.show-sql=true
#spring.jpa.database=MYSQL
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.properties.hibernate.cache.use_second_level_cache=true
spring.jpa.properties.javax.persistence.sharedCache.mode=DISABLE_SELECTIVE
spring.jpa.properties.javax.persistence.schema-generation.database.action=drop-and-create
spring.jpa.properties.javax.persistence.sql-load-script-source=script/data.sql

Ahora crearemos las clases JPA que serán el reflejo de las entidades de BBDD. Para nuestro ejemplo crearemos una clase 'Book' que contendrá información básica de libros y una clase 'GenericEntity' que nos servirá para aglutinar aquellos atributos que serán iguales en todas las futuras entidades, como son el campo clave y los campos de auditoría. Todo ello gracias a la anotación 'MappedSuperclass'. Sobre como hacer la auditoría ya hicimos un post anteriormente, aquí.

El siguiente paso es crear el DAO o Repository que nos permita acceder a los datos y/o modificarlos. Para ello crearemos una clase 'BookDao' y 'GenericDao'. De esta forma tendremos clases de acceso a datos con un conjunto de métodos por defecto. La anotación '@NoRepositoryBean' indicará a Spring que no cree un proxy de acceso a datos para la interfaz, cosa que si hará con 'BookDao'.

@NoRepositoryBean
public interface GenericDao<E extends GenericEntity<K>, K extends Serializable>
  extends JpaRepository<E, K> { }
....
public interface BookDao extends GenericDao<Book, Integer> { }

Por último nos queda crear el controlador


@RestController("/book")
public class BookRestController {
 @Autowired
 private BookDao repository;
 @GetMapping
 public List<Book> findAll() {
  return repository.findAll();
 }
 @GetMapping("/{id}")
 public Book findById(@PathVariable("id") final Integer id) {
  return repository.findOne(id);
 }
}

Si arrancamos la aplicación podremos acceder a los datos por ejemplo con la siguiente consulta: http://localhost:8082/book/3


sábado, 29 de abril de 2017

Mejorar el rendimiento en una búsqueda de texto con SQL

Normalmente cuando queremos comparar texto en una sentencia SQL usamos el LIKE y los porcentajes para comparar parte del texto del campo. Además cuando usamos caracteres latinos, solemos hacer una pequeña operación de traducción de texto. Ya indicamos en su día que lo correcto sería crear una función para entre otras cosas hacer demasiado engorroso la consulta, aquí el post. Un ejemplo de este tipo de consulta sería:

SELECT c.ID AS ID,c.NAME AS NAME
FROM CARS c
JOIN MODELS m ON c.ID = m.CAR
WHERE (
REPLACE(UPPER(TRIM(c.DESCRIPTION)),'ÁÀÂÄÉÈÊËÍÌÎÏÓÒÔÖÚÙÛÜÑÇ', 'AAAAEEEEIIIIOOOOUUUUNC') LIKE REPLACE(UPPER(TRIM('%CACTUS%')),'ÁÀÂÄÉÈÊËÍÌÎÏÓÒÔÖÚÙÛÜÑÇ','AAAAEEEEIIIIOOOOUUUUNC')
OR REPLACE(UPPER(TRIM(m.DESCRIPTION)),'ÁÀÂÄÉÈÊËÍÌÎÏÓÒÔÖÚÙÛÜÑÇ', 'AAAAEEEEIIIIOOOOUUUUNC') LIKE REPLACE(UPPER(TRIM('%CACTUS%')),'ÁÀÂÄÉÈÊËÍÌÎÏÓÒÔÖÚÙÛÜÑÇ','AAAAEEEEIIIIOOOOUUUUNC')
)

Suponiendo que el campo 'DESCRIPTION' en ambas tablas sea campos destinados a guardar mucha información, las búsquedas se pueden hacer muy pesadas. Sobretodo si se estas búsquedas tienen mucha repeticiones. Para poder realizar búsquedas de textos en campos amplios de SQL necesitamos del uso combinado de los indices 'FULLTEXT' y la función 'MATCH/AGAINST'.

La función 'MATCH/AGAINST' te permite realizar las búsquedas de texto de forma más optimizada pero solo se puede usar con campos que tienen un indice FULLTEXT. Estos indices se pueden asociar a cualquier campo SQL que almacene caracteres, pero lo normal es que se use para campos tipo TEXT que son los que tienen la dificultad de las búsquedas. Por lo que el primer paso sería modificar las tablas para crear dichos indices.


ALTER TABLE 'CARS'
ADD FULLTEXT INDEX 'IDX_CARS_TEXT' ('DESCRIPTION');

ALTER TABLE 'MODELS'
ADD FULLTEXT INDEX 'IDX_MODELS_TEXT' ('DESCRIPTION');

Una vez creados ya podremos usar la funcion 'MATCH/AGAINST'. Esta función al menos existen en MYSQL y SQL Server. Y en ejemplo veremos la versión de MYSQL, donde en 'AGAINST' ademas indicamos el módo de búsqueda del texto que deseamos buscar. 'IN NATURAL LANGUAGE MODE' permite buscar la cadena de texto dentro del texto completo que contiene dicho campo. 


SELECT DISTINCT c.ID AS ID, c.NAME as NAME
FROM CARS c
JOIN MODELS m ON c.ID = m.CAR
WHERE (
MATCH(c.DESCRIPTION) AGAINST ( 'CACTUS' IN NATURAL LANGUAGE MODE)
OR MATCH(m.DESCRIPTION) AGAINST ( 'CACTUS' IN NATURAL LANGUAGE MODE)
)


Como veis, la búsqueda se ha vuelto mucho más básica, más rápida y además no se ve afectada por los caracteres latinos o mayúsculas. 

sábado, 14 de enero de 2017

Ejecutar procedimiento con JPA y anotaciones


En este post daremos dos formas de realizar llamadas a procedimientos haciendo uso de JPA. La primera de ellas, es algo menos ortodoxa y nos servirá para hacer llamadas nativas de cualquier tipo, no solo procedimientos. 

La primera forma es utilizando la anotacion '@NamedNativeQuery', que se encuentra disponible en JPA desde su primera especificación. Esta anotación te permite definir llamadas nativas sin mucha configuración, unicamente poniendole un nombre y cual va a ser la sentencia SQL Nativa. Aquí un ejemplo:

@NamedNativeQueries( { @NamedNativeQuery( name = "countColourCars", query = "CALL ASSES_PRODUCT(:color)" ) } )
@Entity
@Table( name = "CAR" )
public class Car{

Para poder realizar la llamada, podemos hacerlo a través de la implementación de un DAO. Un ejemplo:

@Repository
public class CarDaoImpl{
	@PersistenceContext
	private EntityManager em;
	public int countColourCars( final String colour) {
		Query query = em.createNamedQuery( "countColourCars" ).setParameter( "colour", colour);
		return ((BigInteger) query.getSingleResult()).intValue();
	}

Otra forma de hacerlo sería a través de la anotación '@NamedStoredProcedureQuery', la cual esta disponible a partir de la especificación JPA 2.1. Esta anotación tiene un funcionamiento similar a la anterior pero sirve solamente para procedimientos almacenados. Además tiene la particularidad de que a menos que se utilice Java 8 no puede ser repetida. Además en este ejemplo haremos uso de la antoación '@SqlResultSetMapping' que permita configurar cual va a ser la salida del procedimiento.

@StoredProcedureParameter( mode = ParameterMode.IN, type = String.class, name = "findNewsByName" ),
		@StoredProcedureParameter( mode = ParameterMode.IN, type = String.class, name = "orderField" ) }, resultSetMappings = "findNewsMapping" )
@SqlResultSetMapping( name = "findNewsMapping", classes = { @ConstructorResult( targetClass = New.class, columns = { @ColumnResult( name = "key", type = Integer.class ), @ColumnResult( name = "name", type = String.class ) } ) } )
@Entity
@Table( name = "NEW" )
public class New{

La forma de obtener los datos es practicamente igual.

@Repository
public class NewDaoImpl {
	@PersistenceContext
	private EntityManager em;

	@SuppressWarnings( "unchecked" )
	@Override
	public List<New> findNewsByName( final String newsName, final String orderField ) {
		StoredProcedureQuery storedProcedureQuery = em.createNamedStoredProcedureQuery( "findNewsByName" );
		storedProcedureQuery.setParameter( "newsName", newsName );
		storedProcedureQuery.setParameter( "orderField", orderField );
		storedProcedureQuery.execute();
		return storedProcedureQuery.getResultList();
	}

El funcionamiento es similar para ambas anotaciones, incluso también son posibles mapeos automaticos con '@NamedNativeQuery'.

domingo, 24 de julio de 2016

Que son y como funcionan los Cursores SQL

Este va a ser un sencillo post sobre que son los cursores SQL y su funcionamiento. 

Para empezar comentaremos que hay dos tipos de cursores: Implícitos y explícitos.

Los implícitos tienen las siguientes características:
  • No necesitan una declaración específica.
  • Son aquellos cursores que permiten almacenar una única variable.
  • Es necesaria la clausula INTO para almacenar el valor deseado.
  • La variable debe ser del mismo tipo que el valor a retener.
  • Comúnmente utilizados en los PL/SQL para almacenar valores a utilizar posteriormente.

declare
  vNombre VARCHAR2(50);
begin
  SELECT NOMBRE INTO vNombre from FICHEROS WHERE ID_FICHERO = 123456;
  dbms_output.put_line('El nombre es: ' || vNombre);
end;

Los explícitos son algo más complejos. Las principales características son:
  • Necesitan una declaración específica antes de poder utilizarlos. 
  • Permiten el almacenamiento de más de una variable en una estructura reutilizable. 
  • Permiten parámetros de entrada para filtrar los resultados devueltos.
A continuación mostraremos un ejemplo de como declararlos y utilizarlos:


procedure createArticuloAlbaran (idAlbaran in VARCHAR2, idArticulo in VARCHAR2, resultado out RESULTADO_TYPE)
 is
 CURSOR CUR_PROPIO(idArt VARCHAR2) IS
  SELECT A.PRECIO AS PRECIO, A.CANTIDAD AS CANTIDAD
        FROM ARTICULOS A
        WHERE A.IDARTICULO = idArt; 

        BEGIN
  FOR REGISTRO_CUR IN CUR_PROPIO(idArticulo) LOOP
   INSERT INTO ALBARANES(IDALBARAN, IDARTICULO, PRECIO, CANTIDAD)
   VALUES(idAlbaran, idArticulo, REGISTRO_CUR.PRECIO, REGISTRO_CUR.CANTIDAD );
  END LOOP;
  COMMIT;
 end createArticuloAlbaran;

domingo, 3 de abril de 2016

Ordenación SQL por un campo y un valor determinado

Aquí os traigo hoy un sencillo truco para poder ordenar mediante SQL una lista de datos en función de un campo y un valor determinado. Con esto conseguimos que aparezca un determinado valor el primero y luego los restantes, según la ordenación que queramos.

La forma es sencilla, en la clausula 'order by' indicamos cual es el valor que queremos mostrar primero y le damos el valor 1, al resto el valor 2 y como ordenación secundaría indicamos el campo por el cual queremos ordenar el resto.

SELECT ID, DESCRIPCION 
FROM TABLA_DATOS 
ORDER BY CASE WHEN DESCRIPCION = 'XXXX' THEN 1, ELSE 2 END, DESCRIPCION ASC

sábado, 2 de abril de 2016

Llamar a una función con parámetro de salida y JPA

Ya hicimos el otro día un ejemplo de como obtener el valor de una función SQL con Java y a través de JDBC. La idea es hacerlo hoy pero con un método algo más avanzado, con JPA.

Si googleas un poco puedes ver como hay un par de formas para realizar la llamada para un procedimiento, con @NamedNativeQuery o @NamedStoredProcedureQuery. Pero ambos métodos no funcionan correctamente para llamar a una función que devuelva un valor concreto.

Al final, lo más fácil es lo más sencillo y es realizar una llamada nativa realizando directamente la query:

Query query = em.createNativeQuery("select hello(:param) from dual");
query.setParameter("param", name);
return (String) query.getSingleResult();





jueves, 17 de diciembre de 2015

Búsquedas en SQL sin mayúsculas o acentos

Es muy normal que se pida realizar búsquedas contra la BBDD que no tengan en cuenta mayúsculas/minúsculas y por supuesto los acentos. Para ello podemos hacer uso de funciones SQL que nos permitan de forma sencilla realizar esto mismo. Si va a ser una operación común en todas las búsquedas, puede ser util crear una función SQL para evitar duplicidad y mejorar el mantenimiento.
CREATE OR REPLACE FUNCTION QUITAR_ACENTOS (entrada    VARCHAR2) 
RETURN VARCHAR2 IS salida    VARCHAR2(4000);
BEGIN
    salida := translate(upper(entrada),'ÁÀÂÄÉÈÊËÍÌÎÏÓÒÔÖÚÙÛÜÑÇ', 'AAAAEEEEIIIIOOOOUUUUNC');
    RETURN(salida);
END QUITAR_ACENTOS;

Ejemplo de uso:
SELECT * FROM TABLA T WHERE QUITAR_ACENTOS(T.CAMPO) LIKE '%'||QUITAR_ACENTOS(cadena_busqueda)||'%'

Versión 2.0

En esta versión utilizamos también la función regexp_replace. Lo cual nos permite indicar una expresión regular, en este caso para indicarle que sustituya los caracteres extraños por el caracter vacío y omitirlos en la búsqueda.
...
    salida := regexp_replace(translate(upper(entrada),'ÁÀÂÄÉÈÊËÍÌÎÏÓÒÔÖÚÙÛÜÑÇ', 'AAAAEEEEIIIIOOOOUUUUNC'), '[^A-Za-z0-9]', '');
...

domingo, 22 de febrero de 2015

Dar permisos y crear sinonimos en BBDD

Esto es muy básico pero aquí esta un breve resumen de como hacerlo y no liarnos a la hora de donde hacer el Grant o donde crear el esquema.

Tenemos un esquema A con la tabla X y un esquema B al que le queremos dar permisos sobre

la tabla A.X.

Para ello debemos realizar lo siguiente:

- En A realizamos el grant con los permisos que queramos darle al esquema B

GRANT SELECT ON A.X TO B;

- En B creamos un sinónimo para la tabla que nos han dado permiso

CREATE SYNONYM B_PRIMA FOR A.X;

De esta forma podremos acceder a la tabla X desde B sin ningún problema y ejecutando un select normal y corriente.

lunes, 15 de abril de 2013

SQLException: Field 'xxxx' doesn't have a default value

Haciendo uso de JPA e intentando actualizar objeto con una relacion OneToMany tal y como comentaba en el post anterior, me he encontrado con el error que reza en el titulo de este.

Y es que parece que aunque todo esta bien configurado no funciona, y me indica que el campo a añadir requiere de algún valor que no sea nulo.

Expliquemos el modelo de la base de datos para dar a entender el problema. Tenemos una tabla Padre y una tabla Hijo, en la cual hay una Foreign Key a la tabla Padre. Esa FK esta indicada como Not Null, como es normal para evitar que se introduzca ningún registro sin referencia a la tabla Padre.

El asunto es que ahí se encuentra el fallo, y hay que modificar el modelo de base de datos y no indicar la FK como Not Null permitiendo incluir un registro sin referencia a la tabla Padre y que posteriormente se encargue JPA de actualizar dicho valor en otra sentencia asociada a la transacción