Spring Data JPAは、データベース操作を簡潔かつ効率的に扱うための強力なツールです。この記事では、基本的なCRUD操作から高度な機能まで、Spring Data JPAの利用方法を解説します。
Spring Data JPAとは
Spring Data JPAは、Springフレームワークの一部で、JPA(Java Persistence API)を使用したデータアクセス層の開発を簡素化します。多くのテンプレートコードを自動生成し、開発者が複雑なデータ操作に集中できるように支援します。
クイックスタート
プロジェクト設定
まずは、Spring Bootプロジェクトを作成し、pom.xmlに必要な依存関係を追加します。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
データソースの設定
application.propertiesにデータベース接続情報を記述します。
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
エンティティクラスの作成
次に、シンプルなエンティティクラスUserを定義します。
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String userName;
private String emailAddress;
// GetterとSetterメソッド
}
リポジトリインターフェースの作成
JpaRepositoryを継承したリポジトリインターフェースを作ります。
public interface PersonRepository extends JpaRepository<Person, Long> {
List<Person> findByUserName(String userName);
}
リポジトリの利用
サービスクラスでPersonRepositoryをインジェクションし、データ操作を行います。
@Service
public class PersonService {
@Autowired
private PersonRepository personRepository;
public Person registerUser(String userName, String emailAddress) {
Person person = new Person();
person.setUserName(userName);
person.setEmailAddress(emailAddress);
return personRepository.save(person);
}
public List<Person> findUsersByUserName(String userName) {
return personRepository.findByUserName(userName);
}
}
詳細機能
カスタムクエリメソッド
メソッド名からクエリを自動生成する機能があります。
List<Person> findByEmailAddress(String emailAddress);
また、@Queryアノテーションを使用してJPQLクエリを定義できます。
@Query("SELECT p FROM Person p WHERE p.emailAddress = ?1")
Person findUserByEmail(String emailAddress);
ページネーションとソート
Pageableパラメータを使用して、ページネーションを実装します。
Page<Person> findAll(Pageable pageable);
サービスクラスでの使用例:
Page<Person> people = personRepository.findAll(PageRequest.of(0, 10, Sort.by("userName")));
複雑なクエリ
複雑なクエリにはSpecification APIを利用します。
public class PersonSpecification {
public static Specification<Person> hasUserName(String userName) {
return (root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("userName"), userName);
}
}
サービスクラスでの使用例:
List<Person> people = personRepository.findAll(PersonSpecification.hasUserName("John"));
高度な機能
楽観的ロックと悲観的ロック
楽観的ロックは、@Versionアノテーションを使用して実現します。
@Version
private Integer lockVersion;
悲観的ロックは、クエリ時にロックモードを指定します。
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT p FROM Person p WHERE p.userName = :userName")
List<Person> findByUserNameWithLock(@Param("userName") String userName);
オーディティング機能
オーディティング機能は、@CreatedDateと@LastModifiedDateアノテーションで実装します。
@EntityListeners(AuditingEntityListener.class)
public class Person {
@CreatedDate
private LocalDateTime createdAt;
@LastModifiedDate
private LocalDateTime updatedAt;
}
そして、コンフィグクラスでオーディティングを有効にします。
@Configuration
@EnableJpaAuditing
public class JpaConfiguration {
}
実践例
簡単なブログシステムを例にとって、ユーザー管理、記事管理、コメント管理を実装します。
エンティティクラスの定義
@Entity
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long postId;
private String title;
private String content;
@ManyToOne
@JoinColumn(name = "person_id")
private Person author;
// GetterとSetterメソッド
}
@Entity
public class Review {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long reviewId;
private String content;
@ManyToOne
@JoinColumn(name = "person_id")
private Person reviewer;
@ManyToOne
@JoinColumn(name = "post_id")
private Post post;
// GetterとSetterメソッド
}
リポジトリインターフェースの作成
public interface PostRepository extends JpaRepository<Post, Long> {
}
public interface ReviewRepository extends JpaRepository<Review, Long> {
}
サービスクラスの実装
@Service
public class BlogService {
@Autowired
private PersonRepository personRepository;
@Autowired
private PostRepository postRepository;
@Autowired
private ReviewRepository reviewRepository;
public Post writePost(String title, String content, Long authorId) {
Person author = personRepository.findById(authorId).orElseThrow(() -> new RuntimeException("ユーザーが見つかりません"));
Post post = new Post();
post.setTitle(title);
post.setContent(content);
post.setAuthor(author);
return postRepository.save(post);
}
public Review addReview(String content, Long postId, Long reviewerId) {
Person reviewer = personRepository.findById(reviewerId).orElseThrow(() -> new RuntimeException("ユーザーが見つかりません"));
Post post = postRepository.findById(postId).orElseThrow(() -> new RuntimeException("記事が見つかりません"));
Review review = new Review();
review.setContent(content);
review.setReviewer(reviewer);
review.setPost(post);
return reviewRepository.save(review);
}
}