Pular para o conteúdo principal

Quiz: Spring Data JPA Avançado

Teste seus conhecimentos sobre Spring Data JPA, relações e consultas.

30 perguntas - Clique em cada pergunta para revelar a resposta.


Perguntas 1-10: Convenções e Consultas​

Pergunta 1: O que são Query Methods do Spring Data JPA?

Resposta: Métodos cujo nome define a consulta automaticamente:

public interface UserRepository extends JpaRepository<User, Long> {
List<User> findByEmail(String email);
List<User> findByLastNameAndFirstName(String ln, String fn);
List<User> findByAgeGreaterThan(int age);
}

O Spring gera a implementação SQL automaticamente.


Pergunta 2: Quais são as palavras-chave de consulta mais comuns?

Resposta:

Palavra-chaveExemplo
AndfindByNameAndAge
OrfindByNameOrEmail
BetweenfindByAgeBetween
LessThanfindByAgeLessThan
GreaterThanfindByAgeGreaterThan
LikefindByNameLike
ContainingfindByNameContaining
OrderByfindByAgeOrderByNameAsc
NotfindByNameNot
InfindByAgeIn(Collection)
True/FalsefindByActiveTrue

Pergunta 3: Como limitar o número de resultados?

Resposta:

// Top/First
User findFirstByOrderByCreatedAtDesc();
List<User> findTop10ByOrderByScoreDesc();

// Com Pageable
Page<User> findAll(Pageable pageable);

Pergunta 4: Como escrever uma consulta JPQL personalizada?

Resposta: Com @Query:

@Query("SELECT u FROM User u WHERE u.email LIKE %:domain")
List<User> findByEmailDomain(@Param("domain") String domain);

@Query("SELECT u FROM User u WHERE u.age > :age ORDER BY u.name")
List<User> findOlderThan(@Param("age") int age);

Pergunta 5: Como escrever uma consulta SQL nativa?

Resposta:

@Query(value = "SELECT * FROM users WHERE email LIKE %?1", nativeQuery = true)
List<User> findByEmailNative(String domain);

@Query(value = "SELECT COUNT(*) FROM users", nativeQuery = true)
long countAllNative();

Pergunta 6: Como fazer uma consulta de atualização?

Resposta:

@Modifying
@Query("UPDATE User u SET u.active = false WHERE u.lastLogin < :date")
int deactivateInactiveUsers(@Param("date") LocalDate date);

@Modifying
@Query("DELETE FROM User u WHERE u.active = false")
int deleteInactiveUsers();

@Modifying é obrigatório para UPDATE/DELETE.


Pergunta 7: Como projetar apenas alguns campos?

Resposta: Com projeções:

// Interface projection
public interface UserSummary {
String getName();
String getEmail();
}

List<UserSummary> findByActiveTrue();

// DTO projection
@Query("SELECT new com.example.UserDto(u.name, u.email) FROM User u")
List<UserDto> findAllAsDto();

Pergunta 8: Como verificar a existência de uma entidade?

Resposta:

boolean existsByEmail(String email);
boolean existsByIdAndActiveTrue(Long id);

// Evita carregar a entidade completa

Pergunta 9: Como contar entidades?

Resposta:

long countByActiveTrue();
long countByDepartment(String department);

@Query("SELECT COUNT(u) FROM User u WHERE u.age > :age")
long countOlderThan(@Param("age") int age);

Pergunta 10: Como deletar por critérios?

Resposta:

void deleteByEmail(String email);
long deleteByActivefalse(); // Retorna o número removido

@Modifying
@Query("DELETE FROM User u WHERE u.createdAt < :date")
int deleteOldUsers(@Param("date") LocalDate date);

Perguntas 11-20: Relações​

Pergunta 11: Como definir uma relação OneToMany?

Resposta:

@Entity
public class Department {
@Id
private Long id;

@OneToMany(mappedBy = "department", cascade = CascadeType.ALL)
private List<Employee> employees = new ArrayList<>();
}

@Entity
public class Employee {
@ManyToOne
@JoinColumn(name = "department_id")
private Department department;
}

Pergunta 12: O que é mappedBy?

Resposta: mappedBy indica o lado não-proprietário da relação. Ele aponta para o campo da outra entidade que possui a chave estrangeira.

@OneToMany(mappedBy = "department")  // "department" é o campo em Employee
private List<Employee> employees;

Pergunta 13: Como definir uma relação ManyToMany?

Resposta:

@Entity
public class Student {
@ManyToMany
@JoinTable(
name = "student_course",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id")
)
private Set<Course> courses = new HashSet<>();
}

@Entity
public class Course {
@ManyToMany(mappedBy = "courses")
private Set<Student> students = new HashSet<>();
}

Pergunta 14: Qual a diferença entre FetchType.LAZY e EAGER?

Resposta:

  • LAZY: carrega apenas quando acessado (recomendado)
  • EAGER: carrega imediatamente com a entidade pai
@OneToMany(fetch = FetchType.LAZY)  // Padrão para coleções
private List<Order> orders;

@ManyToOne(fetch = FetchType.EAGER) // Padrão para single
private User user;

Pergunta 15: O que é o problema N+1?

Resposta: Quando você carrega N entidades e faz N consultas adicionais para as relações:

// 1 consulta para os departamentos
List<Department> deps = repo.findAll();

// N consultas para os funcionários (1 por departamento)
deps.forEach(d -> d.getEmployees().size());

Solução: JOIN FETCH ou @EntityGraph.


Pergunta 16: Como resolver o problema N+1 com JOIN FETCH?

Resposta:

@Query("SELECT d FROM Department d JOIN FETCH d.employees")
List<Department> findAllWithEmployees();

Uma única consulta carrega tudo.


Pergunta 17: O que é um @EntityGraph?

Resposta: Define quais relações carregar:

@EntityGraph(attributePaths = {"employees", "manager"})
List<Department> findAll();

// Ou definido na entidade
@NamedEntityGraph(
name = "Department.full",
attributeNodes = @NamedAttributeNode("employees")
)
@Entity
public class Department { }

@EntityGraph("Department.full")
List<Department> findByName(String name);

Pergunta 18: Como gerenciar cascatas?

Resposta:

@OneToMany(cascade = CascadeType.ALL)  // Todas as operações
@OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})

// Tipos:
// PERSIST - salvar
// MERGE - atualizar
// REMOVE - remover
// REFRESH - atualizar do banco
// DETACH - desanexar
// ALL - tudo

Pergunta 19: O que é orphanRemoval?

Resposta: Remove automaticamente os filhos órfãos:

@OneToMany(mappedBy = "parent", orphanRemoval = true)
private List<Child> children;

// Se remover um filho da lista, ele é removido do banco
parent.getChildren().remove(child); // DELETE no banco

Pergunta 20: Como evitar referências circulares em JSON?

Resposta:

// Opção 1: @JsonIgnore
@JsonIgnore
@ManyToOne
private Department department;

// Opção 2: @JsonManagedReference / @JsonBackReference
@JsonManagedReference
@OneToMany(mappedBy = "department")
private List<Employee> employees;

@JsonBackReference
@ManyToOne
private Department department;

// Opção 3: Usar DTOs (recomendado)

Perguntas 21-30: Paginação e Performance​

Pergunta 21: Como implementar paginação?

Resposta:

// Repository
Page<User> findAll(Pageable pageable);
Page<User> findByActiveTrue(Pageable pageable);

// Service
Pageable pageable = PageRequest.of(0, 20, Sort.by("name"));
Page<User> page = userRepository.findAll(pageable);

// Resultado
page.getContent(); // Lista
page.getTotalElements(); // Total
page.getTotalPages(); // Número de páginas
page.getNumber(); // Página atual

Pergunta 22: Como ordenar os resultados?

Resposta:

// Por método
List<User> findByActiveOrderByNameAsc(boolean active);

// Por parâmetro Sort
List<User> findByActive(boolean active, Sort sort);
userRepository.findByActive(true, Sort.by("name").descending());

// Multi-critérios
Sort sort = Sort.by("lastName").and(Sort.by("firstName"));

Pergunta 23: Como usar Slice ao invés de Page?

Resposta: Slice não conta o total (mais performático):

Slice<User> findByActiveTrue(Pageable pageable);

// Resultado
slice.getContent();
slice.hasNext(); // Existe próxima página?
slice.hasPrevious();
// Não possui getTotalElements() nem getTotalPages()

Pergunta 24: Como auditar entidades (createdAt, updatedAt)?

Resposta:

@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
public abstract class Auditable {

@CreatedDate
private LocalDateTime createdAt;

@LastModifiedDate
private LocalDateTime updatedAt;

@CreatedBy
private String createdBy;
}

// Ativar
@EnableJpaAuditing
@Configuration
public class JpaConfig { }

Pergunta 25: Como implementar soft delete?

Resposta:

@Entity
@Where(clause = "deleted = false") // Hibernate
public class User {
private boolean deleted = false;
}

// Ou com @SQLDelete
@SQLDelete(sql = "UPDATE users SET deleted = true WHERE id = ?")

Pergunta 26: Como otimizar consultas com índices?

Resposta:

@Entity
@Table(indexes = {
@Index(name = "idx_email", columnList = "email"),
@Index(name = "idx_name_dept", columnList = "lastName, department")
})
public class User { }

Pergunta 27: O que é o cache de segundo nível?

Resposta: Cache compartilhado entre sessões do Hibernate:

@Entity
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Product { }

// Configuração
spring.jpa.properties.hibernate.cache.use_second_level_cache=true
spring.jpa.properties.hibernate.cache.region.factory_class=...

Pergunta 28: Como usar Specifications para consultas dinâmicas?

Resposta:

public interface UserRepository extends JpaSpecificationExecutor<User> { }

// Specification
Specification<User> hasName(String name) {
return (root, query, cb) -> cb.equal(root.get("name"), name);
}

Specification<User> isActive() {
return (root, query, cb) -> cb.isTrue(root.get("active"));
}

// Combinação
userRepository.findAll(hasName("John").and(isActive()));

Pergunta 29: Como executar consultas em batch?

Resposta:

// Configuração
spring.jpa.properties.hibernate.jdbc.batch_size=50

// Código
@Transactional
public void saveAll(List<User> users) {
for (int i = 0; i < users.size(); i++) {
entityManager.persist(users.get(i));
if (i % 50 == 0) {
entityManager.flush();
entityManager.clear();
}
}
}

Pergunta 30: Como gerenciar versões para optimistic locking?

Resposta:

@Entity
public class Product {
@Version
private Long version;
}

// Se duas transações modificarem simultaneamente,
// uma lança OptimisticLockException

Pontuação​

PontuaçãoNível
27-30Excelente - JPA dominado
21-26Bom - Algumas revisões
15-20Médio - Releia os capítulos
Menos de 15Precisa revisar

Voltar ao Módulo 6