# Data Access Patterns

## Overview

This codebase uses Spring Data repository interfaces as the primary boundary for reading persistent data. Rather than implementing data access logic in service classes, the repositories define query intent directly in their method signatures or annotations, letting Spring Data generate the backing implementation at runtime.

The overall pattern is intentionally lightweight:

- repositories are interfaces, not concrete DAO classes
- read operations are expressed with Spring Data naming conventions or JPQL annotations
- common repository features such as paging are exposed through Spring Data abstractions
- selected read paths are cached when the data is stable and frequently requested

This keeps persistent access consistent across the application while making the query surface easy to scan and extend.

## Implementation Patterns

### Spring Data repository interfaces

The main access pattern is interface-based repositories that extend Spring Data types:

- [`OwnerRepository`](src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java)
- [`PetTypeRepository`](src/main/java/org/springframework/samples/petclinic/owner/PetTypeRepository.java)
- [`VetRepository`](src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java)

Two different base styles appear:

- [`JpaRepository`](src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java) for repositories that participate directly in Spring Data JPA
- [`Repository`](src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java) for a narrower repository contract where only selected methods are exposed

This gives each aggregate a repository surface that matches its use case instead of forcing a single uniform pattern.

### Derived queries

Where the query shape is simple, the codebase relies on Spring Data method naming:

- [`findByLastNameStartingWith`](src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java) expresses a prefix search without handwritten query code

This is the dominant style for standard lookups because it remains concise and readable while still mapping to database queries automatically.

### Pageable results for larger result sets

Repositories expose paged access when the result set may be larger than a single screen:

- [`OwnerRepository`](src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java) returns a [`Page`](src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java) for filtered owner searches
- [`VetRepository`](src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java) offers both collection and pageable variants of `findAll`

This pattern makes data access friendlier to UI flows that need pagination, sorting, or controlled fetch sizes.

### Explicit query definition when ordering matters

When a repository needs a specific ordering that is not conveyed by method naming alone, the code uses `@Query`:

- [`PetTypeRepository`](src/main/java/org/springframework/samples/petclinic/owner/PetTypeRepository.java) defines a JPQL query that orders pet types by name

That keeps the ordering rule near the repository interface, where it is easy to find and maintain.

### Caching for stable read-mostly data

[`VetRepository`](src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java) adds `@Cacheable("vets")` to its read methods. This indicates that vet listings are treated as read-mostly data and can be served from cache rather than repeatedly hitting the database.

The cache is applied only to read methods here, which matches the broader pattern of keeping repository operations side-effect free unless they are intentionally mutating state.

## Key Classes

### [`OwnerRepository`](src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java)

Repository for owner records. It combines Spring Data JPA inheritance with:

- a derived query for prefix searches on last name
- pageable search results
- an explicit `findById` contract returning `Optional`

This repository represents the typical pattern for flexible read access over a primary business aggregate.

### [`PetTypeRepository`](src/main/java/org/springframework/samples/petclinic/owner/PetTypeRepository.java)

Repository for pet type lookup data. It extends `JpaRepository` and adds a custom JPQL query to return pet types in name order.

This is the clearest example of using repository-level query annotations when the default naming conventions are not enough.

### [`VetRepository`](src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java)

Repository for vet listings. It uses the narrower Spring Data `Repository` base interface and exposes only the methods the application needs:

- all vets
- paged vets

Both read methods are transactional as read-only and cacheable, which makes this repository the best example of the project's read-optimization pattern.

## How It Fits Together

```mermaid
flowchart TD
OwnerRepo["OwnerRepository"] --> SpringDataJPA["Spring Data JPA runtime"]
PetTypeRepo["PetTypeRepository"] --> SpringDataJPA
VetRepo["VetRepository"] --> SpringDataRuntime["Spring Data repository runtime"]
SpringDataJPA --> Database["Persistent store"]
SpringDataRuntime --> Database
OwnerRepo --> OwnerQuery["Derived query - last name prefix"]
PetTypeRepo --> PetTypeQuery["JPQL ordered lookup"]
VetRepo --> VetCache["Cache 'vets'"]
VetCache --> Database
```

In practice, application code calls a repository interface, Spring Data resolves the query strategy, and the result comes back as a domain object, collection, page, or optional depending on the use case.

## Notes for Developers

- Prefer repository interfaces over handwritten data-access classes when the query can be expressed with Spring Data conventions.
- Use derived query methods for straightforward filters and lookups.
- Use `@Query` when the query needs explicit ordering or structure that is awkward to encode in a method name.
- Return `Page` when a result set may grow beyond a simple collection display.
- Use `Optional` for single-entity lookups when absence is expected and should be handled explicitly.
- Add caching only to stable, read-heavy data where repeated database access is avoidable.
- Keep repository contracts narrow when the application only needs a small set of read operations.
