Cheat SheetsHibernate & JPASpring Data JPA

Spring Data JPA — Cheat Sheet

Hibernate & JPA · 5 topics. Download the PDF or the Instagram carousel and share it.

Cheat Sheet · AiCanCode.org
Spring Data JPA
Hibernate & JPA5 topicsQuick revision reference
1

Spring Data JPA — JpaRepository

Extend JpaRepository<Entity, ID> to get findById, findAll, save, delete, and pagination out of the box; Spring Data generates the implementation at runtime.

  • JpaRepository provides CRUD, batch, and pagination APIs without any implementation code
  • Derived query method names are parsed into JPQL at startup — no SQL needed for simple queries
  • @Query accepts JPQL (entity/field names) or native SQL (nativeQuery=true)
  • @Modifying + @Transactional is required for UPDATE/DELETE @Query methods
  • Interface projections reduce fetched columns without writing DTO constructors
  • Pagination: return Page<T> and pass a Pageable parameter for count + data in one query
Spring Data JPA — derived query methods
@Entity
public class User {
    @Id @GeneratedValue Long id;
    String email;
    String status;
    int age;
}

public interface UserRepository extends JpaRepository<User, Long> {

    // SELECT u FROM User u WHERE u.email = ?1
    Optional<User> findByEmail(String email);

    // SELECT u FROM User u WHERE u.status = ?1 AND u.age >= ?2
    List<User> findByStatusAndAgeGreaterThanEqual(String status, int minAge);

    // SELECT COUNT(u) FROM User u WHERE u.status = ?1
    long countByStatus(String status);

    // SELECT u FROM User u WHERE u.email LIKE %?1%
    List<User> findByEmailContaining(String fragment);

    // Pagination: returns a Page<User> with total count
    Page<User> findByStatus(String status, Pageable pageable);
}

// Usage
Page<User> page = userRepo.findByStatus("ACTIVE",
    PageRequest.of(0, 20, Sort.by("email")));
2

Query Methods by Method Name

Spring Data parses method names like findByEmailAndStatus, countByCategory, or deleteByExpiredBefore and generates the corresponding JPQL automatically.

  • Method name grammar: Subject (find/count/exists/delete) + By + Predicates (And/Or/Between...)
  • Property names are validated at startup — a typo in findByEmial causes a startup failure
  • Comparison keywords: GreaterThan, LessThan, Between, In, IsNull, Like, Containing, etc.
  • exists queries generate SQL EXISTS — more efficient than loading entities and checking size
  • Top/First limits rows at the query level: findTop5By…, findFirstBy…
  • For complex queries, switch to @Query — method names become unreadable beyond 2-3 predicates
Java — query method name examples
public interface UserRepository extends JpaRepository<User, Long> {

    // find by single field
    Optional<User> findByEmail(String email);

    // AND / OR
    List<User> findByStatusAndRole(String status, String role);
    List<User> findByEmailOrPhone(String email, String phone);

    // Comparison keywords
    List<User> findByAgeGreaterThan(int age);
    List<User> findByCreatedAtBetween(LocalDateTime from, LocalDateTime to);

    // String operations
    List<User> findByNameContaining(String keyword);       // LIKE %keyword%
    List<User> findByEmailStartingWith(String prefix);     // LIKE prefix%

    // Null checks
    List<User> findByPhoneIsNull();
    List<User> findByPhoneIsNotNull();

    // Ordered results
    List<User> findByStatusOrderByCreatedAtDesc(String status);
}
3

@Query Annotation

@Query overrides the derived query with explicit JPQL or native SQL; use @Modifying with @Transactional for bulk UPDATE or DELETE repository methods.

  • @Query accepts JPQL (default) or SQL (nativeQuery=true) inline on the repository method
  • Use @Param to bind named parameters; positional ?1 also works but is less readable
  • @Modifying is required for UPDATE/DELETE @Query methods — omitting it throws an exception
  • @Transactional must be present on the method or the calling service for @Modifying queries
  • clearAutomatically=true clears the L1 cache after bulk DML — prevents reading stale entities
  • JPQL constructor expression: SELECT new com.example.Dto(a, b) FROM Entity — maps to DTO constructor
Java — @Query with JPQL and projections
public interface OrderRepository extends JpaRepository<Order, Long> {

    // JPQL — entity alias, relationship traversal
    @Query("SELECT o FROM Order o JOIN FETCH o.items WHERE o.customerId = :cid AND o.status = :status")
    List<Order> findWithItemsByCustomer(@Param("cid") Long customerId,
                                        @Param("status") String status);

    // Projection with JPQL constructor expression
    @Query("SELECT new com.example.dto.OrderSummary(o.id, o.status, o.totalAmount) " +
           "FROM Order o WHERE o.createdAt >= :since")
    List<OrderSummary> findSummariesSince(@Param("since") LocalDateTime since);

    // Positional parameters (less readable but shorter)
    @Query("SELECT o FROM Order o WHERE o.status = ?1 ORDER BY o.createdAt DESC")
    List<Order> findByStatus(String status);
}
4

Specifications & Predicates

JpaSpecificationExecutor lets you compose dynamic queries using Specification objects (reusable Criteria API predicates), ideal for complex filtered search endpoints.

  • Specification<T> wraps JPA Criteria API — returns a Predicate from Root, CriteriaQuery, CriteriaBuilder.
  • Extend JpaSpecificationExecutor<T> on the repository to get findAll(Specification, Pageable).
  • Compose with .and(), .or(), .not() — start with Specification.where(null) as the base.
  • Return cb.conjunction() (always true) when the filter argument is null — safe skip.
  • Add root.join() inside a Specification to filter on related entity fields.
  • Call query.distinct(true) when joining a collection to prevent duplicate result rows.
Java — Specification factory methods
// Repository — extend JpaSpecificationExecutor
public interface OrderRepository extends
        JpaRepository<Order, Long>,
        JpaSpecificationExecutor<Order> {  // adds findAll(Specification, Pageable)
}

// Specification factory class
public class OrderSpecs {

    public static Specification<Order> hasStatus(OrderStatus status) {
        return (root, query, cb) ->
            status == null ? cb.conjunction()           // no filter if null
                           : cb.equal(root.get("status"), status);
    }

    public static Specification<Order> forCustomer(Long customerId) {
        return (root, query, cb) ->
            customerId == null ? cb.conjunction()
                               : cb.equal(root.get("customerId"), customerId);
    }

    public static Specification<Order> createdAfter(LocalDate from) {
        return (root, query, cb) ->
            from == null ? cb.conjunction()
                         : cb.greaterThanOrEqualTo(root.get("createdAt"),
                                                   from.atStartOfDay());
    }

    public static Specification<Order> totalBetween(BigDecimal min, BigDecimal max) {
        return (root, query, cb) -> {
            if (min == null && max == null) return cb.conjunction();
            if (min == null) return cb.lessThanOrEqualTo(root.get("total"), max);
            if (max == null) return cb.greaterThanOrEqualTo(root.get("total"), min);
            return cb.between(root.get("total"), min, max);
        };
    }
}
5

Pagination & Sorting in Spring Data

Pass Pageable to repository methods; Spring Data returns Page<T> with content, total elements, and page metadata; Sort can be constructed dynamically from user input.

  • Page<T> runs two queries (data + COUNT); Slice<T> runs only the data query (use for infinite scroll)
  • PageRequest.of(page, size, sort) is 0-based — page 0 is the first page
  • Never pass raw user input to Sort.by() — validate against a whitelist first
  • JOIN FETCH or @EntityGraph with Pageable causes in-memory pagination — use keyset pagination instead
  • Slice.hasNext() checks whether a next page exists without knowing the total count
  • Sort.by("field").descending() and Sort.by(Direction, "field") are both valid
Java — Page<T> with PageRequest
public interface OrderRepository extends JpaRepository<Order, Long> {

    // Simple paginated query
    Page<Order> findByStatus(String status, Pageable pageable);

    // Paginated with @Query
    @Query("SELECT o FROM Order o WHERE o.customerId = :cid ORDER BY o.createdAt DESC")
    Page<Order> findByCustomer(@Param("cid") Long customerId, Pageable pageable);
}

// Service — page 0, size 20, sort by createdAt descending
Pageable pageable = PageRequest.of(0, 20, Sort.by(Sort.Direction.DESC, "createdAt"));
Page<Order> page = orderRepository.findByStatus("PLACED", pageable);

log.info("Page {}/{}, {} total orders",
    page.getNumber(), page.getTotalPages(), page.getTotalElements());
page.getContent().forEach(o -> process(o));
Learn this free with Aria, your AI tutor → AiCanCode.org/learn/hibernate