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'.

2 comentarios:

  1. Muchas gracias Daniel por la información, hace un par de semanas que estaba buscando como llamar a un procedimiento.
    Disculpa mi ignorancia, pero estoy empezando a programar con Java, y lo que no me queda claro es, en que clase @Entity lo declaras @NamedStoredProcedureQuery?
    Nuevamente te agradecería bastante la ayuda!

    ResponderEliminar
  2. La anotacion '@@NamedStoredProcedureQuery' la puedes poner en cualquier '@Entity'. Aunque lo normal es que lo pongas en aquella clase que este mas asociada al procedimiento. En este caso el procedimiento lo que realiza es una búsqueda de 'noticias' en base al nombre. Por tanto lo añadimos en la clase JPA/@Entity 'New' y hacemos la llamada al procedimiento a través de su clase Dao, 'NewDaoImpl'.
    Espero que te sirva ;-)

    ResponderEliminar