Spring Data JPA

Spring Data JPAは「Simplifies the development of creating a JPA-based data access layer」として開発されたSpring Frameworkの公式データアクセスライブラリです。JPA(Java Persistence API)の実装であるHibernateを基盤として、リポジトリパターンとメソッド名による自動クエリ生成により、定形的なデータアクセスコードを大幅に削減します。2025年現在、Javaエンタープライズアプリケーション開発における事実上の標準ORMライブラリとして、Spring Bootエコシステムの中核を担っています。

ORMライブラリJavaJPAHibernateSpring Frameworkデータアクセスリポジトリパターン

GitHub概要

spring-projects/spring-data-jpa

Simplifies the development of creating a JPA-based data access layer.

スター3,100
ウォッチ198
フォーク1,500
作成日:2014年4月16日
言語:Java
ライセンス:Apache License 2.0

トピックス

javajpaspringspring-dataormdatabasehibernaterepository

スター履歴

spring-projects/spring-data-jpa Star History
データ取得日時: 2025/7/17 02:35

ライブラリ

Spring Data JPA

概要

Spring Data JPAは「Simplifies the development of creating a JPA-based data access layer」として開発されたSpring Frameworkの公式データアクセスライブラリです。JPA(Java Persistence API)の実装であるHibernateを基盤として、リポジトリパターンとメソッド名による自動クエリ生成により、定形的なデータアクセスコードを大幅に削減します。2025年現在、Javaエンタープライズアプリケーション開発における事実上の標準ORMライブラリとして、Spring Bootエコシステムの中核を担っています。

詳細

Spring Data JPA 3.4は2025年現在の最新安定版で、Spring Framework 6とJakarta EE 10に対応した次世代アーキテクチャを採用しています。従来のJPAの複雑な設定とボイラープレートコードを排除し、インターフェースの定義のみでデータアクセス層を構築可能。メソッド名の命名規則から自動的にJPQLクエリを生成する「Derived Query Methods」機能により、90%以上のCRUD操作を実装不要で実現します。Spring Boot自動設定、トランザクション管理、監査機能、ページネーション、ソート機能を統合した包括的なデータアクセスソリューションです。

主な特徴

  • 自動クエリ生成: メソッド名から自動的にJPQLクエリを生成
  • リポジトリパターン: インターフェース定義のみでデータアクセス層を構築
  • Spring Boot統合: 設定レスでの自動構成とデータソース管理
  • トランザクション管理: 宣言的トランザクション制御と自動ロールバック
  • ページネーション: 大規模データの効率的な分割取得機能
  • 監査機能: 作成日時、更新日時、作成者の自動記録
  • Criteria API: 動的クエリ構築のためのSpecificationパターン

メリット・デメリット

メリット

  • Javaエンタープライズ開発のデファクトスタンダード
  • Spring Bootによる設定レスな開発体験
  • 豊富なコミュニティサポートと企業導入実績
  • Hibernateエコシステムの恩恵(キャッシュ、コネクションプール等)
  • 強力な型安全性とコンパイル時チェック
  • トランザクション境界の宣言的制御
  • テスト支援機能の充実(@DataJpaTest等)

デメリット

  • 複雑なクエリでは生のSQLより性能が劣る場合がある
  • N+1問題など、ORM特有の性能問題に注意が必要
  • Hibernate固有の機能に依存した設計になりがち
  • 大規模データ処理では詳細な調整が必要
  • 学習コストがやや高い(JPA仕様理解が必要)
  • レガシー SQL の移行が困難な場合がある

参考ページ

書き方の例

インストールとセットアップ(Spring Boot)

<!-- Maven依存関係 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>
// Gradle依存関係
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'com.h2database:h2'

エンティティの定義

import jakarta.persistence.*;

@Entity
@Table(name = "users")
public class User {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(nullable = false, unique = true)
    private String email;
    
    @Column(nullable = false)
    private String firstName;
    
    @Column(nullable = false)
    private String lastName;
    
    @CreationTimestamp
    private LocalDateTime createdAt;
    
    @UpdateTimestamp
    private LocalDateTime updatedAt;
    
    // コンストラクタ、getters、setters
    public User() {}
    
    public User(String email, String firstName, String lastName) {
        this.email = email;
        this.firstName = firstName;
        this.lastName = lastName;
    }
    
    // getter、setter省略
}

リポジトリインターフェースの定義

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import java.util.List;
import java.util.Optional;

public interface UserRepository extends JpaRepository<User, Long> {
    
    // 自動クエリ生成(Derived Query Methods)
    List<User> findByLastName(String lastName);
    
    List<User> findByFirstNameLike(String firstName);
    
    Optional<User> findByEmail(String email);
    
    List<User> findByLastNameAndFirstName(String lastName, String firstName);
    
    // ページネーション対応
    Page<User> findByLastNameStartingWith(String prefix, Pageable pageable);
    
    // カスタムクエリ(JPQL)
    @Query("SELECT u FROM User u WHERE u.firstName = :firstName OR u.lastName = :lastName")
    List<User> findByFirstNameOrLastName(
        @Param("firstName") String firstName, 
        @Param("lastName") String lastName
    );
    
    // ネイティブSQL
    @Query(value = "SELECT * FROM users WHERE email LIKE %:domain%", nativeQuery = true)
    List<User> findByEmailDomain(@Param("domain") String domain);
    
    // 更新クエリ
    @Modifying
    @Query("UPDATE User u SET u.firstName = :firstName WHERE u.email = :email")
    int updateFirstNameByEmail(@Param("firstName") String firstName, @Param("email") String email);
    
    // 削除クエリ
    @Modifying
    @Query("DELETE FROM User u WHERE u.createdAt < :date")
    int deleteOldUsers(@Param("date") LocalDateTime date);
}

サービス層での利用

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
public class UserService {
    
    private final UserRepository userRepository;
    
    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
    
    public User createUser(String email, String firstName, String lastName) {
        User user = new User(email, firstName, lastName);
        return userRepository.save(user);
    }
    
    @Transactional(readOnly = true)
    public Optional<User> findByEmail(String email) {
        return userRepository.findByEmail(email);
    }
    
    @Transactional(readOnly = true)
    public Page<User> findUsersByLastName(String lastName, int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by("firstName"));
        return userRepository.findByLastNameStartingWith(lastName, pageable);
    }
    
    public void updateUserFirstName(String email, String newFirstName) {
        userRepository.updateFirstNameByEmail(newFirstName, email);
    }
    
    public void deleteUser(Long id) {
        userRepository.deleteById(id);
    }
    
    @Transactional(readOnly = true)
    public List<User> searchUsers(String searchTerm) {
        return userRepository.findByFirstNameOrLastName(searchTerm, searchTerm);
    }
}

Specificationを使用した動的クエリ

import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

// リポジトリの拡張
public interface UserRepository extends JpaRepository<User, Long>, 
                                       JpaSpecificationExecutor<User> {
}

// Specification定義
public class UserSpecifications {
    
    public static Specification<User> hasFirstName(String firstName) {
        return (root, query, criteriaBuilder) -> 
            firstName == null ? null : criteriaBuilder.equal(root.get("firstName"), firstName);
    }
    
    public static Specification<User> hasLastName(String lastName) {
        return (root, query, criteriaBuilder) -> 
            lastName == null ? null : criteriaBuilder.equal(root.get("lastName"), lastName);
    }
    
    public static Specification<User> emailContains(String email) {
        return (root, query, criteriaBuilder) -> 
            email == null ? null : criteriaBuilder.like(root.get("email"), "%" + email + "%");
    }
    
    public static Specification<User> createdAfter(LocalDateTime date) {
        return (root, query, criteriaBuilder) -> 
            date == null ? null : criteriaBuilder.greaterThan(root.get("createdAt"), date);
    }
}

// サービスでの使用
@Service
public class UserSearchService {
    
    private final UserRepository userRepository;
    
    public List<User> searchUsers(String firstName, String lastName, String email, LocalDateTime after) {
        Specification<User> spec = Specification.where(null);
        
        if (firstName != null) {
            spec = spec.and(UserSpecifications.hasFirstName(firstName));
        }
        if (lastName != null) {
            spec = spec.and(UserSpecifications.hasLastName(lastName));
        }
        if (email != null) {
            spec = spec.and(UserSpecifications.emailContains(email));
        }
        if (after != null) {
            spec = spec.and(UserSpecifications.createdAfter(after));
        }
        
        return userRepository.findAll(spec);
    }
}

監査機能の設定

// 監査用エンティティ基底クラス
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class AuditableEntity {
    
    @CreatedDate
    @Column(name = "created_at", nullable = false, updatable = false)
    private LocalDateTime createdAt;
    
    @LastModifiedDate
    @Column(name = "updated_at")
    private LocalDateTime updatedAt;
    
    @CreatedBy
    @Column(name = "created_by", updatable = false)
    private String createdBy;
    
    @LastModifiedBy
    @Column(name = "updated_by")
    private String updatedBy;
    
    // getters、setters
}

// 設定クラス
@Configuration
@EnableJpaAuditing
public class JpaAuditConfig {
    
    @Bean
    public AuditorAware<String> auditorProvider() {
        return () -> {
            // 現在のユーザーを取得するロジック
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            if (authentication == null || !authentication.isAuthenticated()) {
                return Optional.of("system");
            }
            return Optional.of(authentication.getName());
        };
    }
}

// エンティティでの継承
@Entity
public class User extends AuditableEntity {
    // 通常のフィールド定義
}

カスタムリポジトリメソッドの実装

// カスタムインターフェース
public interface UserRepositoryCustom {
    List<User> findUsersWithComplexConditions(UserSearchCriteria criteria);
}

// カスタム実装
@Repository
public class UserRepositoryImpl implements UserRepositoryCustom {
    
    @PersistenceContext
    private EntityManager entityManager;
    
    @Override
    public List<User> findUsersWithComplexConditions(UserSearchCriteria criteria) {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<User> query = cb.createQuery(User.class);
        Root<User> user = query.from(User.class);
        
        List<Predicate> predicates = new ArrayList<>();
        
        if (criteria.getFirstName() != null) {
            predicates.add(cb.like(user.get("firstName"), "%" + criteria.getFirstName() + "%"));
        }
        
        if (criteria.getEmail() != null) {
            predicates.add(cb.equal(user.get("email"), criteria.getEmail()));
        }
        
        query.where(predicates.toArray(new Predicate[0]));
        query.orderBy(cb.asc(user.get("lastName")));
        
        return entityManager.createQuery(query).getResultList();
    }
}

// メインリポジトリインターフェース
public interface UserRepository extends JpaRepository<User, Long>, 
                                       JpaSpecificationExecutor<User>,
                                       UserRepositoryCustom {
}

その他

Spring Data JPAは、エンタープライズJava開発において最も成熟し、広く採用されているORMソリューションです。Spring Bootとの統合により設定レスな開発が可能で、チーム開発における生産性向上に大きく貢献します。特に、大規模なエンタープライズアプリケーションでの実績が豊富で、パフォーマンスチューニング、トランザクション管理、セキュリティ統合などの企業要件に対する包括的なソリューションを提供します。