Spring Data JPA acceso a datos simple y rápido
En el tutorial anterior Hibernate JPA logramos reducir y simplificar el código de acceso a datos, comparando con lo que hicimos en el curso acceso a datos con JDBC obtuvimos, grandes beneficios, aun así, en este momento todavía tenemos gran cantidad de código repetitivo, por ejemplo, para cada entidad JPA debemos escribir las operaciones CRUD (crear, leer, actualizar, borrar), Spring Data genera automáticamente estas y otras operaciones que son de uso común, además nos provee una manera simple para crear consultas y obtener los datos.
Como siempre lo primero que hacemos es agregar las dependencias necesarias, seguimos trabajando con el mismo proyecto que hemos usados en toda la serie de tutoriales de acceso a datos con Spring, abrimos el archivo pom.xml
y agregamos la siguiente dependencia, requerida para usar Spring Data.
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.11.1.RELEASE</version>
</dependency>
Lo siguiente será modificar nuestra configuración, necesitamos activar los repositorios JPA e indicarle a Spring Data donde localizarlos, lo hacemos del siguiente modo:
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories("carmelo.spring.data.repository")
public class SpringConfiguration {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:data/schema.sql")
.addScript("classpath:data/data.sql")
.generateUniqueName(true)
.build();
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean factoryBean
= new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(dataSource());
factoryBean.setJpaVendorAdapter(jpaVendorAdapter());
factoryBean.setPackagesToScan("carmelo.spring.data.model");
factoryBean.setPersistenceUnitName("spring-data-pun");
Map<String, String> props = new HashMap<>();
props.put("hibernate.format_sql", "true");
factoryBean.setJpaPropertyMap(props);
return factoryBean;
}
@Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setGenerateDdl(false);
jpaVendorAdapter.setDatabase(Database.HSQL);
jpaVendorAdapter.setShowSql(true);
return jpaVendorAdapter;
}
@Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return transactionManager;
}
}
Casi todo el código de esta configuración la explicamos en el tutorial anterior, Spring Hibernate JPA, lo único nuevo que vemos es que la clase SpringConfiguration
se encuentra anotada con la anotación @EnableJpaRepositories("carmelo.spring.data.repository")
, esta indica el paquete donde se encuentran los repositorios, el XML equivalente de esta anotación sería el siguiente:
<jpa:repositories base-package="carmelo.spring.data.repository" />
Nótese que requerimos el namespace jpa.
En este tutorial estamos buscando simplificar el código de tenemos que escribir, por lo que modificaremos la clase Product
la cual representa la tabla producto, aquí haremos uso la librería llamada lombok que se encarga de generar el código que siempre requerimos para una entidad JPA, esta librería la vimos en el tutorial reducción de código con lombok, la dependencia es la siguiente:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.12</version>
</dependency>
Usaremos la anotación @Data
para que lombok genere los métodos: getter, setter, equals, hashCode y el constructor, todos ellos requeridos a la hora de usar una entidad JPA.
import javax.persistence.Entity;
import javax.persistence.Id;
import lombok.Data;
@Data
@Entity
public class Product {
@Id
private Long id;
private String name;
private Double price;
}
En Netbeans podemos ver enseguida lo que hemos hecho usando la ventana Navigator.
En los tutoriales previos usamos las anotaciones @Table
y @Column
para indicar el nombre de la tabla y la columna correspondiente, esto no es estrictamente necesario ya que el nombre de la tabla corresponde con el de la clase y el nombre de las columnas es igual a de cada uno de los campos de la clase. por ejemplo: el campo price se corresponde con la columna Price.
Ya estamos listos para hacer el trabajo que realmente nos interesa, el acceso a datos con Spring Data, debemos crear los repositorios, veamos como crear el repositorio para la clase Product
, para ello vamos a crear la siguiente interface:
package carmelo.spring.data.repository;
import carmelo.spring.data.model.Product;
import org.springframework.data.repository.CrudRepository;
public interface ProductRepository extends CrudRepository<Product, Long> {
}
Lo que hicimos fue crear una interface de extiende CrudRepository<T, ID>
donde T debe ser la clase de la cual crearemos el repositorio e ID el tipo usado como identificador o clave primaria en la base de datos, en nuestro caso Product
y Long
respectivamente.
Con esto hemos hecho magia, Spring Data creará las operaciones CRUD para la entidad Product
, esto quiere decir que ya podemos: guardar, eliminar, leer, etc., un producto de la BD.
Estas son algunas de las operaciones disponibles en la interface CrudRepository
, para la entidad Product
las operaciones serian las siguientes:
- Guardar un producto.
- Buscar un producto por ID.
- Listado de todos los productos.
- Contar los productos.
- Eliminar un producto de la base de datos.
- Determinar si existe un producto con el ID indicado.
Probando lo que tenemos:
AbstractApplicationContext ctx
= new AnnotationConfigApplicationContext(SpringConfiguration.class);
ProductRepository pr = ctx.getBean(ProductRepository.class);
// crear un nuevo producto
Product p0 = new Product();
p0.setId(100L);
p0.setName("Porotos");
p0.setPrice(2.50);
// agregar un nuevo producto
pr.save(p0);
// listar todos los productos
pr.findAll().forEach(System.out::println);
ctx.close();
Habrás notado que hemos consultado la base de datos y no hemos escrito una sola línea SQL o JPQL, entonces, que pasa si deseamos crear nuestras propias consultas, pues Spring Data nos provee de un mecanismo que le permite generar una consulta a partir de la definición de un método, veamos un ejemplo, busquemos todos los productos con un precio mayor al indicado, como se hace:
public interface ProductRepository extends CrudRepository<Product, Long> {
List<Product> findByPriceGreaterThan(Double price);
}
Si probamos este método, de la siguiente manera:
ProductRepository pr = ctx.getBean(ProductRepository.class);
// productos con precio mayor a B/ 100.00
System.out.println("--- PRODUCTOS CON SOBREPRECIO ---");
pr.findByPriceGreaterThan(100.0).forEach(System.out::println);
El resultado será el siguiente:
El listado de todos los productos con un precio mayor a B/. 100.00, hicimos magia nuevamente, expliquemos el truco.
Spring Data es capaz de deducir la consulta a partir del método correspondiente, en nuestro ejemplo el método List<Product> findByPriceGreaterThan(Double price)
donde el nombre de el método findByPriceGreaterThan indica que deseamos buscar por precio, findByPrice y que la condición de búsqueda es que el precio sea mayor al indicado, GreaterThan, la consulta generada será parecida a esta:
select p from Product p where p.price > ?1
El como se generan las consultas es un tema muy amplio por lo que le dedicaremos un tutorial a esta tarea, en el futuro.
Otra cosa que es importante conocer es que si la consulta es muy compleja pudiese ser difícil definirla a partir de un método, por lo que Spring Data nos da la opción de escribir consultar en el modo tradicional usando el lenguaje JPQL y la anotación @Query
, veamos como se usa:
public interface ProductRepository extends CrudRepository<Product, Long> {
@Query("select p from Product p where p.price > ?1")
List<Product> findByPriceGreaterThan(Double price);
}
Usando @Query
se ejecutara la consulta establecida, por lo que el nombre del método ya no es relevante, obsérvese que los parámetros de la consulta deben coincidir con los del método, que igual manera el valor devuelto también debe ser del tipo adecuado.
public interface ProductRepository extends CrudRepository<Product, Long> {
@Query("select p from Product p where p.price > :precio")
List<Product> findByPriceGreaterThan(@Param("precio") Double price);
}
El mismo ejemplo solo que esta vez estamos usando parámetros con nombre, como vemos, para indicar un parámetro iniciamos con “:” seguido del nombre del parámetro, en el método usamos la anotación @Param
para indicar el nombre del parámetro al que se le aplica la variable.
- Ver código: Spring Data JPA
- Ver más tutoriales: Serie Spring Framework
Comentarios
Publicar un comentario