Spring Data JPA simplifies data access by providing a repository abstraction that eliminates boilerplate DAO code. It builds on JPA and Hibernate.

Setup

  <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>
  
  spring:
  datasource:
    url: jdbc:h2:mem:testdb
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
    properties:
      hibernate:
        format_sql: true
  

Entity

  @Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, unique = true)
    private String email;

    private String name;

    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
    private List<Order> orders = new ArrayList<>();
}
  

Repository Interface

  public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByEmail(String email);
    List<User> findByNameContainingIgnoreCase(String name);
    boolean existsByEmail(String email);

    @Query("SELECT u FROM User u WHERE u.createdAt > :since")
    List<User> findRecentUsers(@Param("since") LocalDateTime since);

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

Spring Data generates implementations at runtime — no boilerplate needed.

Derived Query Methods

Method prefix Example Generated SQL
findBy findByEmail(String email) WHERE email = ?
findBy...And findByNameAndActive WHERE name = ? AND active = ?
findBy...Or findByNameOrEmail WHERE name = ? OR email = ?
findBy...OrderBy findByActiveOrderByNameDesc ORDER BY name DESC
countBy countByActiveTrue SELECT COUNT(*) WHERE active = true
deleteBy deleteByEmail DELETE WHERE email = ?

Pagination and Sorting

  Page<User> findByActiveTrue(Pageable pageable);

// Usage
Page<User> page = userRepository.findByActiveTrue(
    PageRequest.of(0, 20, Sort.by("name").ascending()));
  

Specifications (Dynamic Queries)

  public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> { }

Specification<User> spec = (root, query, cb) -> {
    List<Predicate> predicates = new ArrayList<>();
    if (name != null) predicates.add(cb.like(root.get("name"), "%" + name + "%"));
    if (active != null) predicates.add(cb.equal(root.get("active"), active));
    return cb.and(predicates.toArray(new Predicate[0]));
};
Page<User> results = userRepository.findAll(spec, pageable);
  

Auditing

  @Entity
@EntityListeners(AuditingEntityListener.class)
public class User {
    @CreatedDate
    private LocalDateTime createdAt;

    @LastModifiedDate
    private LocalDateTime updatedAt;

    @CreatedBy
    private String createdBy;
}

@EnableJpaAuditing
@SpringBootApplication
public class Application { }
  

Best Practices

  • Extend JpaRepository for full CRUD + pagination support
  • Use derived queries for simple lookups; @Query for complex ones
  • Use @Modifying with @Transactional for update/delete queries
  • Prefer Specifications or QueryDSL for dynamic query building
  • Enable auditing for automatic timestamp tracking