# Data Access Patterns

## Overview

The codebase uses Spring Data repository interfaces as the primary abstraction for persistent data access. Rather than implementing DAO classes manually, the repositories declare query methods and let Spring Data generate the underlying database interactions. This keeps data access centralized, type-safe, and easy to extend.

Across the codebase, repositories are focused on read access patterns and domain-specific queries:

- `OwnerRepository` uses a standard Spring Data JPA base interface and derived query methods for owner lookup.
- `PetTypeRepository` adds a JPQL query to return pet types in a predictable order.
- `VetRepository` uses Spring Data repository support together with read-only transactions and caching for frequently accessed vet data.

These patterns matter because they define how the application balances simplicity, consistency, and performance when reading from persistent storage.

## Implementation Patterns

### Spring Data repository interfaces

Repositories are declared as interfaces, not concrete implementations. Spring Data provides the runtime implementation based on the interface signature and inherited repository base types.

- [`OwnerRepository`](src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java) extends `JpaRepository<Owner, Integer>`.
- [`PetTypeRepository`](src/main/java/org/springframework/samples/petclinic/owner/PetTypeRepository.java) extends `JpaRepository<PetType, Integer>`.
- [`VetRepository`](src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java) extends `Repository<Vet, Integer>`.

This pattern keeps persistence concerns declarative and avoids boilerplate SQL or entity-manager code in feature logic.

### Derived query methods

The repositories commonly rely on Spring Data method-name parsing to generate queries automatically.

- `OwnerRepository` declares `findByLastNameStartingWith(String lastName, Pageable pageable)`, which expresses a prefix search without needing a manual query definition.
- `OwnerRepository` also declares `findById(Integer id)` returning `Optional<Owner>`, making the null/empty case explicit in the API.

This pattern is especially useful for straightforward lookups and filtering, where the repository method name documents the intent.

### Explicit JPQL for ordering

When the default derived query pattern is not enough, the repository can define a query explicitly.

- `PetTypeRepository` uses `@Query("SELECT ptype FROM PetType ptype ORDER BY ptype.name")` to return pet types in a stable, user-friendly order.

This is a good fit when the result ordering is part of the contract rather than an incidental detail.

### Read-only transactional access and caching

For frequently read data, the codebase adds transaction and cache annotations at the repository level.

- `VetRepository` marks `findAll()` and `findAll(Pageable pageable)` with `@Transactional(readOnly = true)`.
- `VetRepository` also uses `@Cacheable("vets")` on both methods.

Together, these annotations communicate intent and improve efficiency for vet listing workflows.

## Key Classes

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

The main access point for owner persistence. It inherits the standard CRUD operations from `JpaRepository` and adds owner-specific lookups based on last name prefix and identifier. This class represents the most typical Spring Data JPA usage in the codebase.

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

The repository for pet type lookup data. It shows how the codebase uses a small explicit JPQL query when the repository needs a specific ordering contract. This is the clearest example of a fixed reference-data read pattern.

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

The repository for vet listings. It demonstrates two important cross-cutting concerns for read-heavy data access: marking reads as transactional-only and caching the results. This class is the strongest signal that not all repository access is treated the same; some datasets are optimized for reuse.

## How It Fits Together

```mermaid
flowchart TD
OwnerRepo["OwnerRepository"] --> JpaRepo["JpaRepository"]
PetTypeRepo["PetTypeRepository"] --> JpaRepo
VetRepo["VetRepository"] --> SpringDataRepo["Spring Data Repository"]
OwnerRepo --> OwnerQueries["Derived query methods"]
PetTypeRepo --> PetTypeQuery["JPQL @Query"]
VetRepo --> VetCache["@Cacheable vets"]
VetRepo --> VetReadonly["@Transactional readOnly"]
```

In practice, the flow is:

1. Application code calls a repository interface.
2. Spring Data resolves the method into a generated implementation or a declared query.
3. The repository reads from persistent storage, optionally applying transaction hints or caching.
4. The caller receives domain objects or pages of domain objects.

## Notes for Developers

- Prefer repository interfaces for persistence access instead of embedding query logic in services.
- Use derived query methods when the lookup is simple and the method name clearly communicates intent.
- Use `@Query` when the query needs explicit ordering, joins, or other behavior that is awkward to express through method names.
- Use `Pageable` for large result sets so callers can request data in chunks instead of loading everything at once.
- Treat caching as a deliberate optimization. `VetRepository` shows that cacheable reads are appropriate for stable reference-like data that is read often.
- Keep repository contracts focused on persistence concerns; avoid leaking higher-level business logic into these interfaces.
- When adding new repositories, follow the same naming and return-type conventions so the data access layer stays consistent and predictable.
