Files
2025-11-29 18:28:34 +08:00

27 KiB

Spring Data JPA - Code Examples

Example 1: Simple CRUD Application

Entity Classes

@Entity
@Table(name = "categories")
public class Category {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(nullable = false, length = 100)
    private String name;
    
    @Column(columnDefinition = "TEXT")
    private String description;
    
    @OneToMany(mappedBy = "category", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Product> products = new ArrayList<>();
    
    public Category() {}
    
    public Category(String name) {
        this.name = name;
    }
    
    // getters, setters, equals, hashCode
}

@Entity
@Table(name = "products")
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(nullable = false, length = 255)
    private String name;
    
    @Column(precision = 10, scale = 2)
    private BigDecimal price;
    
    @Column(columnDefinition = "INT DEFAULT 0")
    private Integer stock;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "category_id", nullable = false)
    private Category category;
    
    @CreatedDate
    @Column(nullable = false, updatable = false)
    private LocalDateTime createdAt;
    
    @LastModifiedDate
    private LocalDateTime updatedAt;
    
    public Product() {}
    
    public Product(String name, BigDecimal price, Category category) {
        this.name = name;
        this.price = price;
        this.category = category;
    }
    
    // getters, setters
}

Repository Interfaces

@Repository
public interface CategoryRepository extends JpaRepository<Category, Long> {
    Optional<Category> findByName(String name);
    boolean existsByName(String name);
}

@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
    List<Product> findByCategory(Category category);
    List<Product> findByCategoryAndPriceGreaterThan(Category category, BigDecimal price);
    Page<Product> findByNameContainingIgnoreCase(String name, Pageable pageable);
    
    @Query("SELECT p FROM Product p WHERE p.stock = 0 ORDER BY p.updatedAt DESC")
    List<Product> findOutOfStockProducts();
    
    @Query("SELECT p FROM Product p WHERE p.price BETWEEN :minPrice AND :maxPrice")
    List<Product> findByPriceRange(
        @Param("minPrice") BigDecimal minPrice,
        @Param("maxPrice") BigDecimal maxPrice
    );
}

Service Layer

@Service
@Transactional
public class ProductService {
    private final ProductRepository productRepository;
    private final CategoryRepository categoryRepository;
    
    public ProductService(ProductRepository productRepository,
                         CategoryRepository categoryRepository) {
        this.productRepository = productRepository;
        this.categoryRepository = categoryRepository;
    }
    
    @Transactional(readOnly = true)
    public Page<Product> searchProducts(String query, int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
        return productRepository.findByNameContainingIgnoreCase(query, pageable);
    }
    
    @Transactional(readOnly = true)
    public List<Product> getProductsByCategory(Long categoryId) {
        Category category = categoryRepository.findById(categoryId)
            .orElseThrow(() -> new CategoryNotFoundException(categoryId));
        return productRepository.findByCategory(category);
    }
    
    @Transactional(readOnly = true)
    public List<Product> getExpensiveProducts(Long categoryId, BigDecimal minPrice) {
        Category category = categoryRepository.findById(categoryId)
            .orElseThrow(() -> new CategoryNotFoundException(categoryId));
        return productRepository.findByCategoryAndPriceGreaterThan(category, minPrice);
    }
    
    public Product createProduct(CreateProductRequest request) {
        Category category = categoryRepository.findById(request.categoryId())
            .orElseThrow(() -> new CategoryNotFoundException(request.categoryId()));
        
        Product product = new Product(request.name(), request.price(), category);
        return productRepository.save(product);
    }
    
    public Product updateProduct(Long id, UpdateProductRequest request) {
        Product product = productRepository.findById(id)
            .orElseThrow(() -> new ProductNotFoundException(id));
        
        product.setName(request.name());
        product.setPrice(request.price());
        
        return productRepository.save(product);
    }
    
    public void deleteProduct(Long id) {
        productRepository.deleteById(id);
    }
    
    @Transactional(readOnly = true)
    public List<Product> getOutOfStockProducts() {
        return productRepository.findOutOfStockProducts();
    }
}

record CreateProductRequest(String name, BigDecimal price, Long categoryId) {}
record UpdateProductRequest(String name, BigDecimal price) {}

Example 2: Complex Query with Auditing

@Entity
@Table(name = "orders")
@EntityListeners(AuditingEntityListener.class)
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String orderNumber;
    private String status;  // PENDING, PROCESSING, SHIPPED, DELIVERED, CANCELLED
    
    @Column(precision = 12, scale = 2)
    private BigDecimal totalAmount;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "customer_id", nullable = false)
    private Customer customer;
    
    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<OrderItem> items = new ArrayList<>();
    
    @CreatedDate
    @Column(nullable = false, updatable = false)
    private LocalDateTime createdAt;
    
    @LastModifiedDate
    private LocalDateTime modifiedAt;
    
    @CreatedBy
    @Column(nullable = false, updatable = false)
    private String createdBy;
    
    @LastModifiedBy
    private String modifiedBy;
    
    // getters, setters, methods
}

@Entity
@Table(name = "order_items")
public class OrderItem {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "order_id", nullable = false)
    private Order order;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "product_id", nullable = false)
    private Product product;
    
    private Integer quantity;
    
    @Column(precision = 10, scale = 2)
    private BigDecimal unitPrice;
    
    // getters, setters
}

@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
    @Query("""
        SELECT o FROM Order o
        JOIN FETCH o.items i
        JOIN FETCH o.customer
        WHERE o.status = :status
        ORDER BY o.createdAt DESC
        """)
    List<Order> findOrdersWithItems(@Param("status") String status);
    
    @Query("""
        SELECT o FROM Order o
        WHERE o.customer.id = :customerId
        AND o.createdAt BETWEEN :startDate AND :endDate
        """)
    List<Order> findCustomerOrdersByDateRange(
        @Param("customerId") Long customerId,
        @Param("startDate") LocalDateTime startDate,
        @Param("endDate") LocalDateTime endDate
    );
    
    @Query(value = """
        SELECT o.id, o.order_number, SUM(oi.quantity * oi.unit_price) as total
        FROM orders o
        JOIN order_items oi ON o.id = oi.order_id
        WHERE o.status = :status
        GROUP BY o.id
        HAVING total > :minAmount
        """, nativeQuery = true)
    List<Map<String, Object>> findHighValueOrdersByStatus(
        @Param("status") String status,
        @Param("minAmount") BigDecimal minAmount
    );
    
    @Modifying
    @Transactional
    @Query("UPDATE Order o SET o.status = :newStatus WHERE o.id = :orderId")
    void updateOrderStatus(
        @Param("orderId") Long orderId,
        @Param("newStatus") String newStatus
    );
    
    @Modifying
    @Transactional
    @Query("""
        DELETE FROM Order o
        WHERE o.status = :status
        AND o.createdAt < :cutoffDate
        """)
    int deleteOldOrders(
        @Param("status") String status,
        @Param("cutoffDate") LocalDateTime cutoffDate
    );
}

@Service
@Transactional
public class OrderService {
    private final OrderRepository orderRepository;
    
    @Transactional(readOnly = true)
    public List<Order> getPendingOrders() {
        return orderRepository.findOrdersWithItems("PENDING");
    }
    
    @Transactional(readOnly = true)
    public List<Order> getCustomerOrders(Long customerId, LocalDateTime from, LocalDateTime to) {
        return orderRepository.findCustomerOrdersByDateRange(customerId, from, to);
    }
    
    @Transactional(readOnly = true)
    public List<Map<String, Object>> getHighValueOrders(BigDecimal minAmount) {
        return orderRepository.findHighValueOrdersByStatus("COMPLETED", minAmount);
    }
    
    public void processOrder(Long orderId) {
        orderRepository.updateOrderStatus(orderId, "PROCESSING");
    }
    
    public int cleanupCancelledOrders(LocalDateTime before) {
        return orderRepository.deleteOldOrders("CANCELLED", before);
    }
}

Example 3: Many-to-Many Relationship

@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String username;
    private String email;
    
    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    @JoinTable(
        name = "user_roles",
        joinColumns = @JoinColumn(name = "user_id"),
        inverseJoinColumns = @JoinColumn(name = "role_id")
    )
    private Set<Role> roles = new HashSet<>();
    
    public void addRole(Role role) {
        this.roles.add(role);
        role.getUsers().add(this);
    }
    
    public void removeRole(Role role) {
        this.roles.remove(role);
        role.getUsers().remove(this);
    }
}

@Entity
@Table(name = "roles")
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String name;
    private String description;
    
    @ManyToMany(mappedBy = "roles")
    private Set<User> users = new HashSet<>();
}

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    @Query("""
        SELECT DISTINCT u FROM User u
        LEFT JOIN FETCH u.roles
        WHERE u.id = :id
        """)
    Optional<User> findByIdWithRoles(@Param("id") Long id);
    
    @Query("""
        SELECT u FROM User u
        JOIN u.roles r
        WHERE r.name = :roleName
        """)
    List<User> findUsersByRole(@Param("roleName") String roleName);
}

@Service
public class UserManagementService {
    private final UserRepository userRepository;
    private final RoleRepository roleRepository;
    
    @Transactional
    public void assignRoleToUser(Long userId, Long roleId) {
        User user = userRepository.findById(userId)
            .orElseThrow(() -> new UserNotFoundException(userId));
        Role role = roleRepository.findById(roleId)
            .orElseThrow(() -> new RoleNotFoundException(roleId));
        
        user.addRole(role);
        userRepository.save(user);
    }
    
    @Transactional
    public void removeRoleFromUser(Long userId, Long roleId) {
        User user = userRepository.findByIdWithRoles(userId)
            .orElseThrow(() -> new UserNotFoundException(userId));
        Role role = user.getRoles().stream()
            .filter(r -> r.getId().equals(roleId))
            .findFirst()
            .orElseThrow(() -> new RoleNotFoundException(roleId));
        
        user.removeRole(role);
        userRepository.save(user);
    }
}

Example 4: Pagination with Dynamic Filtering

public record ProductFilter(
    String name,
    Long categoryId,
    BigDecimal minPrice,
    BigDecimal maxPrice,
    Boolean inStock
) {}

@Repository
public interface ProductRepository extends JpaRepository<Product, Long>, ProductCustomRepository {
}

public interface ProductCustomRepository {
    Page<Product> findByFilter(ProductFilter filter, Pageable pageable);
}

@Repository
public class ProductCustomRepositoryImpl implements ProductCustomRepository {
    @PersistenceContext
    private EntityManager entityManager;
    
    @Override
    public Page<Product> findByFilter(ProductFilter filter, Pageable pageable) {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<Product> cq = cb.createQuery(Product.class);
        Root<Product> root = cq.from(Product.class);
        
        List<Predicate> predicates = new ArrayList<>();
        
        if (filter.name() != null) {
            predicates.add(cb.like(
                cb.lower(root.get("name")),
                "%" + filter.name().toLowerCase() + "%"
            ));
        }
        
        if (filter.categoryId() != null) {
            predicates.add(cb.equal(root.get("category").get("id"), filter.categoryId()));
        }
        
        if (filter.minPrice() != null) {
            predicates.add(cb.greaterThanOrEqualTo(root.get("price"), filter.minPrice()));
        }
        
        if (filter.maxPrice() != null) {
            predicates.add(cb.lessThanOrEqualTo(root.get("price"), filter.maxPrice()));
        }
        
        if (filter.inStock() != null && filter.inStock()) {
            predicates.add(cb.greaterThan(root.get("stock"), 0));
        }
        
        cq.where(cb.and(predicates.toArray(new Predicate[0])));
        cq.orderBy(cb.desc(root.get("createdAt")));
        
        TypedQuery<Product> query = entityManager.createQuery(cq);
        query.setFirstResult((int) pageable.getOffset());
        query.setMaxResults(pageable.getPageSize());
        
        List<Product> results = query.getResultList();
        long total = getTotal(filter);
        
        return new PageImpl<>(results, pageable, total);
    }
    
    private long getTotal(ProductFilter filter) {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<Long> cq = cb.createQuery(Long.class);
        Root<Product> root = cq.from(Product.class);
        
        List<Predicate> predicates = new ArrayList<>();
        // Add same predicates as above
        
        cq.select(cb.count(root));
        cq.where(cb.and(predicates.toArray(new Predicate[0])));
        
        return entityManager.createQuery(cq).getSingleResult();
    }
}

@Service
public class ProductSearchService {
    private final ProductRepository productRepository;
    
    @Transactional(readOnly = true)
    public Page<Product> search(ProductFilter filter, int page, int size) {
        Sort sort = Sort.by("createdAt").descending();
        Pageable pageable = PageRequest.of(page, size, sort);
        return productRepository.findByFilter(filter, pageable);
    }
}

Example 5: Batch Operations

@Service
@Transactional
public class BatchOperationService {
    private final ProductRepository productRepository;
    private static final int BATCH_SIZE = 50;
    
    public void importProducts(List<ProductDTO> products) {
        for (int i = 0; i < products.size(); i++) {
            Product product = mapToEntity(products.get(i));
            productRepository.save(product);
            
            if ((i + 1) % BATCH_SIZE == 0) {
                productRepository.flush();
            }
        }
    }
    
    public void importProductsBatch(List<ProductDTO> products) {
        List<Product> entities = products.stream()
            .map(this::mapToEntity)
            .collect(Collectors.toList());
        
        productRepository.saveAll(entities);
        productRepository.flush();
    }
    
    public long deleteOldProducts(LocalDateTime cutoffDate) {
        return productRepository.deleteByCreatedAtBefore(cutoffDate);
    }
    
    public int bulkUpdatePrices(List<Long> productIds, BigDecimal newPrice) {
        return productRepository.updatePriceForIds(productIds, newPrice);
    }
    
    private Product mapToEntity(ProductDTO dto) {
        Product product = new Product();
        product.setName(dto.name());
        product.setPrice(dto.price());
        return product;
    }
}

@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
    long deleteByCreatedAtBefore(LocalDateTime date);
    
    @Modifying
    @Transactional
    @Query("""
        UPDATE Product p
        SET p.price = :newPrice
        WHERE p.id IN :ids
        """)
    int updatePriceForIds(
        @Param("ids") List<Long> ids,
        @Param("newPrice") BigDecimal newPrice
    );
}

Example 6: Entity Graph for Eager Loading

@Entity
public class Post {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String title;
    private String content;
    
    @ManyToOne(fetch = FetchType.LAZY)
    private User author;
    
    @OneToMany(mappedBy = "post", fetch = FetchType.LAZY)
    private List<Comment> comments = new ArrayList<>();
}

@Entity
public class Comment {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String text;
    
    @ManyToOne(fetch = FetchType.LAZY)
    private Post post;
    
    @ManyToOne(fetch = FetchType.LAZY)
    private User author;
}

@Repository
public interface PostRepository extends JpaRepository<Post, Long> {
    // Using EntityGraph annotation
    @EntityGraph(attributePaths = {"author", "comments"})
    Optional<Post> findById(Long id);
    
    @EntityGraph(attributePaths = {"author", "comments", "comments.author"})
    List<Post> findAll();
    
    // Using @Query with JOIN FETCH
    @Query("""
        SELECT DISTINCT p FROM Post p
        JOIN FETCH p.author
        JOIN FETCH p.comments c
        JOIN FETCH c.author
        WHERE p.id = :id
        """)
    Optional<Post> findByIdWithDetails(@Param("id") Long id);
}

@Service
@Transactional(readOnly = true)
public class PostService {
    private final PostRepository postRepository;
    
    public Post getPostWithComments(Long id) {
        return postRepository.findById(id)
            .orElseThrow(() -> new PostNotFoundException(id));
    }
}

Example 7: Transaction Propagation

@Service
public class OrderProcessingService {
    private final OrderRepository orderRepository;
    private final PaymentService paymentService;
    private final InventoryService inventoryService;
    
    @Transactional
    public void processOrder(Long orderId) throws PaymentException {
        Order order = orderRepository.findById(orderId)
            .orElseThrow();
        
        try {
            processPayment(order);
            updateInventory(order);
            order.setStatus("COMPLETED");
            orderRepository.save(order);
        } catch (PaymentException e) {
            order.setStatus("PAYMENT_FAILED");
            orderRepository.save(order);
            throw e;
        }
    }
    
    @Transactional(propagation = Propagation.REQUIRED)
    private void processPayment(Order order) throws PaymentException {
        paymentService.charge(order.getCustomer(), order.getTotalAmount());
    }
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    private void updateInventory(Order order) {
        order.getItems().forEach(item -> {
            inventoryService.decreaseStock(item.getProduct().getId(), item.getQuantity());
        });
    }
}

Example 8: UUID Primary Keys

@Entity
@Table(name = "articles", indexes = {
    @Index(name = "idx_author_created", columnList = "author_id, created_date DESC"),
    @Index(name = "idx_status", columnList = "status")
})
public class Article {
    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    private UUID id;
    
    private String title;
    private String content;
    private String status;  // DRAFT, PUBLISHED
    
    private LocalDateTime createdDate;
    private LocalDateTime publishedDate;
    
    @ManyToOne
    private User author;
}

@Entity
@Table(name = "users", indexes = {
    @Index(name = "idx_email", columnList = "email", unique = true),
    @Index(name = "idx_username", columnList = "username", unique = true)
})
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    private UUID id;
    
    @Column(unique = true, length = 100)
    private String email;
    
    @Column(unique = true, length = 100)
    private String username;
    
    private String firstName;
    private String lastName;
}

@Repository
public interface ArticleRepository extends JpaRepository<Article, UUID> {
    // Indexes support these queries efficiently
    List<Article> findByStatusOrderByPublishedDateDesc(String status);
    List<Article> findByAuthorOrderByCreatedDateDesc(User author);
    Page<Article> findByStatusAndPublishedDateAfter(
        String status,
        LocalDateTime date,
        Pageable pageable
    );
}

@Repository
public interface UserRepository extends JpaRepository<User, UUID> {
    Optional<User> findByEmail(String email);
    Optional<User> findByUsername(String username);
}

@Service
@Transactional
public class ArticleService {
    private final ArticleRepository articleRepository;
    private final UserRepository userRepository;
    
    public Article createArticle(CreateArticleRequest request, UUID authorId) {
        User author = userRepository.findById(authorId)
            .orElseThrow(() -> new UserNotFoundException(authorId));
        
        Article article = new Article();
        article.setTitle(request.title());
        article.setContent(request.content());
        article.setStatus("DRAFT");
        article.setCreatedDate(LocalDateTime.now());
        article.setAuthor(author);
        
        return articleRepository.save(article);  // UUID generated automatically
    }
    
    @Transactional(readOnly = true)
    public Page<Article> getPublishedArticles(int page, int size) {
        Pageable pageable = PageRequest.of(page, size,
            Sort.by("publishedDate").descending());
        return articleRepository.findByStatusAndPublishedDateAfter(
            "PUBLISHED",
            LocalDateTime.now().minusDays(30),
            pageable
        );
    }
    
    @Transactional(readOnly = true)
    public Article getArticle(UUID id) {
        return articleRepository.findById(id)
            .orElseThrow(() -> new ArticleNotFoundException(id));
    }
}

record CreateArticleRequest(String title, String content) {}

Example 9: Index Optimization

@Entity
@Table(name = "orders", indexes = {
    // Single column index
    @Index(name = "idx_status", columnList = "status"),
    
    // Composite index for common query pattern
    @Index(name = "idx_customer_date", columnList = "customer_id, created_date DESC"),
    
    // For date range queries
    @Index(name = "idx_created_date", columnList = "created_date DESC"),
    
    // Unique index
    @Index(name = "idx_order_number", columnList = "order_number", unique = true),
    
    // Multi-column ordering
    @Index(name = "idx_status_amount", columnList = "status ASC, total_amount DESC")
})
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(unique = true, length = 50)
    private String orderNumber;
    
    @Column(length = 20)
    private String status;  // PENDING, PROCESSING, SHIPPED, DELIVERED
    
    private LocalDateTime createdDate;
    
    @Column(precision = 12, scale = 2)
    private BigDecimal totalAmount;
    
    @ManyToOne
    @JoinColumn(name = "customer_id")
    private Customer customer;
}

@Entity
@Table(name = "order_items", indexes = {
    // Index on foreign key for JOIN performance
    @Index(name = "idx_order_id", columnList = "order_id"),
    
    // Composite index for finding items by order and status
    @Index(name = "idx_order_status", columnList = "order_id, status"),
    
    // Index on product foreign key
    @Index(name = "idx_product_id", columnList = "product_id")
})
public class OrderItem {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @ManyToOne
    @JoinColumn(name = "order_id")
    private Order order;
    
    @ManyToOne
    @JoinColumn(name = "product_id")
    private Product product;
    
    private Integer quantity;
    private String status;
}

@Entity
@Table(name = "customers", indexes = {
    @Index(name = "idx_email", columnList = "email", unique = true),
    @Index(name = "idx_country_city", columnList = "country, city")
})
public class Customer {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String firstName;
    private String lastName;
    
    @Column(unique = true)
    private String email;
    
    private String country;
    private String city;
}

@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
    // Uses idx_status
    List<Order> findByStatus(String status);
    
    // Uses idx_customer_date
    List<Order> findByCustomerOrderByCreatedDateDesc(
        Customer customer,
        Pageable pageable
    );
    
    // Uses idx_created_date
    Page<Order> findByCreatedDateAfterOrderByCreatedDateDesc(
        LocalDateTime date,
        Pageable pageable
    );
    
    // Uses idx_status_amount
    List<Order> findByStatusOrderByTotalAmountDesc(String status);
    
    // Custom query using indexes
    @Query("""
        SELECT o FROM Order o
        WHERE o.status = :status
        AND o.createdDate BETWEEN :start AND :end
        ORDER BY o.totalAmount DESC
        """)
    List<Order> findHighValueOrdersInPeriod(
        @Param("status") String status,
        @Param("start") LocalDateTime start,
        @Param("end") LocalDateTime end
    );
}

@Repository
public interface CustomerRepository extends JpaRepository<Customer, Long> {
    // Uses idx_email
    Optional<Customer> findByEmail(String email);
    
    // Uses idx_country_city
    List<Customer> findByCountryAndCity(String country, String city);
}

@Service
@Transactional(readOnly = true)
public class OrderAnalyticsService {
    private final OrderRepository orderRepository;
    
    public Page<Order> getRecentOrders(int page, int size) {
        LocalDateTime weekAgo = LocalDateTime.now().minusDays(7);
        Pageable pageable = PageRequest.of(page, size);
        return orderRepository.findByCreatedDateAfterOrderByCreatedDateDesc(
            weekAgo,
            pageable
        );  // Uses idx_created_date
    }
    
    public List<Order> getPendingOrders() {
        return orderRepository.findByStatus("PENDING");  // Uses idx_status
    }
    
    public List<Order> getHighValueOrders(LocalDateTime from, LocalDateTime to) {
        return orderRepository.findHighValueOrdersInPeriod(
            "COMPLETED",
            from,
            to
        );  // Uses idx_status_amount
    }
    
    @Transactional
    public void optimizeIndexUsage() {
        // These queries benefit from composite indexes
        List<Order> customerOrders = orderRepository.findByCustomerOrderByCreatedDateDesc(
            new Customer(),
            PageRequest.of(0, 50)
        );  // Uses idx_customer_date
    }
}

These examples demonstrate real-world usage patterns for Spring Data JPA, from simple CRUD operations to complex scenarios involving relationships, auditing, pagination, batch processing, UUID keys, and index optimization.