Spring Data JPA

O Spring Data JPA faz parte da família Spring Data. O Spring Data facilita a criação de aplicativos Spring que utilizam novas formas de acesso a dados, como bancos de dados não relacionais, frameworks de map-reduce, serviços em nuvem, além de oferecer amplo suporte a bancos de dados relacionais avançados. Este artigo irá discutir sobre o Spring Data JPA. Também iremos analisar um exemplo de aplicativo com o Spring Data JPA.

Spring Data JPA

Algumas das características interessantes fornecidas pelo Spring Data JPA são:

  1. Criação e suporte a repositórios criados com Spring e JPA
  2. Suporte a consultas QueryDSL e JPA
  3. Auditoria de classes de domínio
  4. Suporte a carregamento em lote, ordenação, consultas dinâmicas
  5. Suporta mapeamento XML para entidades
  6. Reduz o tamanho do código para operações CRUD genéricas usando CrudRepository

Quando usar o Spring Data JPA?

I would say that if you need to quickly create a JPA-based repository layer that is mainly for CRUD operations, and you do not want to create abstract DAO, implementing interfaces, Spring Data JPA is a good choice.

Exemplo de Spring Data JPA

Para o nosso exemplo do Spring Data JPA, iremos criar um serviço web RESTful que se conectará ao banco de dados Postgresql. Implementaremos operações básicas de CRUD e trabalharemos em dados de amostra que já criamos.

Exemplo de Dados de Amostra do Spring JAP

Use a consulta abaixo para criar uma tabela no banco de dados Postgresql e adicionar alguns dados de teste.

create table people (
id serial not null primary key,
first_name varchar(20) not null,
last_name varchar(20) not null,
age integer not null
);

insert into people (id, first_name, last_name, age) values
(1, 'Vlad', 'Boyarskiy', 21),
(2,'Oksi', ' Bahatskaya', 30),
(3,'Vadim', ' Vadimich', 32);

Estrutura do Projeto Maven do Spring Data JPA

A imagem abaixo mostra a estrutura final do projeto Spring JPA. Analisaremos cada um dos componentes detalhadamente mais tarde.

Dependências do Maven do Spring Data JPA

Precisamos adicionar as seguintes dependências para o nosso projeto de exemplo do Spring Data JPA.

  1. postgresql: Driver Java do Postgresql.
  2. spring-core, spring-context: Spring Framework Core dependencies.
  3. spring-webmvc, jackson-databind: Para aplicação Spring REST.
  4. spring-data-jpa, hibernate-entitymanager: para o suporte ao Spring Data JPA e Hibernate.

Segue o conteúdo do arquivo final de construção do pom.xml.

<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.journaldev</groupId>
	<artifactId>springData</artifactId>
	<packaging>war</packaging>
	<version>1.0-SNAPSHOT</version>
	<name>Spring Data JPA Maven Webapp</name>
	<url>https://maven.apache.org</url>
	<properties>
		<spring.framework>4.3.0.RELEASE</spring.framework>
		<postgres.version>42.1.4</postgres.version>
		<serializer.version>2.8.1</serializer.version>
		<spring.data>1.3.4.RELEASE</spring.data>
		<hibernate.manager>4.2.5.Final</hibernate.manager>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring.framework}</version>
		</dependency>
		<dependency>
			<groupId>org.postgresql</groupId>
			<artifactId>postgresql</artifactId>
			<version>${postgres.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${spring.framework}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring.framework}</version>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-jpa</artifactId>
			<version>${spring.data}</version>
		</dependency>

		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-entitymanager</artifactId>
			<version>${hibernate.manager}</version>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>2.5</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>${serializer.version}</version>
		</dependency>
	</dependencies>

	<build>
		<finalName>${project.artifactId}</finalName>
	</build>
</project>

Classes de Configuração do Spring

package com.journaldev.spring.config;

import java.util.Properties;

import javax.sql.DataSource;

import org.hibernate.ejb.HibernatePersistence;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories("com.journaldev.spring.repository")
@PropertySource("classpath:database.properties")
public class DataConfig {

	private final String PROPERTY_DRIVER = "driver";
	private final String PROPERTY_URL = "url";
	private final String PROPERTY_USERNAME = "user";
	private final String PROPERTY_PASSWORD = "password";
	private final String PROPERTY_SHOW_SQL = "hibernate.show_sql";
	private final String PROPERTY_DIALECT = "hibernate.dialect";

	@Autowired
	Environment environment;

	@Bean
	LocalContainerEntityManagerFactoryBean entityManagerFactory() {
		LocalContainerEntityManagerFactoryBean lfb = new LocalContainerEntityManagerFactoryBean();
		lfb.setDataSource(dataSource());
		lfb.setPersistenceProviderClass(HibernatePersistence.class);
		lfb.setPackagesToScan("com.journaldev.spring.model");
		lfb.setJpaProperties(hibernateProps());
		return lfb;
	}

	@Bean
	DataSource dataSource() {
		DriverManagerDataSource ds = new DriverManagerDataSource();
		ds.setUrl(environment.getProperty(PROPERTY_URL));
		ds.setUsername(environment.getProperty(PROPERTY_USERNAME));
		ds.setPassword(environment.getProperty(PROPERTY_PASSWORD));
		ds.setDriverClassName(environment.getProperty(PROPERTY_DRIVER));
		return ds;
	}

	Properties hibernateProps() {
		Properties properties = new Properties();
		properties.setProperty(PROPERTY_DIALECT, environment.getProperty(PROPERTY_DIALECT));
		properties.setProperty(PROPERTY_SHOW_SQL, environment.getProperty(PROPERTY_SHOW_SQL));
		return properties;
	}

	@Bean
	JpaTransactionManager transactionManager() {
		JpaTransactionManager transactionManager = new JpaTransactionManager();
		transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
		return transactionManager;
	}
}
  • @Configuration: essa anotação spring indica que é uma classe de configuração.

  • @EnableTransactionManagement: essa anotação permite aos usuários usar o gerenciamento de transações na aplicação.

  • @EnableJpaRepositories("com.journaldev.spring.repository"): indica onde as classes de repositório estão localizadas.

  • @PropertySource("classpath:database.properties"): indica que temos um arquivo de propriedades em nosso classpath. Os valores deste arquivo serão injetados na variável de ambiente. O conteúdo do arquivo database.properties é mostrado abaixo.

    driver=org.postgresql.Driver
    url=jdbc:postgresql://127.0.0.1:5432/postgres
    user=postgres
    password=postgres
    
    hibernate.dialect=org.hibernate.dialect.PostgreSQL82Dialect
    hibernate.show_sql=true
    
  • Para usar o Spring Data, primeiro temos que configurar o bean DataSource. Em seguida, precisamos configurar o bean LocalContainerEntityManagerFactoryBean. Precisamos desse bean para controlar as entidades. Nestes beans, você deve especificar o provedor de persistência, ou seja, HibernatePersistence no nosso caso.

  • O próximo passo é configurar o bean para gerenciamento de transações. No nosso exemplo, é JpaTransactionManager. Note que sem configurar o gerenciador de transações, não podemos usar a @Transactional anotação.

AppInitializer e WebConfig classes servem para configurar nossa aplicação como aplicação web sem usar o arquivo web.xml.

Classe Modelo

package com.journaldev.spring.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "people")
public class Person {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;

	@Column(name = "age")
	private Integer age;
	@Column(name = "first_name")
	private String firstName;
	@Column(name = "last_name")
	private String lastName;

	public Person() {
	}

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	@Override
	public String toString() {
		return "Person{" + "id=" + id + ", age=" + age + ", firstName='" + firstName + '\'' + ", lastName='" + lastName
				+ '\'' + '}';
	}
}

Aqui temos algumas novas anotações. Vamos falar sobre elas com mais detalhes.

  • @Entity: Esta anotação permite que o gerenciador de entidades use essa classe e a coloque em contexto.
  • @Table(name = “people”): associa uma classe a uma tabela no banco de dados.
  • @Id: indica que este campo é a chave primária.
  • @GeneratedValue(strategy = GenerationType.IDENTITY): Define a estratégia para gerar a chave primária.
  • @Column(name = "age"): indica uma coluna no banco de dados com a qual este campo será associado.

Repositório Spring Data JPA

O próximo passo é criar o repositório JPA.

package com.journaldev.spring.repository;

import org.springframework.data.repository.CrudRepository;

import com.journaldev.spring.model.Person;

import java.util.List;

public interface PersonRepository<P> extends CrudRepository<Person, Long> {
    List<Person> findByFirstName(String firstName);
}

Ao herdar de CrudRepository, podemos chamar muitos métodos sem a necessidade de implementá-los. Alguns desses métodos são:

  • save
  • findOne
  • exists
  • findAll
  • count
  • delete
  • deleteAll

Também podemos definir nossos próprios métodos. Esses nomes de métodos devem usar palavras-chave especiais como “find”, “order” com o nome das variáveis. Os desenvolvedores do Spring Data JPA tentaram levar em conta a maioria das opções possíveis que você possa precisar. Em nosso exemplo, o método findByFirstName(String firstName) retorna todas as entradas da tabela onde o campo first_name é igual a firstName. Esta é uma das características mais importantes do Spring Data JPA, pois reduz muito o código repetitivo. Além disso, as chances de erros são menores porque esses métodos do Spring são bem testados por muitos projetos que já os utilizam.

Classe de Serviço Spring

Agora que nosso código Spring Data JPA está pronto, o próximo passo é criar a classe de serviço e definir os métodos com os quais teremos que trabalhar com a tabela do banco de dados.

package com.journaldev.spring.services;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.journaldev.spring.model.Person;
import com.journaldev.spring.repository.PersonRepository;

@Service
public class PersonService {

	@Autowired
	PersonRepository<Person> personRepository;

	@Transactional
	public List<Person> getAllPersons() {
		return (List<Person>) personRepository.findAll();
	}

	@Transactional
	public List<Person> findByName(String name) {
		return personRepository.findByFirstName(name);
	}

	@Transactional
	public Person getById(Long id) {
		return personRepository.findOne(id);
	}

	@Transactional
	public void deletePerson(Long personId) {
		personRepository.delete(personId);
	}

	@Transactional
	public boolean addPerson(Person person) {
		return personRepository.save(person) != null;
	}

	@Transactional
	public boolean updatePerson(Person person) {
		return personRepository.save(person) != null;
	}
}

@Transactional A anotação indica que o método será executado na transação. O Spring cuidará do gerenciamento de transações.

Classe Controladora Spring

O último passo é criar a classe controladora para expor nossas APIs para o mundo externo.

package com.journaldev.spring.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import com.journaldev.spring.model.Person;
import com.journaldev.spring.services.PersonService;

@RestController
public class PersonController {

	@Autowired
	PersonService personService;

	@RequestMapping(value = "/person/{id}", method = RequestMethod.GET)
	public @ResponseBody Person getAllUsers(@PathVariable Long id) {
		return personService.getById(id);
	}

	@RequestMapping(value = "/personByName/{name}", method = RequestMethod.GET)
	public List<Person> getPersoneByName(@PathVariable String name) {
		return personService.findByName(name);
	}

	@RequestMapping(value = "/person", method = RequestMethod.GET)
	public List<Person> getAll() {
		return personService.getAllPersons();
	}

	@RequestMapping(value = "/person/{id}", method = RequestMethod.DELETE)
	public HttpStatus deletePersnone(@PathVariable Long id) {
		personService.deletePerson(id);
		return HttpStatus.NO_CONTENT;
	}

	@RequestMapping(value = "/person", method = RequestMethod.POST)
	public HttpStatus insertPersone(@RequestBody Person person) {
		return personService.addPerson(person) ? HttpStatus.CREATED : HttpStatus.BAD_REQUEST;
	}

	@RequestMapping(value = "/person", method = RequestMethod.PUT)
	public HttpStatus updatePerson(@RequestBody Person person) {
		return personService.updatePerson(person) ? HttpStatus.ACCEPTED : HttpStatus.BAD_REQUEST;
	}
}

Testando o Spring Data JPA

Basta construir e implantar o projeto em seu contêiner de servlet favorito, como o Tomcat. As imagens abaixo mostram a resposta para algumas das chamadas de API.

Spring Data JPA Ler Tudo

Spring Data JPA Obter Por Nome

Spring Data JPA Criar

Spring Data JPA Atualizar

Spring Data JPA Excluir

É isso para o tutorial de exemplo do Spring Data JPA. Você pode baixar o projeto final a partir do link abaixo.

Download do Projeto de Exemplo do Spring Data JPA

Referência: Site Oficial

Source:
https://www.digitalocean.com/community/tutorials/spring-data-jpa