jpskill.com
🛠️ 開発・MCP コミュニティ

spring-data-jpa

Spring Data JPAを活用し、最新のベストプラクティスに沿ってリポジトリやエンティティを実装し、高度なクエリやパフォーマンス最適化まで実現することで、効率的なデータアクセス層を構築するSkill。

📜 元の英語説明(参考)

Implement Spring Data JPA repositories, entities, and queries following modern best practices. Use for creating repositories (only for aggregate roots), writing queries (@Query, DTO projections), custom repositories (Criteria API, bulk ops), CQRS query services, entity relationships, and performance optimization. Covers patterns from simple repositories to advanced CQRS with detailed anti-patterns guidance.

🇯🇵 日本人クリエイター向け解説

一言でいうと

Spring Data JPAを活用し、最新のベストプラクティスに沿ってリポジトリやエンティティを実装し、高度なクエリやパフォーマンス最適化まで実現することで、効率的なデータアクセス層を構築するSkill。

※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o spring-data-jpa.zip https://jpskill.com/download/8069.zip && unzip -o spring-data-jpa.zip && rm spring-data-jpa.zip
🪟 Windows (PowerShell)
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/8069.zip -OutFile "$d\spring-data-jpa.zip"; Expand-Archive "$d\spring-data-jpa.zip" -DestinationPath $d -Force; ri "$d\spring-data-jpa.zip"

完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。

💾 手動でダウンロードしたい(コマンドが難しい人向け)
  1. 1. 下の青いボタンを押して spring-data-jpa.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → spring-data-jpa フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。

詳しい使い方ガイドを見る →
最終更新
2026-05-18
取得日時
2026-05-18
同梱ファイル
1

📖 Skill本文(日本語訳)

※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。

Spring Data JPA の実装

重要なルール

すべてのエンティティに対してリポジトリを作成してはいけません。必ず、集約のルートに対してのみリポジトリを作成してください。

複雑なクエリメソッド名を使用しないでください。自明でないクエリには、必ず @Query を使用してください。

安易に save() を使用しないでください。必ず、persist と merge のセマンティクスを理解してください (Vlad Mihalcea のガイダンスを参照)。

ステップ 1: リポジトリのニーズを特定する

以下の点を考慮します。

  1. これは集約のルートですか? - 集約のルートのみがリポジトリを持ちます。
  2. クエリの複雑さは? - 単純な検索ですか、それとも複雑なフィルタリングですか?
  3. 読み取り vs 書き込み? - コマンド (書き込み) ですか、それともクエリ (読み取り) ですか?
  4. パフォーマンスが重要ですか? - 大規模なデータセット、ページネーション、またはプロジェクションですか?

ステップ 2: パターンを選択する

パターン いつ 読み取り
シンプルなリポジトリ 基本的な CRUD、1〜2 個のカスタムクエリ -
@Query リポジトリ 複数のフィルター、結合、ソート references/query-patterns.md
DTO プロジェクション 読み取り専用、パフォーマンスが重要な場合 references/dto-projections.md
カスタムリポジトリ 複雑なロジック、バルクオペレーション、Criteria API references/custom-repositories.md
CQRS クエリサービス 読み取り/書き込みの分離、複数のプロジェクション references/cqrs-query-service.md

決定基準:

ニーズ シンプル @Query DTO カスタム CQRS
基本的な CRUD
カスタムクエリ
最高のパフォーマンス ✅✅ ✅✅ ✅✅
複雑なロジック
読み取り/書き込みの分離 ✅✅

ステップ 3: リポジトリを実装する

シンプルなリポジトリ

基本的な検索 (1〜2 個のプロパティ) の場合:

public interface ProductRepository extends JpaRepository<ProductEntity, Long> {
    Optional<ProductEntity> findByCode(String code);
    List<ProductEntity> findByStatus(ProductStatus status);
}

アセット: 既存のエンティティとリポジトリのパターンを使用します。

@Query リポジトリ

3 つ以上のフィルター、結合、または可読性が必要な場合。参照: references/query-patterns.md

public interface OrderRepository extends JpaRepository<OrderEntity, Long> {

    @Query("""
        SELECT DISTINCT o
        FROM OrderEntity o
        LEFT JOIN FETCH o.items
        WHERE o.userId = :userId
        ORDER BY o.createdAt DESC
        """)
    List<OrderEntity> findUserOrders(@Param("userId") Long userId);
}

アセット: assets/query-repository.java - 例を含む完全なテンプレート

DTO プロジェクション

読み取り専用で、パフォーマンスが重要なクエリの場合。参照: references/dto-projections.md

public record ProductSummary(Long id, String name, BigDecimal price) {}

@Query("""
    SELECT new com.example.ProductSummary(p.id, p.name, p.price)
    FROM ProductEntity p
    WHERE p.status = 'ACTIVE'
    """)
List<ProductSummary> findActiveSummaries();

アセット: assets/dto-projection.java - レコード、インターフェース、ネイティブクエリ

カスタムリポジトリ

Criteria API、バルクオペレーションの場合。参照: references/custom-repositories.md

// 1. カスタムインターフェース
public interface ProductRepositoryCustom {
    List<ProductEntity> findByDynamicCriteria(SearchCriteria criteria);
}

// 2. 実装 (名前は <Repository>Impl である必要があります)
@Repository
class ProductRepositoryImpl implements ProductRepositoryCustom {
    @PersistenceContext
    private EntityManager entityManager;
    // Criteria API を使用した実装
}

// 3. メインリポジトリは両方を拡張します
public interface ProductRepository extends JpaRepository<ProductEntity, Long>,
                                           ProductRepositoryCustom {
    Optional<ProductEntity> findBySku(String sku);
}

アセット: assets/custom-repository.java - 完全なパターン

CQRS クエリサービス

Tomato/DDD アーキテクチャの場合。参照: references/cqrs-query-service.md

// リポジトリ (パッケージプライベート) - 書き込みのみ
interface ProductRepository extends JpaRepository<ProductEntity, ProductId> {
    Optional<ProductEntity> findBySku(ProductSKU sku);
}

// QueryService (パブリック) - 読み取りのみ
@Service
@Transactional(readOnly = true)
public class ProductQueryService {

    private final JdbcTemplate jdbcTemplate;

    public List<ProductVM> findAllActive() {
        return jdbcTemplate.query("""
            SELECT id, name, price FROM products
            WHERE status = 'ACTIVE'
            """,
            (rs, rowNum) -> new ProductVM(
                rs.getLong("id"),
                rs.getString("name"),
                rs.getBigDecimal("price")
            )
        );
    }
}

アセット: assets/query-service.java - JdbcTemplate を使用した完全な CQRS パターン

ステップ 4: エンティティの関係

詳細なガイダンスについては、参照: references/relationships.md

簡単なパターン:

// ✅ 良い: @ManyToOne (最も一般的)
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "order_id", nullable = false)
private Order order;

// ✅ 代替案: ID のみを使用する (疎結合)
@Column(name = "product_id", nullable = false)
private Long productId;

// ❌ 回避: @OneToMany (代わりに多側のクエリを使用する)
// 代わりに: List<OrderItem> items = itemRepository.findByOrderId(orderId);

// ❌ 絶対にしない: @ManyToMany (代わりに結合エンティティを作成する)
@Entity
public class Enrollment {
    @ManyToOne private Student student;
    @ManyToOne private Course course;
    private LocalDate enrolledAt;
}

アセット: assets/relationship-patterns.java - 例を含むすべての関係タイプ

ステップ 5: パフォーマンスの最適化

完全なチェックリストについては、参照: references/performance-guide.md

重要な最適化:

  1. N+1 クエリを防ぐ:

    
    // JOIN FETCH を使用する
    @Query("SELECT o FROM Order o JOIN FETCH o.customer")
    List<Order> findWithCustomer();
    
    // または DTO プロジェクションを使用する
    @Query("SELECT new OrderSummary(o.id, c.name) FROM Order o JOIN o.customer c")
    List<OrderS
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開

Spring Data JPA Implementation

Critical Rules

NEVER create repositories for every entity. ALWAYS create repositories only for aggregate roots.

NEVER use complex query method names. ALWAYS use @Query for non-trivial queries.

NEVER use save() blindly. ALWAYS understand persist vs merge semantics (see Vlad Mihalcea's guidance).

Step 1: Identify Repository Needs

Ask:

  1. Is this an aggregate root? - Only aggregate roots get repositories
  2. Query complexity? - Simple lookup or complex filtering?
  3. Read vs Write? - Commands (write) or queries (read)?
  4. Performance critical? - Large datasets, pagination, or projections?

Step 2: Choose Pattern

Pattern When Read
Simple Repository Basic CRUD, 1-2 custom queries -
@Query Repository Multiple filters, joins, sorting references/query-patterns.md
DTO Projection Read-only, performance-critical references/dto-projections.md
Custom Repository Complex logic, bulk ops, Criteria API references/custom-repositories.md
CQRS Query Service Separate read/write, multiple projections references/cqrs-query-service.md

Decision criteria:

Need Simple @Query DTO Custom CQRS
Basic CRUD
Custom Queries
Best Performance ✅✅ ✅✅ ✅✅
Complex Logic
Read/Write Separation ✅✅

Step 3: Implement Repository

Simple Repository

For basic lookups (1-2 properties):

public interface ProductRepository extends JpaRepository<ProductEntity, Long> {
    Optional<ProductEntity> findByCode(String code);
    List<ProductEntity> findByStatus(ProductStatus status);
}

Asset: Use existing entity and repository patterns

@Query Repository

For 3+ filters, joins, or readability. Read: references/query-patterns.md

public interface OrderRepository extends JpaRepository<OrderEntity, Long> {

    @Query("""
        SELECT DISTINCT o
        FROM OrderEntity o
        LEFT JOIN FETCH o.items
        WHERE o.userId = :userId
        ORDER BY o.createdAt DESC
        """)
    List<OrderEntity> findUserOrders(@Param("userId") Long userId);
}

Asset: assets/query-repository.java - Complete template with examples

DTO Projections

For read-only, performance-critical queries. Read: references/dto-projections.md

public record ProductSummary(Long id, String name, BigDecimal price) {}

@Query("""
    SELECT new com.example.ProductSummary(p.id, p.name, p.price)
    FROM ProductEntity p
    WHERE p.status = 'ACTIVE'
    """)
List<ProductSummary> findActiveSummaries();

Asset: assets/dto-projection.java - Records, interfaces, native queries

Custom Repository

For Criteria API, bulk ops. Read: references/custom-repositories.md

// 1. Custom interface
public interface ProductRepositoryCustom {
    List<ProductEntity> findByDynamicCriteria(SearchCriteria criteria);
}

// 2. Implementation (must be named <Repository>Impl)
@Repository
class ProductRepositoryImpl implements ProductRepositoryCustom {
    @PersistenceContext
    private EntityManager entityManager;
    // Implementation using Criteria API
}

// 3. Main repository extends both
public interface ProductRepository extends JpaRepository<ProductEntity, Long>,
                                           ProductRepositoryCustom {
    Optional<ProductEntity> findBySku(String sku);
}

Asset: assets/custom-repository.java - Complete pattern

CQRS Query Service

For Tomato/DDD architectures. Read: references/cqrs-query-service.md

// Repository (package-private) - writes only
interface ProductRepository extends JpaRepository<ProductEntity, ProductId> {
    Optional<ProductEntity> findBySku(ProductSKU sku);
}

// QueryService (public) - reads only
@Service
@Transactional(readOnly = true)
public class ProductQueryService {

    private final JdbcTemplate jdbcTemplate;

    public List<ProductVM> findAllActive() {
        return jdbcTemplate.query("""
            SELECT id, name, price FROM products
            WHERE status = 'ACTIVE'
            """,
            (rs, rowNum) -> new ProductVM(
                rs.getLong("id"),
                rs.getString("name"),
                rs.getBigDecimal("price")
            )
        );
    }
}

Asset: assets/query-service.java - Full CQRS pattern with JdbcTemplate

Step 4: Entity Relationships

Read: references/relationships.md for detailed guidance

Quick patterns:

// ✅ GOOD: @ManyToOne (most common)
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "order_id", nullable = false)
private Order order;

// ✅ ALTERNATIVE: Just use ID (loose coupling)
@Column(name = "product_id", nullable = false)
private Long productId;

// ❌ AVOID: @OneToMany (query from many side instead)
// Instead: List<OrderItem> items = itemRepository.findByOrderId(orderId);

// ❌ NEVER: @ManyToMany (create join entity instead)
@Entity
public class Enrollment {
    @ManyToOne private Student student;
    @ManyToOne private Course course;
    private LocalDate enrolledAt;
}

Asset: assets/relationship-patterns.java - All relationship types with examples

Step 5: Performance Optimization

Read: references/performance-guide.md for complete checklist

Critical optimizations:

  1. Prevent N+1 queries:

    // Use JOIN FETCH
    @Query("SELECT o FROM Order o JOIN FETCH o.customer")
    List<Order> findWithCustomer();
    
    // Or use DTO projection
    @Query("SELECT new OrderSummary(o.id, c.name) FROM Order o JOIN o.customer c")
    List<OrderSummary> findSummaries();
  2. Use pagination:

    Pageable pageable = PageRequest.of(0, 20);
    Page<Product> page = repository.findByCategory("Electronics", pageable);
  3. Mark read services as readOnly:

    @Service
    @Transactional(readOnly = true)
    public class ProductQueryService { }
  4. Configure batch size:

    spring.jpa.properties.hibernate.jdbc.batch_size: 25

Step 6: Transaction Management

Best practices:

@Service
@Transactional(readOnly = true)  // Class-level for read services
public class ProductService {

    public List<ProductVM> findAll() {
        // Read operations
    }

    @Transactional  // Override for writes
    public void createProduct(CreateProductCmd cmd) {
        ProductEntity product = ProductEntity.create(cmd);
        repository.save(product);
    }
}

Rules:

  • Use @Transactional(readOnly = true) at class level for query services
  • Put @Transactional at service layer, not repository
  • Override with @Transactional for write methods in read services

Step 7: Testing

@DataJpaTest
@Testcontainers
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class ProductRepositoryTest {

    @Container
    static PostgreSQLContainer<?> postgres =
        new PostgreSQLContainer<>("postgres:16-alpine");

    @Autowired
    private ProductRepository repository;

    @DynamicPropertySource
    static void configureProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", postgres::getJdbcUrl);
        registry.add("spring.datasource.username", postgres::getUsername);
        registry.add("spring.datasource.password", postgres::getPassword);
    }

    @Test
    void shouldFindProductByCode() {
        ProductEntity product = createTestProduct("P001");
        repository.save(product);

        Optional<ProductEntity> found = repository.findByCode("P001");

        assertThat(found).isPresent();
    }
}

Anti-Patterns

Don't Do Why
Repository for every entity Only for aggregate roots Maintains boundaries
Use save() blindly Understand persist/merge Avoids unnecessary SELECT
Long query method names Use @Query Readability
findAll() without pagination Use Page<> or Stream Memory issues
Fetch entities for read views Use DTO projections Performance
FetchType.EAGER LAZY + JOIN FETCH Avoids N+1
@ManyToMany Use join entity Allows relationship attributes
@Transactional in repository Put in service layer Proper boundaries
Return entities from controllers Return DTOs/VMs Prevents lazy issues

Common Pitfalls

1. LazyInitializationException

Problem: Accessing lazy associations outside transaction

Solution: Use DTO projection or JOIN FETCH

@Query("SELECT o FROM Order o JOIN FETCH o.items WHERE o.id = :id")
Optional<Order> findByIdWithItems(@Param("id") Long id);

2. N+1 Queries

Problem: Loading associations in loop

Solution: See references/performance-guide.md

3. Cartesian Product

Problem: Multiple JOIN FETCH with collections

Solution: Separate queries or DTO projections

Quick Reference

When to Load References

  • Multiple filters/joins neededreferences/query-patterns.md
  • Read-only, performance-criticalreferences/dto-projections.md
  • Dynamic queries, bulk operationsreferences/custom-repositories.md
  • CQRS, read/write separationreferences/cqrs-query-service.md
  • Entity associationsreferences/relationships.md
  • Slow queries, N+1 issuesreferences/performance-guide.md

Available Assets

All templates in assets/:

  • query-repository.java - @Query examples, pagination, bulk ops
  • dto-projection.java - Records, interfaces, native queries
  • custom-repository.java - Criteria API, EntityManager
  • query-service.java - CQRS with JdbcTemplate
  • relationship-patterns.java - All JPA associations

References

Incorporates best practices from:

Browse vladmihalcea.com/blog for deep-dive articles.