Livro de receitas Spring Data JPA
utilizando Java 11 e Spring Boot 2.5
Cada receita exemplifica um tópico do Spring Data JPA
Spring Data JPA é uma camada de abstração sobre JPA que simplifica a utilização deste.
Fundamentos de JPA e Spring Boot não serão discutidos nas receitas.
Conteúdo
- Configuração
- CRUD
- Buscando todos os registros
- Buscando um registro por ID
- Buscando registros por ID
- Buscando todos os registros ordenadamente
- Buscando registros paginadamente
- Verificando se um registro existe
- Buscando quantidade de registros
- Excluindo registros por ID
- Excluindo registros por entidade
- Excluindo todos os registros
- Excluindo registros em lote
- Inserindo registros sem ID ou versão
- Inserindo registros com ID
- Atualizando um registro com ID conhecido
- Buscando e atualizando um registro
- Query by Example
- JPQL
- Native query
- Specification
- Referências
👉 Algum conceito faltando?
Configuração
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.4</version>
<relativePath/>
</parent>
<groupId>dev.caiosantesso</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2021.9</version>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
Dependências
- Execute
mvn dependency:tree
para obter as dependências do spring-boot-starter-data-jpa.
[INFO] ------------------< dev.caiosantesso:spring-data-jpa >------------------
[INFO] Building spring-data-jpa 2021.9
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:3.1.2:tree (default-cli) @ spring-data-jpa ---
[INFO] dev.caiosantesso:spring-data-jpa:jar:2021.9
[INFO] +- org.springframework.boot:spring-boot-starter-data-jpa:jar:2.5.4:compile
[INFO] | +- org.springframework.boot:spring-boot-starter-aop:jar:2.5.4:compile
[INFO] | | +- org.springframework.boot:spring-boot-starter:jar:2.5.4:compile
[INFO] | | | +- org.springframework.boot:spring-boot:jar:2.5.4:compile
[INFO] | | | +- org.springframework.boot:spring-boot-autoconfigure:jar:2.5.4:compile
[INFO] | | | +- org.springframework.boot:spring-boot-starter-logging:jar:2.5.4:compile
[INFO] | | | | +- ch.qos.logback:logback-classic:jar:1.2.5:compile
[INFO] | | | | | \- ch.qos.logback:logback-core:jar:1.2.5:compile
[INFO] | | | | +- org.apache.logging.log4j:log4j-to-slf4j:jar:2.14.1:compile
[INFO] | | | | | \- org.apache.logging.log4j:log4j-api:jar:2.14.1:compile
[INFO] | | | | \- org.slf4j:jul-to-slf4j:jar:1.7.32:compile
[INFO] | | | +- jakarta.annotation:jakarta.annotation-api:jar:1.3.5:compile
[INFO] | | | \- org.yaml:snakeyaml:jar:1.28:compile
[INFO] | | +- org.springframework:spring-aop:jar:5.3.9:compile
[INFO] | | \- org.aspectj:aspectjweaver:jar:1.9.7:compile
[INFO] | +- org.springframework.boot:spring-boot-starter-jdbc:jar:2.5.4:compile
[INFO] | | +- com.zaxxer:HikariCP:jar:4.0.3:compile
[INFO] | | \- org.springframework:spring-jdbc:jar:5.3.9:compile
[INFO] | +- jakarta.transaction:jakarta.transaction-api:jar:1.3.3:compile
[INFO] | +- jakarta.persistence:jakarta.persistence-api:jar:2.2.3:compile
[INFO] | +- org.hibernate:hibernate-core:jar:5.4.32.Final:compile
[INFO] | | +- org.jboss.logging:jboss-logging:jar:3.4.2.Final:compile
[INFO] | | +- org.javassist:javassist:jar:3.27.0-GA:compile
[INFO] | | +- net.bytebuddy:byte-buddy:jar:1.10.22:compile
[INFO] | | +- antlr:antlr:jar:2.7.7:compile
[INFO] | | +- org.jboss:jandex:jar:2.2.3.Final:compile
[INFO] | | +- com.fasterxml:classmate:jar:1.5.1:compile
[INFO] | | +- org.dom4j:dom4j:jar:2.1.3:compile
[INFO] | | +- org.hibernate.common:hibernate-commons-annotations:jar:5.1.2.Final:compile
[INFO] | | \- org.glassfish.jaxb:jaxb-runtime:jar:2.3.5:compile
[INFO] | | +- jakarta.xml.bind:jakarta.xml.bind-api:jar:2.3.3:compile
[INFO] | | +- org.glassfish.jaxb:txw2:jar:2.3.5:compile
[INFO] | | +- com.sun.istack:istack-commons-runtime:jar:3.0.12:compile
[INFO] | | \- com.sun.activation:jakarta.activation:jar:1.2.2:runtime
[INFO] | +- org.springframework.data:spring-data-jpa:jar:2.5.4:compile
[INFO] | | +- org.springframework.data:spring-data-commons:jar:2.5.4:compile
[INFO] | | +- org.springframework:spring-orm:jar:5.3.9:compile
[INFO] | | +- org.springframework:spring-context:jar:5.3.9:compile
[INFO] | | | \- org.springframework:spring-expression:jar:5.3.9:compile
[INFO] | | +- org.springframework:spring-tx:jar:5.3.9:compile
[INFO] | | +- org.springframework:spring-beans:jar:5.3.9:compile
[INFO] | | +- org.springframework:spring-core:jar:5.3.9:compile
[INFO] | | | \- org.springframework:spring-jcl:jar:5.3.9:compile
[INFO] | | \- org.slf4j:slf4j-api:jar:1.7.32:compile
[INFO] | \- org.springframework:spring-aspects:jar:5.3.9:compile
[INFO] +- com.h2database:h2:jar:1.4.200:runtime
[INFO] \- org.projectlombok:lombok:jar:1.18.20:compile (optional)
[INFO] ------------------------------------------------------------------------
application.properties
spring.output.ansi.enabled=always
#Cria tabelas para as entidades
spring.jpa.hibernate.ddl-auto=none
#Exibe comandos e tempo de duração de data.sql
logging.level.org.springframework.jdbc.datasource.init.ScriptUtils=DEBUG
#Exibe SQL gerado pelo Hibernate
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
#spring.jpa.properties.hibernate.use_sql_comments=true
#Exibe argumentos utilizados pelo Hibernate
--
data.sql
CREATE TABLE `estado` (
`estado` char(2) NOT NULL,
PRIMARY KEY (`estado`)
);
INSERT INTO `estado`
VALUES ('AC'),('AP'),('PE'),('PR'),('RS'),('SC'),('SP');
CREATE TABLE `clube` (
`id` tinyint NOT NULL AUTO_INCREMENT,
`nome` varchar(16),
`estado` char(2),
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`nome`),
CONSTRAINT `clube_FK` FOREIGN KEY (`estado`) REFERENCES `estado` (`estado`)
);
INSERT INTO `clube`
VALUES (3,'Athlético-PR','PR'),(6,'Avaí','SC'),(27,'Internacional',
'RS'),(32,'Palmeiras','SP'), (39,'Santos','SP'),(42,'Sport','PE');
CRUD
A interface org.springframework.data.jpa.repository.JpaRepository
provê métodos para a maior parte dos casos de uso mais simples. Cada um dos repositórios gerencia uma entidade.
- Crie uma entidade.
- Crie uma interface estendendo
JpaRepository<T, ID>
.- Substitua o parâmetro de tipo
T
pela entidade. - E
ID
pelo campo da entidade anotado com@Id
.
- Substitua o parâmetro de tipo
- Injete a interface criada.
- Chame o método.
Buscando todos os registros
findAll()
devolve lista de entidades com todos os registros.
package dev.caiosantesso.spring_data_jpa.crud.select.all;
import lombok.*;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.JpaRepository;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.Id;
import javax.persistence.PersistenceContext;
import java.util.List;
interface ClubeRepo extends JpaRepository<Clube, Integer> {}
@NoArgsConstructor @Getter @Setter
@Entity
class Clube {
@Id Integer id;
String nome;
}
@SpringBootApplication @RequiredArgsConstructor
public class SelecioneTodos implements ApplicationRunner {
final ClubeRepo repo;
public static void main(String[] args) {
SpringApplication.run(SelecioneTodos.class, args);
}
public void run(ApplicationArguments args) {
List<Clube> todosOsClubes = repo.findAll();
}
}
Consulta gerada.
select
clube0_.id as id1_0_,
clube0_.nome as nome2_0_
from
clube clube0_
Buscando um registro por ID
findById(ID)
traz Optional<T>
com um possível registro.
package dev.caiosantesso.spring_data_jpa.crud.select.one;
import lombok.*;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.JpaRepository;
import javax.persistence.Entity;
import javax.persistence.Id;
import java.util.List;
import java.util.Optional;
import java.util.Set;
interface ClubeRepo extends JpaRepository<Clube, Integer> {}
@NoArgsConstructor @Getter @Setter
@Entity
class Clube {
@Id Integer id;
String nome;
}
@SpringBootApplication @RequiredArgsConstructor
public class SelecioneUmPorId implements ApplicationRunner {
final ClubeRepo repo;
public static void main(String[] args) {
SpringApplication.run(SelecioneUmPorId.class, args);
}
public void run(ApplicationArguments args) {
Optional<Clube> possivelClube = repo.findById(32);
}
}
select
clube0_.id as id1_0_0_,
clube0_.nome as nome2_0_0_
from
clube clube0_
where
clube0_.id=?
-binding parameter [1] as [INTEGER] - [32]
Buscando registros por ID
findById(Iterable<ID>)
traz lista com todos os registros cujos IDs foram especificados no Iterable<ID>
.
package dev.caiosantesso.spring_data_jpa.crud.select.some;
import lombok.*;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.JpaRepository;
import javax.persistence.Entity;
import javax.persistence.Id;
import java.util.List;
import java.util.Optional;
import java.util.Set;
interface ClubeRepo extends JpaRepository<Clube, Integer> {}
@NoArgsConstructor @Getter @Setter
@Entity
class Clube {
@Id Integer id;
String nome;
}
@SpringBootApplication @RequiredArgsConstructor
public class SelecioneAlgunsPorId implements ApplicationRunner {
final ClubeRepo repo;
public static void main(String[] args) {
SpringApplication.run(SelecioneAlgunsPorId.class, args);
}
public void run(ApplicationArguments args) {
Iterable<Integer> ids = Set.of(27, 32);
List<Clube> clubes = repo.findAllById(ids);
}
}
select
clube0_.id as id1_0_,
clube0_.nome as nome2_0_
from
clube clube0_
where
clube0_.id in (
? , ?
)
-binding parameter [1] as [INTEGER] - [27]
-binding parameter [2] as [INTEGER] - [32]
Buscando todos os registros ordenadamente
- Crie um objeto
Sort
para definir a ordenação.- Por padrão
Sort
ordena a coluna ascendentemente. Order
pode expandir a definição deSort
especificando ordem dos nulos e ignorando a caixa das letras.Sort#and(Sort)
combina duas instâncias deSort
, priorizando a ordenação da esquerda para direita.
- Por padrão
- Invoque
findAll(Sort)
.
package dev.caiosantesso.spring_data_jpa.crud.select.sorted;
import lombok.*;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.JpaRepository;
import javax.persistence.Entity;
import javax.persistence.Id;
import java.util.List;
import java.util.Set;
interface ClubeRepo extends JpaRepository<Clube, Integer> {}
@NoArgsConstructor @Getter @Setter
@Entity
class Clube {
@Id Integer id;
String nome;
String estado;
}
@SpringBootApplication @RequiredArgsConstructor
public class SelecioneOrdenadamente implements ApplicationRunner {
final ClubeRepo repo;
public static void main(String[] args) {
SpringApplication.run(SelecioneOrdenadamente.class, args);
}
public void run(ApplicationArguments args) {
Sort ordemPorEstado = Sort.by("estado");
Sort ordemPorNome = Sort.by("id").descending();
Sort ordemPorEstadoENome = ordemPorEstado.and(ordemPorNome);
List<Clube> clubes = repo.findAll(ordemPorEstadoENome);
}
}
select
clube0_.id as id1_0_,
clube0_.estado as estado2_0_,
clube0_.nome as nome3_0_
from
clube clube0_
order by
lower(clube0_.nome) asc,
clube0_.id desc
Buscando registros paginadamente
- Crie um
Pageable
chamandoPageRequest#of(int, int, Sort)
com, respectivamente:- o número da página (inicia em 0);
- a quantidade de registros que cada página deve ter (a última página obviamente pode conter menos);
- e a definição da ordenação (opcional).
- Invoque
findAll(Pageable)
. Page
então é devolvido contendo a quantidade de registros parametrizada emPageable
.- Chame
Page#getContent()
para obter as entidades em lista.
- Chame
package dev.caiosantesso.spring_data_jpa.crud.select.paged;
import lombok.*;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.JpaRepository;
import javax.persistence.Entity;
import javax.persistence.Id;
interface ClubeRepo extends JpaRepository<Clube, Integer> {}
@NoArgsConstructor @Getter @Setter @ToString
@Entity
class Clube {
@Id Integer id;
String nome;
String estado;
}
@SpringBootApplication @RequiredArgsConstructor
public class SelecionePaginadamente implements ApplicationRunner {
final ClubeRepo repo;
public static void main(String[] args) {
SpringApplication.run(SelecionePaginadamente.class, args);
}
public void run(ApplicationArguments args) {
Pageable paginavel = PageRequest.of(3, 2, Sort.by("nome"));
Page<Clube> pagina3 = repo.findAll(paginavel);
System.out.printf("%d itens em %d paginas de até %d itens.%n",
pagina3.getTotalElements(),
pagina3.getTotalPages(),
pagina3.getSize());
System.out.printf("A página %d contém %d item:%s.%n",
pagina3.getNumber(),
pagina3.getNumberOfElements(),
pagina3.getContent());
}
}
7 itens em 4 paginas de até 2 itens.
A página 3 contém 1 item:[Clube(id=42, nome=Sport, estado=PE)].
select
clube0_.id as id1_0_,
clube0_.estado as estado2_0_,
clube0_.nome as nome3_0_
from
clube clube0_
order by
clube0_.nome asc limit ? offset ?
Verificando se um registro existe
exists(ID)
devolve booleano indicando a existência do registro dado um ID
.
package dev.caiosantesso.spring_data_jpa.crud.select.exists;
import lombok.*;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.JpaRepository;
import javax.persistence.Entity;
import javax.persistence.Id;
import java.util.Optional;
interface ClubeRepo extends JpaRepository<Clube, Integer> {}
@NoArgsConstructor @Getter @Setter @ToString
@Entity
class Clube {
@Id Integer id;
String nome;
}
@SpringBootApplication @RequiredArgsConstructor
public class SelecioneExistente implements ApplicationRunner {
final ClubeRepo repo;
public static void main(String[] args) {
SpringApplication.run(SelecioneExistente.class, args);
}
public void run(ApplicationArguments args) {
boolean existe = repo.existsById(32);
}
}
select
count(*) as col_0_0_
from
clube clube0_
where
clube0_.id=?
-binding parameter [1] as [INTEGER] - [32]
Buscando quantidade de registros
count()
devolve a quantidade de registros.
package dev.caiosantesso.spring_data_jpa.crud.select.count;
import lombok.*;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.JpaRepository;
import javax.persistence.Entity;
import javax.persistence.Id;
interface ClubeRepo extends JpaRepository<Clube, Integer> {}
@NoArgsConstructor @Getter @Setter @ToString
@Entity
class Clube {
@Id Integer id;
String nome;
}
@SpringBootApplication @RequiredArgsConstructor
public class SelecioneContagem implements ApplicationRunner {
final ClubeRepo repo;
public static void main(String[] args) {
SpringApplication.run(SelecioneContagem.class, args);
}
public void run(ApplicationArguments args) {
long contagem = repo.count();
}
}
select
count(*) as col_0_0_
from
clube clube0_
Excluindo registros por ID
deleteById(ID)
busca o registro com o ID especificado, e:
- caso encontrado exclui o registro;
- caso contrário lança a exceção
org.springframework.dao.EmptyResultDataAccessException
.
deleteAllById(Iterable<ID>)
itera os IDs informandos chamando deleteById(ID)
para cada.
package dev.caiosantesso.spring_data_jpa.crud.delete.id;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.JpaRepository;
import javax.persistence.*;
import java.util.List;
interface EstadoRepo extends JpaRepository<Estado, String> {}
@NoArgsConstructor @Getter @Setter
@Entity
class Estado {
@Id String estado;
@OneToMany(mappedBy = "estado", cascade = CascadeType.REMOVE)
List<Clube> clubes;
}
@NoArgsConstructor @Getter @Setter
@Entity
class Clube {
@Id Integer id;
String nome;
@ManyToOne @JoinColumn(name = "estado") Estado estado;
}
@SpringBootApplication @RequiredArgsConstructor
public class ExcluaPorId implements ApplicationRunner {
final EstadoRepo repo;
public static void main(String[] args) {
SpringApplication.run(ExcluaPorId.class, args);
}
public void run(ApplicationArguments args) {
repo.deleteById("RS");
}
}
select
estado0_.estado as estado1_1_0_
from
estado estado0_
where
estado0_.estado=?
--binding parameter [1] as [VARCHAR] - [RS]
select
clubes0_.estado as estado3_0_0_,
clubes0_.id as id1_0_0_,
clubes0_.id as id1_0_1_,
clubes0_.estado as estado3_0_1_,
clubes0_.nome as nome2_0_1_
from
clube clubes0_
where
clubes0_.estado=?
--binding parameter [1] as [VARCHAR] - [RS]
delete
from
clube
where
id=?
--binding parameter [1] as [INTEGER] - [27]
delete
from
estado
where
estado=?
--binding parameter [1] as [VARCHAR] - [RS]
Excluindo registros por entidade
delete(T)
obtém o ID
da entidade T
, busca entidade com esse ID
e:
- caso encontrada verifica então se a entidade passada como argumento consta no cache (
EntityManager
)- se sim, exclui a entidade.
- se não, sincroniza a entidade passada como argumento com
EntityManager
e exclui a nova referência sincronizada.
- caso não encontrada nada faz.
deleteAll(Iterable<T>)
itera as entidades chamando delete(T)
para cada.
package dev.caiosantesso.spring_data_jpa.crud.delete.entity;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.JpaRepository;
import javax.persistence.*;
import java.util.List;
interface EstadoRepo extends JpaRepository<Estado, String> {}
@NoArgsConstructor @Getter @Setter
@Entity
class Estado {
@Id String estado;
@OneToMany(mappedBy = "estado", cascade = CascadeType.REMOVE)
List<Clube> clubes;
}
@NoArgsConstructor @Getter @Setter
@Entity
class Clube {
@Id Integer id;
String nome;
@ManyToOne @JoinColumn(name = "estado")
Estado estado;
}
@SpringBootApplication @RequiredArgsConstructor
public class ExcluaEntidade implements ApplicationRunner {
final EstadoRepo repo;
public static void main(String[] args) {
SpringApplication.run(ExcluaEntidade.class, args);
}
public void run(ApplicationArguments args) {
Estado uf = repo.getById("RS");
repo.delete(uf);
}
}
select
estado0_.estado as estado1_1_0_
from
estado estado0_
where
estado0_.estado=?
--binding parameter [1] as [VARCHAR] - [RS]
select
clubes0_.estado as estado3_0_0_,
clubes0_.id as id1_0_0_,
clubes0_.id as id1_0_1_,
clubes0_.estado as estado3_0_1_,
clubes0_.nome as nome2_0_1_
from
clube clubes0_
where
clubes0_.estado=?
--binding parameter [1] as [VARCHAR] - [RS]
delete
from
clube
where
id=?
--binding parameter [1] as [INTEGER] - [27]
delete
from
estado
where
estado=?
--binding parameter [1] as [VARCHAR] - [RS]
Excluindo todos os registros
deleteAll()
busca por todos os registros e então chama delete(T)
para cada um.
package dev.caiosantesso.spring_data_jpa.crud.delete.all;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.JpaRepository;
import javax.persistence.*;
import java.util.List;
interface EstadoRepo extends JpaRepository<Estado, String> {}
@NoArgsConstructor @Getter @Setter
@Entity
class Estado {
@Id String estado;
@OneToMany(mappedBy = "estado", cascade = CascadeType.REMOVE)
List<Clube> clubes;
}
@NoArgsConstructor @Getter @Setter
@Entity
class Clube {
@Id Integer id;
String nome;
@ManyToOne @JoinColumn(name = "estado")
Estado estado;
}
@SpringBootApplication @RequiredArgsConstructor
public class ExcluaTudo implements ApplicationRunner {
final EstadoRepo repo;
public static void main(String[] args) {
SpringApplication.run(ExcluaTudo.class, args);
}
public void run(ApplicationArguments args) {
repo.deleteAll();
}
}
select
estado0_.estado as estado1_1_
from
estado estado0_
select
clubes0_.estado as estado3_0_0_,
clubes0_.id as id1_0_0_,
clubes0_.id as id1_0_1_,
clubes0_.estado as estado3_0_1_,
clubes0_.nome as nome2_0_1_
from
clube clubes0_
where
clubes0_.estado=?
--binding parameter [1] as [VARCHAR] - [AC]
--Repete consulta acim para cada estado
--binding parameter [1] as [VARCHAR] - [PE]
--binding parameter [1] as [VARCHAR] - [RS]
--binding parameter [1] as [VARCHAR] - [SP]
delete
from
estado
where
estado=?
--binding parameter [1] as [VARCHAR] - [AC]
delete
from
clube
where
id=?
--binding parameter [1] as [INTEGER] - [42]
delete
from
estado
where
estado=?
--binding parameter [1] as [VARCHAR] - [PE]
delete
from
clube
where
id=?
--binding parameter [1] as [INTEGER] - [27]
delete
from
estado
where
estado=?
--binding parameter [1] as [VARCHAR] - [RS]
delete
from
clube
where
id=?
--binding parameter [1] as [INTEGER] - [32]
delete
from
estado
where
estado=?
--binding parameter [1] as [VARCHAR] - [SP]
Excluindo registros em lote
Os métodos delete*InBatch()
:
- Ignoram
javax.persistence.CascadeType
; - Não sincronizam com
EntityManager
, consequentemente aceitando qualquer ID, existente ou não. - Ignoram
javax.persistence.PreRemove
;
package dev.caiosantesso.spring_data_jpa.crud.delete.batch;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.JpaRepository;
import javax.persistence.*;
import java.util.List;
import java.util.Set;
interface ClubeRepo extends JpaRepository<Clube, Integer> {}
@NoArgsConstructor @Getter @Setter
@Entity
class Estado {
@Id String estado;
}
@NoArgsConstructor @Getter @Setter
@Entity
class Clube {
@Id Integer id;
String nome;
//Cardinalidade errada para demonstrar falta de cascade.
@OneToOne(cascade = CascadeType.REMOVE) @JoinColumn(name = "estado")
Estado estado;
@PreRemove
void ignorado() {System.out.println("Ignorado");}
}
@SpringBootApplication @RequiredArgsConstructor
public class DeleteInBatch implements ApplicationRunner {
final ClubeRepo repo;
public static void main(String[] args) {
SpringApplication.run(DeleteInBatch.class, args);
}
public void run(ApplicationArguments args) {
List<Integer> ids = List.of(32, 99);
repo.deleteAllByIdInBatch(ids);
Clube inter = repo.getById(27);
Clube sport = repo.getById(42);
repo.deleteAllInBatch(Set.of(inter, sport));
repo.deleteAllInBatch();
}
}
deleteAllByIdInBatch(Iterable<ID>)
exclui todos os registros cujos IDs foram especificados no Iterable<ID>
em um só commando SQL.
delete
from
clube
where
id in (
? , ?
)
-binding parameter [1] as [INTEGER] - [32]
-binding parameter [2] as [INTEGER] - [99]
deleteAllInBatch(Iterable<T>)
exclui todas as entidades especificadas no Iterable<T>
em um só comando SQL.
delete
from
clube
where
id=?
or id=?
-binding parameter [1] as [INTEGER] - [27]
-binding parameter [2] as [INTEGER] - [42]
deleteAllInBatch()
exclui todos os registros em um só comando SQL.
delete
from
clube
Inserindo registros sem ID ou versão
- Crie uma entidade:
- com
@Id
nulo; - e/ou com
@Version
nulo.
- com
- Chame
save(T)
.
package dev.caiosantesso.spring_data_jpa.crud.insert.no_id;
import lombok.*;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.JpaRepository;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
interface ClubeRepo extends JpaRepository<Clube, Integer> {}
@NoArgsConstructor @Getter @Setter @AllArgsConstructor
@Entity
class Clube {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) Integer id;
String nome;
String estado;
}
@SpringBootApplication @RequiredArgsConstructor
public class InsiraSemId implements ApplicationRunner {
final ClubeRepo repo;
public static void main(String[] args) {
SpringApplication.run(InsiraSemId.class, args);
}
public void run(ApplicationArguments args) {
Clube clube = new Clube(null, "Bragantino", "SP");
repo.save(clube);
}
}
insert
into
clube
(id, estado, nome)
values
(null, ?, ?)
--binding parameter [1] as [VARCHAR] - [SP]
--binding parameter [2] as [VARCHAR] - [Bragantino]
Inserindo registros com ID
- Implemente
Persistable<ID>
na entidade a ser inserida. - Implemente
isNew()
devolvendotrue
quando a entidade não existir no BD. - Crie a entidade a ser inserida.
- Invoque
save(T)
passando a entidade.
package dev.caiosantesso.spring_data_jpa.crud.insert.with_id;
import lombok.*;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.domain.Persistable;
import org.springframework.data.jpa.repository.JpaRepository;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Transient;
interface ClubeRepo extends JpaRepository<Clube, Integer> {}
@NoArgsConstructor @Getter @Setter @AllArgsConstructor
@Entity
class Clube implements Persistable<Integer> {
@Id Integer id;
String nome;
String estado;
@Transient boolean isNew;
@Override
public boolean isNew() {
return isNew;
}
}
@SpringBootApplication @RequiredArgsConstructor
public class InsiraComId implements ApplicationRunner {
final ClubeRepo repo;
public static void main(String[] args) {
SpringApplication.run(InsiraComId.class, args);
}
public void run(ApplicationArguments args) {
Clube clube = new Clube(99, "Bragantino", "SP", true);
repo.save(clube);
}
}
insert
into
clube
(estado, nome, id)
values
(?, ?, ?)
--binding parameter [1] as [VARCHAR] - [SP]
--binding parameter [2] as [VARCHAR] - [Bragantino]
--binding parameter [3] as [INTEGER] - [99]
Atualizando um registro com ID conhecido
- Crie uma entidade informando o
ID
. - Chame
save(T)
passando a entidade.
package dev.caiosantesso.spring_data_jpa.crud.update;
import lombok.*;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.JpaRepository;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.util.Optional;
interface ClubeRepo extends JpaRepository<Clube, Integer> {}
@NoArgsConstructor @Getter @Setter @AllArgsConstructor
@Entity
class Clube {
@Id Integer id;
String nome;
String estado;
}
@SpringBootApplication @RequiredArgsConstructor
public class AtualizeUm implements ApplicationRunner {
final ClubeRepo repo;
public static void main(String[] args) {
SpringApplication.run(AtualizeUm.class, args);
}
public void run(ApplicationArguments args) {
Clube palmeiras = new Clube(32, "Palestra", "SP");
repo.save(palmeiras);
}
}
select
clube0_.id as id1_0_0_,
clube0_.estado as estado2_0_0_,
clube0_.nome as nome3_0_0_
from
clube clube0_
where
clube0_.id=?
--binding parameter [1] as [INTEGER] - [32]
update
clube
set
estado=?,
nome=?
where
id=?
--binding parameter [1] as [VARCHAR] - [SP]
--binding parameter [2] as [VARCHAR] - [Palestra]
--binding parameter [3] as [INTEGER] - [32]
Buscando e atualizando um registro
- Anote o método com
@Transactional
. - Busque a entidade preferencialmente com
getById(ID)
ou com algum métodfind*()
. - Altere o campo da entidade com o respectivo
setter
.
package dev.caiosantesso.spring_data_jpa.crud.update.transaction;
import lombok.*;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.Entity;
import javax.persistence.Id;
import java.util.Optional;
interface ClubeRepo extends JpaRepository<Clube, Integer> {}
@NoArgsConstructor @Getter @Setter @AllArgsConstructor
@Entity
class Clube {
@Id Integer id;
String nome;
String estado;
}
@SpringBootApplication @RequiredArgsConstructor
public class SelecioneEAtualize implements ApplicationRunner {
final ClubeRepo repo;
public static void main(String[] args) {
SpringApplication.run(SelecioneEAtualize.class, args);
}
@Transactional
public void run(ApplicationArguments args) {
Clube palmeiras = repo.getById(32);
palmeiras.setNome("Palestra");
}
}
select
clube0_.id as id1_0_0_,
clube0_.estado as estado2_0_0_,
clube0_.nome as nome3_0_0_
from
clube clube0_
where
clube0_.id=?
--binding parameter [1] as [INTEGER] - [32]
update
clube
set
estado=?,
nome=?
where
id=?
--binding parameter [1] as [VARCHAR] - [SP]
--binding parameter [2] as [VARCHAR] - [Palestra]
--binding parameter [3] as [INTEGER] - [32]
Query by Example
JPQL
Native query
Projections
Specification
Referências
- Acessando dados com JPA - Guia (em inglês)
- Spring Data JPA - Documentação de Referência (em inglês)
- Como persist e merge funcionam em JPA (em inglês)