Spring Data JPA 参考
Spring Data JPA 完整指南:实体映射、Repository 接口、JPQL 查询、关系、分页和 N+1 防御。
1. @Entity 映射
@Entity
@Table(name = "articles", indexes = @Index(columnList = "slug"))
public class Article {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 300)
private String title;
@Enumerated(EnumType.STRING)
private ArticleStatus status = ArticleStatus.DRAFT;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "author_id", nullable = false)
private User author;
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "article_tags",
joinColumns = @JoinColumn(name = "article_id"),
inverseJoinColumns = @JoinColumn(name = "tag_id"))
private Set<Tag> tags = new HashSet<>();
@CreationTimestamp
private Instant createdAt;
}
2. Repository 接口
public interface ArticleRepository extends JpaRepository<Article, Long> {
Optional<Article> findBySlug(String slug);
List<Article> findByStatusOrderByCreatedAtDesc(ArticleStatus status);
boolean existsBySlug(String slug);
@Query("SELECT a FROM Article a WHERE a.status = 'PUBLISHED' ORDER BY a.createdAt DESC")
List<Article> findRecentPublished(Pageable pageable);
@Modifying
@Query("UPDATE Article a SET a.viewCount = a.viewCount + 1 WHERE a.id = :id")
void incrementViewCount(@Param("id") Long id);
}
3. 分页与排序
public Page<ArticleDto> findPublished(int page, int size, String sortBy) {
Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, sortBy));
return repo.findByStatus(ArticleStatus.PUBLISHED, pageable).map(ArticleDto::from);
}
4. 防止 N+1 问题
// 使用 @EntityGraph 避免 N+1
@EntityGraph(attributePaths = {"author", "tags"})
@Query("SELECT a FROM Article a WHERE a.status = :status")
List<Article> findWithAuthorAndTags(@Param("status") ArticleStatus status);