spring-data-jpa

v2026.09.24

Spring Data JPA for database access in Spring Boot applications. Covers repositories, entities, relationships, queries, pagination, and auditing. Based on production patterns from castellino and gestionale-presenze projects. USE WHEN: user mentions "JPA", "repository", "entity", "database queries", asks about "pagination", "Criteria API", "Specifications", "@Query", "relationships" DO NOT USE FOR: Spring Boot basics (use `spring-boot`), Spring Security (use `spring-security`), MongoDB (use `mongodb-expert`), SQL-only tasks (use `sql-expert`)

GitHub
安装命令
npx skhub add claude-dev-suite/spring-data-jpa
Markdown
SKILL.md

Spring Data JPA

Deep Knowledge: Use mcp__documentation__fetch_docs with technology: spring-data-jpa for comprehensive documentation.

Entity with Auditing

@Entity
@Table(name = "users")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@EntityListeners(AuditingEntityListener.class)
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length = 100)
    private String name;

    @Column(unique = true, nullable = false)
    private String email;

    @Enumerated(EnumType.STRING)
    @Column(nullable = false)
    private UserRole role = UserRole.USER;

    @Enumerated(EnumType.STRING)
    @Column(nullable = false)
    private UserStatus status = UserStatus.ACTIVE;

    @CreatedDate
    @Column(updatable = false)
    private LocalDateTime createdAt;

    @LastModifiedDate
    private LocalDateTime updatedAt;

    @CreatedBy
    @Column(updatable = false)
    private String createdBy;

    @LastModifiedBy
    private String updatedBy;
}

Repository Interface

@Repository
public interface UserRepository extends JpaRepository<User, Long> {

    // Derived query methods
    Optional<User> findByEmail(String email);
    boolean existsByEmail(String email);
    List<User> findByStatus(UserStatus status);
    List<User> findByRoleIn(List<UserRole> roles);

    // Query with JPQL
    @Query("SELECT u FROM User u WHERE u.status = :status AND u.role = :role")
    List<User> findByStatusAndRole(
        @Param("status") UserStatus status,
        @Param("role") UserRole role
    );

    // Native query
    @Query(value = "SELECT * FROM users WHERE email LIKE %:domain", nativeQuery = true)
    List<User> findByEmailDomain(@Param("domain") String domain);

    // Pagination
    Page<User> findByNameContainingIgnoreCase(String name, Pageable pageable);

    // Sorting
    List<User> findByStatus(UserStatus status, Sort sort);

    // Modifying queries
    @Modifying
    @Query("UPDATE User u SET u.status = :status WHERE u.id = :id")
    int updateStatus(@Param("id") Long id, @Param("status") UserStatus status);

    @Modifying
    @Query("DELETE FROM User u WHERE u.status = :status")
    int deleteByStatus(@Param("status") UserStatus status);
}

Relationships

// One-to-Many
@Entity
public class Department {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToMany(mappedBy = "department", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Employee> employees = new ArrayList<>();
}

@Entity
public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "department_id", nullable = false)
    private Department department;
}

// Many-to-Many
@Entity
public class User {
    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(
        name = "user_roles",
        joinColumns = @JoinColumn(name = "user_id"),
        inverseJoinColumns = @JoinColumn(name = "role_id")
    )
    private Set<Role> roles = new HashSet<>();
}

Pagination & Sorting

@Service
public class UserService {

    public Page<UserResponse> findAll(int page, int size, String sortBy, String direction) {
        Sort sort = Sort.by(Sort.Direction.fromString(direction), sortBy);
        Pageable pageable = PageRequest.of(page, size, sort);
        return userRepository.findAll(pageable)
            .map(userMapper::toResponse);
    }

    public Page<UserResponse> search(String query, Pageable pageable) {
        return userRepository.findByNameContainingIgnoreCase(query, pageable)
            .map(userMapper::toResponse);
    }
}

// Controller
@GetMapping
public ResponseEntity<Page<UserResponse>> findAll(
        @RequestParam(defaultValue = "0") int page,
        @RequestParam(defaultValue = "10") int size,
        @RequestParam(defaultValue = "createdAt") String sortBy,
        @RequestParam(defaultValue = "desc") String direction) {
    return ResponseEntity.ok(userService.findAll(page, size, sortBy, direction));
}

Specifications (Dynamic Queries)

public class UserSpecifications {

    public static Specification<User> hasStatus(UserStatus status) {
        return (root, query, cb) ->
            status == null ? null : cb.equal(root.get("status"), status);
    }

    public static Specification<User> hasRole(UserRole role) {
        return (root, query, cb) ->
            role == null ? null : cb.equal(root.get("role"), role);
    }

    public static Specification<User> nameContains(String name) {
        return (root, query, cb) ->
            name == null ? null : cb.like(cb.lower(root.get("name")),
                "%" + name.toLowerCase() + "%");
    }
}

// Repository extends JpaSpecificationExecutor
public interface UserRepository extends
        JpaRepository<User, Long>,
        JpaSpecificationExecutor<User> {}

// Usage
Specification<User> spec = Specification
    .where(UserSpecifications.hasStatus(UserStatus.ACTIVE))
    .and(UserSpecifications.hasRole(UserRole.ADMIN))
    .and(UserSpecifications.nameContains("john"));

List<User> users = userRepository.findAll(spec);

Enable Auditing

@Configuration
@EnableJpaAuditing
public class JpaConfig {

    @Bean
    public AuditorAware<String> auditorProvider() {
        return () -> Optional.ofNullable(SecurityContextHolder.getContext())
            .map(SecurityContext::getAuthentication)
            .filter(Authentication::isAuthenticated)
            .map(Authentication::getName);
    }
}

Key Annotations

AnnotationPurpose
@EntityJPA entity
@TableTable mapping
@IdPrimary key
@GeneratedValueAuto-generation strategy
@ColumnColumn mapping
@ManyToOne / @OneToManyRelationships
@QueryCustom JPQL/SQL
@ModifyingUpdate/Delete queries
@CreatedDate / @LastModifiedDateAuditing

When NOT to Use This Skill

  • Spring Boot application setup → Use spring-boot skill
  • REST API patterns → Use spring-web skill
  • MongoDB operations → Use mongodb-expert skill
  • Security configuration → Use spring-security skill
  • Raw SQL optimization → Use sql-expert skill
  • Reactive database access → Use spring-r2dbc skill

Anti-Patterns

Anti-PatternWhy It's BadCorrect Approach
N+1 queriesPoor performanceUse @EntityGraph or fetch joins
Missing @TransactionalData inconsistencyAlways use for write operations
Bidirectional relations without careInfinite recursionUse @JsonManagedReference/@JsonBackReference
Fetch EAGER everywhereLoads unnecessary dataUse LAZY, fetch only when needed
No paginationMemory issuesAlways paginate large results
Query in loopPerformance killerUse batch fetch or single query

Quick Troubleshooting

ProblemLikely CauseSolution
LazyInitializationExceptionAccessing lazy field outside transactionFetch in transaction or use @EntityGraph
MultipleBagFetchExceptionMultiple @OneToMany EAGER fetchUse @EntityGraph or separate queries
Slow queriesMissing indexes or N+1Add indexes, check query logs
No query resultsWrong method nameFollow naming convention or use @Query
Constraint violationEntity state mismatchCheck cascade and orphanRemoval
DetachedEntityExceptionEntity not managedUse merge() or reload entity

Reference Documentation

发现
标签

此技能尚未发布标签。

版本
最新版本元数据

版本

v2026.09.24

发布时间

Sep 24, 2026

分类

未分类

许可证

MIT

源路径

skills/backend-frameworks/spring-data-jpa

默认分支

main

最新提交

9496306

Tree SHA

fe4e2f1