# 026 Spring D0f5f765

> Spring Data JPA - Reference Documentation

- Skill: `tools-only/026-spring-d0f5f765` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add tools-only/026-spring-d0f5f765`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tools-only/026-spring-d0f5f765/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Docs & Writing
- Author: tools-only (https://skillmd.com/u/tools-only)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/tools-only/026-spring-d0f5f765

---

# Spring Data JPA - Reference Documentation

## Repository Interfaces

### Repository Hierarchy

Spring Data JPA provides a clear inheritance hierarchy for repositories:

```
Repository (marker interface)
├── CrudRepository (basic CRUD operations)
│   ├── PagingAndSortingRepository (add pagination/sorting)
│   │   └── JpaRepository (JPA-specific operations)
│   └── ListCrudRepository (Spring Data 3+)
└── Reactive variants (ReactiveCrudRepository, etc.)
```

### CrudRepository

Basic CRUD operations for all entities.

```java
public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {
    // CREATE/UPDATE
    <S extends T> S save(S entity);                    // Save or update entity
    <S extends T> Iterable<S> saveAll(Iterable<S> entities); // Save multiple entities

    // READ
    Optional<T> findById(ID id);                        // Find by primary key
    T getById(ID id);                                   // Returns proxy, never null
    T getReferenceById(ID id);                          // Like getById
    boolean existsById(ID id);                         // Check existence
    Iterable<T> findAll();                             // Find all entities
    Iterable<T> findAllById(Iterable<ID> ids);          // Find by multiple IDs

    // AGGREGATE
    long count();                                      // Count entities

    // DELETE
    void deleteById(ID id);                            // Delete by ID
    void delete(T entity);                             // Delete entity
    void deleteAllById(Iterable<? extends ID> ids);    // Delete by multiple IDs
    void deleteAll(Iterable<? extends T> entities);    // Delete multiple entities
    void deleteAll();                                  // Delete all entities
}
```

### PagingAndSortingRepository

Extends CrudRepository with pagination and sorting capabilities.

```java
public interface PagingAndSortingRepository<T, ID extends Serializable>
    extends CrudRepository<T, ID> {

    // Sorting
    Iterable<T> findAll(Sort sort);                   // Find all with sorting

    // Pagination
    Page<T> findAll(Pageable pageable);                // Find all with pagination
}
```

### JpaRepository

The most comprehensive interface extending PagingAndSortingRepository with JPA-specific operations.

```java
public interface JpaRepository<T, ID extends Serializable>
    extends PagingAndSortingRepository<T, ID> {

    // Enhanced read operations
    List<T> findAll();                                 // Returns List instead of Iterable
    List<T> findAllById(Iterable<ID> ids);            // Returns List instead of Iterable
    List<T> findAll(Sort sort);                       // Returns List instead of Iterable
    Page<T> findAll(Pageable pageable);               // Returns Page

    // Batch operations
    <S extends T> List<S> saveAll(Iterable<S> entities); // Save multiple with return

    // Batch delete operations
    void deleteInBatch(Iterable<T> entities);          // Delete without flushing
    void deleteAllInBatch(Iterable<ID> ids);          // Delete by IDs without flushing
    void deleteAllInBatch();                           // Delete all without flushing

    // Flush operations
    void flush();                                      // Flush to database
    <S extends T> S saveAndFlush(S entity);           // Save and immediately flush
    <S extends T> List<S> saveAllAndFlush(Iterable<S> entities); // Save all and flush
}
```

## Query Methods

### Derived Query Methods

Spring Data automatically generates queries from method names following naming conventions.

#### Simple Lookups
```java
Optional<User> findByEmail(String email);             // Single result
List<User> findByUsername(String username);           // Multiple results
User findFirstByEmail(String email);                // First result
User findTopByOrderByAgeDesc();                      // Top by age descending
```

#### Conditional Operators
```java
// Equality
List<User> findByStatus(String status);
List<User> findByStatusNot(String status);

// Comparison
List<User> findByAgeGreaterThan(Integer age);
List<User> findByAgeLessThanEqual(Integer age);
List<User> findByAgeBetween(Integer min, Integer max);
List<User> findByAgeGreaterThanEqual(25);           // Static comparison

// Null/Empty checks
List<User> findByEmailIsNull();
List<User> findByEmailIsNotNull();
List<User> findByEmailNotEmpty();

// Boolean properties
List<User> findByActiveTrue();
List<User>ByEmailActiveFalse();
```

#### String Operations
```java
// Pattern matching
List<User> findByEmailContaining(String pattern);    // LIKE '%pattern%'
List<User> findByEmailStartsWith(String prefix);     // LIKE 'prefix%'
List<User> findByEmailEndsWith(String suffix);       // LIKE '%suffix'
List<User> findByEmailLike(String pattern);          // Exact LIKE pattern

// Case sensitivity
List<User> findByEmailIgnoreCase(String email);
```

#### Date and Time Operations
```java
// After/Before
List<Order> findByOrderDateAfter(LocalDate date);
List<Order> findByCreatedDateBefore(LocalDateTime dateTime);

// Between
List<Order> findByOrderDateBetween(LocalDate start, LocalDate end);

// Range queries
List<Order> findByTotalPriceGreaterThan(BigDecimal min);
List<Order> findByTotalPriceBetween(BigDecimal min, BigDecimal max);

// Current date comparisons
List<Order> findByCreatedDateBefore(LocalDateTime.now().minusDays(7));
```

#### Ordering
```java
// Simple ordering
List<User> findByStatusOrderByCreatedDateDesc(String status);
List<User> findAllByOrderByLastNameAsc();

// Multiple sort criteria
List<Product> findByCategoryOrderByPriceAscNameDesc(String category);

// Dynamic sorting
List<User> findAll(Sort.by("lastName").ascending());
List<User> findByActiveTrue(Sort.by("createdDate").descending());
```

#### Pagination Integration
```java
Page<User> findByStatus(String status, Pageable pageable);
Slice<User> findByActiveTrue(Pageable pageable);     // No total count
List<User> findTop10ByOrderByCreatedDateDesc();      // Fixed limit
```

#### Delete Operations
```java
// Delete with return count
long deleteByEmail(String email);
long deleteByStatusAndAge(String status, Integer age);

// Delete entities
void deleteByStatus(String status);                  // Bulk delete
```

### Custom Queries with @Query

#### JPQL Queries
Use Java Persistence Query Language for complex queries.

```java
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
    // Basic query with named parameters
    @Query("SELECT o FROM Order o WHERE o.status = :status AND o.totalPrice > :minPrice")
    List<Order> findActiveOrdersAbovePrice(
        @Param("status") String status,
        @Param("minPrice") BigDecimal minPrice
    );

    // Query with JOIN FETCH to avoid N+1 problem
    @Query("SELECT o FROM Order o JOIN FETCH o.items WHERE o.customerId = :customerId")
    List<Order> findOrdersWithItems(@Param("customerId") Long customerId);

    // Aggregate function
    @Query("SELECT COUNT(o) FROM Order o WHERE o.status = 'COMPLETED'")
    long countCompletedOrders();

    // IN clause
    @Query("SELECT o FROM Order o WHERE o.status IN :statuses")
    List<Order> findByStatuses(@Param("statuses") List<String> statuses);

    // EXISTS clause
    @Query("SELECT o FROM Order o WHERE EXISTS (SELECT 1 FROM o.items i WHERE i.quantity = 0)")
    List<Order> findOrdersWithZeroQuantityItems();
}
```

#### Native SQL Queries
Use native SQL for database-specific queries.

```java
@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
    // Simple native query
    @Query(value = "SELECT * FROM products WHERE category = :category AND price < :maxPrice",
           nativeQuery = true)
    List<Product> findProductsByCategory(
        @Param("category") String category,
        @Param("maxPrice") BigDecimal maxPrice
    );

    // Native query with mapping
    @Query(value = """
        SELECT p.id, p.name, p.price, c.name as category_name
        FROM products p
        JOIN categories c ON p.category_id = c.id
        WHERE p.price > :minPrice
        ORDER BY p.price DESC
        """, nativeQuery = true)
    @QueryResults projection = ProductSummary.class;  // Custom projection
    List<ProductSummary> findExpensiveProductSummaries(@Param("minPrice") BigDecimal minPrice);
}
```

#### Modifying Queries
Use `@Modifying` for INSERT, UPDATE, DELETE operations.

```java
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    @Modifying
    @Transactional
    @Query("UPDATE User u SET u.lastLoginDate = :now WHERE u.id = :userId")
    void updateLastLoginDate(
        @Param("userId") Long userId,
        @Param("now") LocalDateTime now
    );

    @Modifying
    @Transactional
    @Query("DELETE FROM User u WHERE u.createdDate < :cutoffDate")
    int deleteInactiveUsers(@Param("cutoffDate") LocalDateTime cutoffDate);

    @Modifying
    @Transactional
    @Query(value = "UPDATE users SET status = 'INACTIVE' WHERE last_login < :cutoff",
           nativeQuery = true)
    int deactivateInactiveUsersNative(@Param("cutoff") LocalDateTime cutoff);
}
```

## Entity Relationships

### One-to-One Relationship

#### Foreign Key Approach
```java
@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length = 100)
    private String email;

    @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinColumn(name = "address_id", referencedColumnName = "id")
    private Address address;
}

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

    @Column(nullable = false)
    private String street;
    private String city;
    private String postalCode;

    @OneToOne(mappedBy = "address")
    private User user;
}
```

#### Shared Primary Key Approach
```java
@Entity
@Table(name = "employees")
public class Employee {
    @Id
    private Long id;  // Shared with profile

    @Column(nullable = false)
    private String firstName;

    @OneToOne(mappedBy = "employee", fetch = FetchType.LAZY)
    private EmployeeProfile profile;
}

@Entity
@Table(name = "employee_profiles")
public class EmployeeProfile {
    @Id
    private Long id;  // Same as employee ID

    @Column(length = 500)
    private String bio;

    @OneToOne(fetch = FetchType.LAZY)
    @MapsId  // Maps to employee.id
    @JoinColumn(name = "id")
    private Employee employee;
}
```

### One-to-Many Relationship

```java
@Entity
@Table(name = "categories")
public class Category {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length = 100)
    private String name;

    @OneToMany(mappedBy = "category",
               cascade = CascadeType.ALL,
               orphanRemoval = true,
               fetch = FetchType.LAZY)
    private List<Product> products = new ArrayList<>();

    // Helper methods
    public void addProduct(Product product) {
        products.add(product);
        product.setCategory(this);
    }

    public void removeProduct(Product product) {
        products.remove(product);
        product.setCategory(null);
    }
}

@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;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "category_id", nullable = false)
    private Category category;
}
```

### Many-to-Many Relationship

#### Simple Join Table
```java
@Entity
@Table(name = "students")
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length = 100)
    private String name;

    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    @JoinTable(
        name = "student_course",
        joinColumns = @JoinColumn(name = "student_id"),
        inverseJoinColumns = @JoinColumn(name = "course_id")
    )
    private Set<Course> courses = new HashSet<>();

    public void addCourse(Course course) {
        courses.add(course);
        course.getStudents().add(this);
    }

    public void removeCourse(Course course) {
        courses.remove(course);
        course.getStudents().remove(this);
    }
}

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

    @Column(nullable = false, length = 100)
    private String title;

    @ManyToMany(mappedBy = "courses")
    private Set<Student> students = new HashSet<>();
}
```

#### Join Table with Additional Attributes
```java
@Entity
@Table(name = "enrollments")
public class Enrollment {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    @JoinColumn(name = "student_id", nullable = false)
    private Student student;

    @ManyToOne
    @JoinColumn(name = "course_id", nullable = false)
    private Course course;

    @Column(nullable = false)
    private LocalDateTime enrolledAt;

    @Column(precision = 3, scale = 2)
    private Double grade;  // Can be null

    @Column(length = 20)
    private String status;  // ACTIVE, WITHDRAWN, COMPLETED

    // Composite primary key (alternative approach)
    @EmbeddedId
    private EnrollmentId enrollmentId;

    @ManyToOne
    @JoinColumn(name = "student_id", insertable = false, updatable = false)
    private Student student;

    @ManyToOne
    @JoinColumn(name = "course_id", insertable = false, updatable = false)
    private Course course;
}

@Embeddable
public class EnrollmentId implements Serializable {
    private Long studentId;
    private Long courseId;

    // equals(), hashCode() implementation
}
```

### Bidirectional Relationships

#### Best Practices
```java
@Entity
public class Department {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String name;

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

    // Helper method for consistency
    public void addEmployee(Employee employee) {
        employees.add(employee);
        employee.setDepartment(this);
    }
}

@Entity
public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String name;

    @ManyToOne
    @JoinColumn(name = "department_id", nullable = false)
    private Department department;
}
```

## Pagination and Sorting

### Pagination Basics

```java
@Service
public class ProductService {
    private final ProductRepository repository;

    public Page<Product> getProductsPage(int page, int size) {
        Pageable pageable = PageRequest.of(page, size);
        return repository.findAll(pageable);
    }

    public Page<Product> getProductsWithSorting(int page, int size, String sortField) {
        Pageable pageable = PageRequest.of(page, size, Sort.by(sortField));
        return repository.findAll(pageable);
    }

    public Page<Product> getProductsWithMultiSort(int page, int size) {
        Sort sort = Sort.by("price").ascending()
                      .and(Sort.by("name").ascending());
        Pageable pageable = PageRequest.of(page, size, sort);
        return repository.findAll(pageable);
    }
}
```

### Advanced Pagination

```java
@Service
public class OrderService {
    private final OrderRepository repository;

    public Page<Order> getOrdersByStatus(String status, int page, int size) {
        Pageable pageable = PageRequest.of(page, size,
            Sort.by("createdDate").descending());
        return repository.findByStatus(status, pageable);
    }

    public Slice<Order> getRecentOrders(int page, int size) {
        // Slice doesn't count total elements (more efficient for large datasets)
        Pageable pageable = PageRequest.of(page, size);
        return repository.findByStatus("NEW", pageable);
    }

    public Stream<Order> streamAllOrders() {
        // Stream for large datasets
        return repository.streamAllBy();
    }
}
```

### Custom Pagination Queries

```java
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    @Query("SELECT u FROM User u WHERE u.active = true")
    Page<User> findActiveUsers(Pageable pageable);

    @Query(value = "SELECT * FROM users WHERE created_at > :date",
           nativeQuery = true)
    Page<User> findUsersCreatedAfter(@Param("date") LocalDateTime date, Pageable pageable);
}
```

## Database Auditing

### Spring Data JPA Auditing

#### Configuration
```java
@Configuration
@EnableJpaAuditing(auditorAwareRef = "auditorProvider")
@EnableJpaRepositories(repositoryFactoryBeanClass = CustomRepositoryFactoryBean.class)
public class AuditingConfig {

    @Bean
    public AuditorAware<String> auditorProvider() {
        return () -> Optional.ofNullable(SecurityContextHolder.getContext())
            .map(SecurityContext::getAuthentication)
            .filter(Authentication::isAuthenticated)
            .map(Authentication::getName)
            .or(() -> Optional.of("system"));
    }

    @Bean
    public JpaTransactionManager transactionManager(EntityManagerFactory emf) {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(emf);
        return transactionManager;
    }
}
```

#### Auditing Entities
```java
@Entity
@EntityListeners(AuditingEntityListener.class)
public class BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @CreatedDate
    @Column(nullable = false, updatable = false)
    private LocalDateTime createdDate;

    @LastModifiedDate
    @Column(nullable = false)
    private LocalDateTime lastModifiedDate;

    @CreatedBy
    @Column(nullable = false, updatable = false, length = 50)
    private String createdBy;

    @LastModifiedBy
    @Column(nullable = false, length = 50)
    private String lastModifiedBy;

    @Version
    private Long version;  // Optimistic locking
}

@Entity
public class Product extends BaseEntity {
    @Column(nullable = false, length = 255)
    private String name;

    @Column(precision = 10, scale = 2)
    private BigDecimal price;

    @Enumerated(EnumType.STRING)
    private ProductStatus status;
}

public enum ProductStatus {
    ACTIVE, INACTIVE, DISCONTINUED
}
```

#### Custom Auditor Provider
```java
@Component
public class CustomAuditorProvider implements AuditorAware<String> {

    @Override
    public Optional<String> getCurrentAuditor() {
        // Try to get from security context first
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication != null && authentication.isAuthenticated()) {
            return Optional.of(authentication.getName());
        }

        // Fallback to system user or throw exception
        return Optional.of("system");
    }
}
```

### JPA Lifecycle Callbacks

```java
@Entity
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length = 20)
    private String status;

    @Column(nullable = false)
    private LocalDateTime createdAt;

    private LocalDateTime updatedAt;

    @PrePersist
    protected void onCreate() {
        createdAt = LocalDateTime.now();
        updatedAt = LocalDateTime.now();
        if (status == null) {
            status = "PENDING";
        }
    }

    @PreUpdate
    protected void onUpdate() {
        updatedAt = LocalDateTime.now();
    }

    @PreRemove
    protected void onRemove() {
        // Cleanup logic before deletion
    }

    @PostLoad
    protected void onLoad() {
        // Post-load processing
    }
}
```

### Hibernate Envers for Auditing

#### Configuration
```java
@Configuration
@EnableJpaRepositories(repositoryFactoryBeanClass = EnversRepositoryFactoryBean.class)
@EnableJpaAuditing(auditorAwareRef = "auditorProvider")
public class EnversConfig {

    @Bean
    public AuditorAware<String> auditorProvider() {
        return () -> Optional.ofNullable(SecurityContextHolder.getContext())
            .map(SecurityContext::getAuthentication)
            .filter(Authentication::isAuthenticated)
            .map(Authentication::getName);
    }
}
```

#### Audited Entities
```java
@Entity
@Audited
@RevisionEntity(EntityRevisionListener.class)
public class Document {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length = 200)
    private String title;

    @Column(columnDefinition = "TEXT")
    private String content;

    @Enumerated(EnumType.STRING)
    private DocumentStatus status;

    @ManyToOne
    @JoinColumn(name = "author_id", nullable = false)
    private User author;
}

@RevisionEntity
public class EntityRevisionListener implements RevisionListener {
    @Override
    public void newRevision(Object revisionEntity) {
        EntityRevision revision = (EntityRevision) revisionEntity;
        revision.setRevisionDate(LocalDateTime.now());
        revision.setUsername(SecurityContextHolder.getContext()
            .getAuthentication().getName());
    }
}

@Entity
public class EntityRevision {
    @Id
    @GeneratedValue
    private Integer id;

    private LocalDateTime revisionDate;

    @Column(length = 50)
    private String username;
}
```

#### Repository Usage
```java
public interface DocumentRepository extends JpaRepository<Document, Long>,
                                            JpaEntityRepository<Document, Long, Integer> {

    @Query("SELECT d FROM Document d WHERE d.id = :id AND d.revisionNumber <= :revision")
    Document findHistoricalVersion(@Param("id") Long id, @Param("revision") Integer revision);

    List<Number> findRevisions(Long documentId);

    <T> T findRevision(Class<T> entityClass, Number revision);
}

@Service
public class DocumentAuditService {
    private final DocumentRepository documentRepository;
    private final AuditReader auditReader;

    public List<DocumentRevision> getDocumentHistory(Long documentId) {
        List<Number> revisions = auditReader.getRevisions(Document.class, documentId);
        return revisions.stream()
            .map(revision -> new DocumentRevision(
                revision,
                auditReader.find(Document.class, documentId, revision),
                auditReader.getRevisionDateForRevision(revision)
            ))
            .collect(Collectors.toList());
    }
}
```

## Transactions and Deletion

### Transaction Management

#### Transaction Configuration
```java
@Service
@Transactional
public class OrderService {
    private final OrderRepository orderRepository;
    private final PaymentRepository paymentRepository;
    private final InventoryService inventoryService;

    @Transactional
    public Order createOrder(Order order, List<Payment> payments) {
        // Start transaction
        Order savedOrder = orderRepository.save(order);

        // Process payments
        payments.forEach(payment -> {
            payment.setOrderId(savedOrder.getId());
            paymentRepository.save(payment);
        });

        // Update inventory
        inventoryService.reserveItems(savedOrder.getItems());

        return savedOrder;
    }

    @Transactional(readOnly = true)
    public Order getOrderWithDetails(Long orderId) {
        return orderRepository.findById(orderId)
            .orElseThrow(() -> new EntityNotFoundException("Order not found: " + orderId));
    }

    @Transactional(rollbackFor = {PaymentException.class, InventoryException.class})
    public void processPayment(Long orderId) throws PaymentException {
        Order order = getOrderWithDetails(orderId);

        // Process payment logic
        paymentService.process(order.getPayments());

        // Update order status
        order.setStatus("PROCESSING");
        orderRepository.save(order);

        // This will trigger rollback if thrown
        if (paymentFailed) {
            throw new PaymentException("Payment processing failed");
        }
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void logOrderCreation(Order order) {
        // Always creates new transaction
        auditLogRepository.save(new AuditLog("ORDER_CREATED", order.getId()));
    }
}
```

#### Propagation Types
```java
@Service
public class TransactionalService {

    @Transactional
    public void methodA() {
        methodB();  // Runs in same transaction
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void methodB() {
        // Runs in new transaction
    }

    @Transactional(propagation = Propagation.NESTED)
    public void methodC() {
        // Runs in nested transaction (savepoint)
    }

    @Transactional(propagation = Propagation.SUPPORTS)
    public void methodD() {
        // Runs in existing transaction or none
    }
}
```

#### Isolation Levels
```java
@Service
public class OrderService {

    @Transactional(isolation = Isolation.READ_COMMITTED)
    public Order getOrderWithConsistentData(Long orderId) {
        // Read committed isolation - prevents dirty reads
        return orderRepository.findById(orderId).orElseThrow();
    }

    @Transactional(isolation = Isolation.SERIALIZABLE)
    public void processInventoryUpdate(List<InventoryItem> items) {
        // Serializable isolation - prevents all concurrency issues
        items.forEach(inventoryService::updateStock);
    }

    @Transactional(timeout = 30)  // 30 seconds timeout
    public void processLongRunningTask() {
        // Long-running operation with timeout
        externalApiService.callExternalSystem();
    }
}
```

### Delete Operations

#### Repository Delete Methods
```java
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
    // Basic delete operations
    void deleteById(Long id);
    void delete(Order entity);
    void deleteAllById(Iterable<Long> ids);
    void deleteAll(Iterable<Order> entities);
    void deleteAll();

    // Derived delete queries
    long deleteByStatus(String status);
    long deleteByCreatedDateBefore(LocalDateTime date);
    long deleteByTotalPriceLessThan(BigDecimal threshold);

    // Custom delete query
    @Modifying
    @Transactional
    @Query("DELETE FROM Order o WHERE o.status = :status AND o.totalPrice < :minPrice")
    int deleteOldPendingOrders(
        @Param("status") String status,
        @Param("minPrice") BigDecimal minPrice
    );
}
```

#### Service Layer Delete Operations
```java
@Service
public class OrderService {
    private final OrderRepository orderRepository;
    private final OrderItemRepository orderItemRepository;

    @Transactional
    public void deleteOrder(Long orderId) {
        Order order = orderRepository.findById(orderId)
            .orElseThrow(() -> new OrderNotFoundException(orderId));

        // Delete related items first (or use cascade)
        orderItemRepository.deleteByOrderId(orderId);

        // Delete order
        orderRepository.delete(order);
    }

    @Transactional
    public long cleanupExpiredOrders(LocalDateTime cutoffDate) {
        return orderRepository.deleteByCreatedDateBefore(cutoffDate);
    }

    @Transactional
    public int cleanupOldPendingOrders(BigDecimal minPrice) {
        return orderRepository.deleteOldPendingOrders("PENDING", minPrice);
    }

    @Transactional
    public void batchDeleteOrders(List<Long> orderIds) {
        // Batch delete for better performance
        orderRepository.deleteAllById(orderIds);
    }
}
```

#### Cascade and Orphan Removal
```java
@Entity
@Table(name = "categories")
public class Category {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length = 100)
    private String name;

    @OneToMany(mappedBy = "category",
               cascade = CascadeType.ALL,     // Cascade save/update/delete
               orphanRemoval = true)           // Remove children when removed from collection
    private List<Product> products = new ArrayList<>();

    @OneToMany(mappedBy = "category",
               cascade = CascadeType.PERSIST,  // Only cascade save operations
               orphanRemoval = false)
    private List<Product> inactiveProducts = new ArrayList<>();

    public void addProduct(Product product) {
        products.add(product);
        product.setCategory(this);
    }

    public void removeProduct(Product product) {
        products.remove(product);
        product.setCategory(null);  // Triggers orphan removal
    }
}

@Entity
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length = 255)
    private String name;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "category_id", nullable = false)
    private Category category;

    // Bidirectional relationship management
    public void setCategory(Category category) {
        if (this.category != null) {
            this.category.removeProduct(this);
        }
        this.category = category;
        if (category != null) {
            category.addProduct(this);
        }
    }
}
```

### Batch Operations

```java
@Service
public class BatchOrderService {
    private final OrderRepository orderRepository;

    @Transactional
    public void batchUpdateOrders(List<OrderUpdate> updates) {
        // Use batch processing for large updates
        int batchSize = 50;
        for (int i = 0; i < updates.size(); i++) {
            OrderUpdate update = updates.get(i);

            Order order = orderRepository.findById(update.orderId())
                .orElseThrow();

            order.setStatus(update.status());
            order.setNotes(update.notes());

            if (i % batchSize == 0) {
                orderRepository.flush();  // Flush periodically
                orderRepository.clear();   // Clear persistence context
            }
        }
    }

    @Transactional
    public void batchDeleteOrdersByStatus(List<String> statuses) {
        // Batch delete by status
        statuses.forEach(status ->
            orderRepository.deleteByStatus(status));
    }
}
```

## UUID as Primary Key

### Modern Approach (Hibernate 6.2+ with JPA 3.1)

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

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

    @Column(length = 50)
    private String username;

    @Enumerated(EnumType.STRING)
    private UserStatus status;

    @CreatedDate
    private LocalDateTime createdDate;
}

@Entity
@Table(name = "sessions")
public class UserSession {
    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    private UUID id;

    @Column(name = "user_id", nullable = false)
    private UUID userId;

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

    @Column(name = "expires_at", nullable = false)
    private LocalDateTime expiresAt;
}

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

@Service
public class UserService {
    private final UserRepository repository;

    public User createUser(CreateUserRequest request) {
        if (repository.findByEmail(request.email()).isPresent()) {
            throw new EmailAlreadyExistsException(request.email());
        }

        User user = new User();
        user.setEmail(request.email());
        user.setUsername(request.username());
        user.setStatus(UserStatus.ACTIVE);

        return repository.save(user);
    }

    public User getUser(UUID id) {
        return repository.findById(id)
            .orElseThrow(() -> new UserNotFoundException(id));
    }
}
```

### Hibernate-Specific UUID Generation

```java
@Entity
@Table(name = "orders")
public class Order {
    @Id
    @UuidGenerator(style = UuidGenerator.Style.TIME)  // Version 1: Time-based
    private UUID id;

    @Column(nullable = false, length = 50, unique = true)
    private String orderNumber;

    @Column(precision = 12, scale = 2)
    private BigDecimal totalAmount;

    @CreatedDate
    private LocalDateTime createdAt;
}

@Entity
@Table(name = "products")
public class Product {
    @Id
    @UuidGenerator(style = UuidGenerator.Style.RANDOM)  // Version 4: Random
    private UUID id;

    @Column(nullable = false, length = 255)
    private String name;

    @Column(precision = 10, scale = 2)
    private BigDecimal price;

    @Column(name = "sku", length = 50, unique = true)
    private String sku;
}

@Entity
@Table(name = "events")
public class SystemEvent {
    @Id
    @UuidGenerator  // Default: RANDOM (Version 4)
    private UUID id;

    @Column(nullable = false, length = 50)
    private String eventType;

    @Column(columnDefinition = "TEXT")
    private String eventData;

    @CreatedDate
    private LocalDateTime timestamp;
}
```

### UUID Storage Options

```java
@Entity
@Table(name = "transactions")
public class Transaction {
    @Id
    @UuidGenerator
    @Column(columnDefinition = "VARCHAR(36)")  // Store as string for some databases
    private String id;

    @Column(precision = 19, scale = 4)  // High precision for financial data
    private BigDecimal amount;

    @Enumerated(EnumType.STRING)
    private TransactionStatus status;

    @Column(name = "reference_id", length = 36)
    private String referenceId;  // Secondary UUID field
}

@Entity
@Table(name = "audit_logs")
public class AuditLog {
    @Id
    @UuidGenerator
    private UUID id;  // Native UUID type (PostgreSQL, etc.)

    @Column(name = "entity_id", length = 36)  // May need VARCHAR for MySQL
    private String entityId;  // Foreign key as string for compatibility

    @Column(nullable = false, length = 100)
    private String action;

    @Column(columnDefinition = "TEXT")
    private String changes;

    @CreatedDate
    private LocalDateTime timestamp;
}
```

### UUID vs Sequential ID Comparison

```java
// UUID Entity: Better for distributed systems
@Entity
@Table(name = "articles")
public class Article {
    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    private UUID id;  // Good for: microservices, distributed DBs, offline-first

    @Column(nullable = false, length = 200)
    private String title;

    @Column(columnDefinition = "TEXT")
    private String content;

    @ManyToOne
    private User author;
}

// Sequential Entity: Better for single database
@Entity
@Table(name = "comments")
public class Comment {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;  // Good for: single database, better index performance

    @Column(nullable = false, columnDefinition = "TEXT")
    private String text;

    @ManyToOne
    private Article article;
}

// Hybrid approach: Best of both worlds
@Entity
@Table(name = "blogs")
public class Blog {
    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    private UUID id;  // Unique identifier

    @Column(unique = true, name = "slug")
    private String slug;  // URL-friendly, sequential-like identifier

    @Column(nullable = false, length = 200)
    private String title;

    @ManyToOne
    private User owner;
}
```

### Performance Considerations

```java
@Service
public class UserService {
    private final UserRepository repository;

    // Batch operations with UUIDs
    @Transactional
    public List<User> batchCreateUsers(List<CreateUserRequest> requests) {
        List<User> users = requests.stream()
            .map(request -> {
                User user = new User();
                user.setEmail(request.email());
                user.setUsername(request.username());
                return user;
            })
            .collect(Collectors.toList());

        return repository.saveAll(users);
    }

    // Index optimization for UUID queries
    @Transactional(readOnly = true)
    public Page<User> findUsersByEmailPattern(String pattern, int page, int size) {
        Pageable pageable = PageRequest.of(page, size);
        return repository.findByEmailContaining(pattern, pageable);
    }

    // Cache UUID lookups
    @Cacheable(value = "users", key = "#id")
    public User getUser(UUID id) {
        return repository.findById(id).orElseThrow();
    }
}
```

## Database Indexing

### Basic Index Definitions

```java
@Entity
@Table(
    name = "products",
    indexes = {
        @Index(name = "idx_product_name", columnList = "name"),
        @Index(name = "idx_product_category", columnList = "category_id")
    }
)
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length = 255)
    private String name;

    @ManyToOne
    @JoinColumn(name = "category_id")
    private Category category;

    @Column(precision = 10, scale = 2)
    private BigDecimal price;

    @Column(name = "created_date")
    private LocalDateTime createdDate;
}

@Entity
@Table(
    name = "users",
    indexes = {
        // Unique index for email
        @Index(name = "idx_user_email_unique", columnList = "email", unique = true),

        // Index for username (unique)
        @Index(name = "idx_user_username", columnList = "username", unique = true),

        // Index for status filtering
        @Index(name = "idx_user_status", columnList = "status")
    }
)
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

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

    @Column(length = 50, unique = true)
    private String username;

    @Enumerated(EnumType.STRING)
    private UserStatus status;

    @Column(name = "created_at")
    private LocalDateTime createdAt;
}
```

### Composite Indexes

```java
@Entity
@Table(
    name = "orders",
    indexes = {
        // Single column indexes
        @Index(name = "idx_order_status", columnList = "status"),
        @Index(name = "idx_order_customer", columnList = "customer_id"),

        // Composite index for common query pattern
        @Index(name = "idx_status_customer_created",
               columnList = "status, customer_id, created_at DESC"),

        // Another composite index
        @Index(name = "idx_order_created_status",
               columnList = "created_at DESC, status"),

        // Index for reporting queries
        @Index(name = "idx_order_report",
               columnList = "status, total_amount, created_at")
    }
)
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(length = 20)
    private String status;  // PENDING, PROCESSING, COMPLETED, CANCELLED

    @ManyToOne
    @JoinColumn(name = "customer_id")
    private Customer customer;

    @Column(precision = 12, scale = 2)
    private BigDecimal totalAmount;

    @Column(name = "created_at")
    private LocalDateTime createdAt;

    @Column(name = "updated_at")
    private LocalDateTime updatedAt;

    @Enumerated(EnumType.STRING)
    private OrderType type;  // RETAIL, WHOLESALE, B2B
}

@Entity
@Table(
    name = "order_items",
    indexes = {
        // Index for finding items by order
        @Index(name = "idx_order_item_order", columnList = "order_id"),

        // Composite index for order and product queries
        @Index(name = "idx_order_item_order_prod

…(truncated)
