spring-data-jpa
Skill344 starsChanged 3 months agoMITView on GitHub
Use when generating JPA entities, repositories, queries, or anything touching the persistence layer. Covers entity conventions, N+1 prevention, projections, and query patterns.
2 other projects use a version of this.
Adopted by
Most recently taken up by
- Amplicode/spring-skillstheir own version, first seen Sep 2, 2026
- james-paul25/claude-skilltheir own version, first seen Jul 11, 2026
Other versions2 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 2
- Amplicode/spring-skills★ 126same name
- james-paul25/claude-skill★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository77 other files it gives its agents.
- agent-qa-browser-regressionSkill
- ai-observabilitySkill
- api-versioningSkill
- configuration-propertiesSkill
- container-native-deploymentSkill
- domain-driven-designSkill
- event-driven-messagingSkill
- flyway-migrationsSkill
- hateoasSkill
- hexagonal-architectureSkill
- http-interface-clientsSkill
- idempotency-patternsSkill
- java-supply-chain-securitySkill
- layered-architectureSkill
- mcp-serverSkill
- multi-module-mavenSkill
- multi-tenancySkill
- null-safetySkill
- oauth2-resource-serverSkill
- openapi-firstSkill
- problem-details-rfc9457Skill
- production-observabilitySkill
- resilience-retrySkill
- rest-api-conventionsSkill
- security-audit-loggingSkill
- spring-ai-integrationSkill
- spring-ai-securitySkill
- spring-batchSkill
- spring-boot-migrationSkill
- spring-cloud-gatewaySkill
- spring-data-redisSkill
- spring-incident-responseSkill
- spring-modulithSkill
- spring-security-jwtSkill
- spring-security-reviewSkill
- testing-pyramidSkill
- transactional-patternsSkill
- webflux-reactive-patternsSkill
- agent-qa-browser-regressionSkill
- ai-observabilitySkill
- api-versioningSkill
- configuration-propertiesSkill
- container-native-deploymentSkill
- domain-driven-designSkill
- event-driven-messagingSkill
- flyway-migrationsSkill
- hateoasSkill
- hexagonal-architectureSkill
- http-interface-clientsSkill
- idempotency-patternsSkill
- java-supply-chain-securitySkill
- layered-architectureSkill
- mcp-serverSkill
- multi-module-mavenSkill
- multi-tenancySkill
- null-safetySkill
- oauth2-resource-serverSkill
- openapi-firstSkill
- problem-details-rfc9457Skill
- production-observabilitySkill
- The first 60 shown.
The file
Files
Spring Data JPA
Entity Conventions
@Entity
@Table(name = "orders")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED) // JPA requires no-arg, hide from callers
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
@Column(updatable = false, nullable = false)
private UUID id;
@Column(nullable = false)
private String customerEmail;
@Enumerated(EnumType.STRING) // always STRING, never ORDINAL
@Column(nullable = false)
private OrderStatus status;
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
private List<OrderItem> items = new ArrayList<>();
@CreationTimestamp
@Column(updatable = false)
private Instant createdAt;
@UpdateTimestamp
private Instant updatedAt;
// Static factory, not public constructor
public static Order create(String customerEmail) {
Order order = new Order();
order.customerEmail = customerEmail;
order.status = OrderStatus.PENDING;
return order;
}
// Behavior on entity, not in service
public void addItem(Product product, int quantity) {
items.add(OrderItem.create(this, product, quantity));
}
}Rules
@Enumerated(EnumType.STRING)always —ORDINALbreaks on enum reorderingGenerationType.UUIDfor IDs — never expose auto-increment integers@NoArgsConstructor(access = PROTECTED)— required by JPA, hidden from app code@Getterfrom Lombok — no@Setteron entities (use behavior methods)- Collections initialized inline (
= new ArrayList<>()) — never null
N+1 Prevention
Identify: One query for orders + N queries for each order's items = N+1.
Fix with JOIN FETCH:
@Query("SELECT o FROM Order o JOIN FETCH o.items WHERE o.id = :id")
Optional<Order> findByIdWithItems(@Param("id") UUID id);
// For lists — use @EntityGraph to avoid duplicates
@EntityGraph(attributePaths = {"items", "items.product"})
List<Order> findByStatus(OrderStatus status);Fix with Projections for read-only views:
// Interface projection — no entity loaded
public interface OrderSummary {
UUID getId();
String getCustomerEmail();
OrderStatus getStatus();
Instant getCreatedAt();
}
List<OrderSummary> findByStatus(OrderStatus status); // fast, no lazy loading issuesQuery Patterns
public interface OrderRepository extends JpaRepository<Order, UUID> {
// Derived query — simple conditions
List<Order> findByStatusAndCustomerEmail(OrderStatus status, String email);
// JPQL — for joins and complex conditions
@Query("SELECT o FROM Order o JOIN FETCH o.items WHERE o.status = :status")
List<Order> findActiveOrdersWithItems(@Param("status") OrderStatus status);
// Native SQL — only when JPQL can't do it
@Query(value = "SELECT * FROM orders WHERE created_at > NOW() - INTERVAL '7 days'",
nativeQuery = true)
List<Order> findRecentOrders();
// Exists check — faster than findById + isPresent
boolean existsByCustomerEmailAndStatus(String email, OrderStatus status);
// Projection
List<OrderSummary> findByCustomerEmail(String email);
}Pagination
// Always use Pageable for list endpoints
Page<Order> findByStatus(OrderStatus status, Pageable pageable);
// In service
Page<Order> orders = orderRepository.findByStatus(status, PageRequest.of(page, size, Sort.by("createdAt").descending()));Bidirectional Relationships
// Parent side (Order)
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
private List<OrderItem> items = new ArrayList<>();
// Child side (OrderItem) — owns the FK
@ManyToOne(fetch = FetchType.LAZY) // LAZY always on @ManyToOne
@JoinColumn(name = "order_id", nullable = false)
private Order order;
// Helper on parent to keep both sides in sync
public void addItem(OrderItem item) {
items.add(item);
item.setOrder(this);
}Deep Pagination — Keyset over OFFSET
OFFSET pagination scans and discards every skipped row. On page 5,000 the DB reads 100,000 rows to
return 20. For large or infinite-scroll datasets, paginate by the last seen key (the "seek" method):
// ❌ Slow on deep pages — OFFSET grows linearly
Page<Order> findByStatus(OrderStatus status, Pageable pageable);
// ✅ Keyset — constant time regardless of depth. Pass the last row's createdAt + id.
@Query("""
SELECT o FROM Order o
WHERE o.status = :status
AND (o.createdAt < :lastCreatedAt
OR (o.createdAt = :lastCreatedAt AND o.id < :lastId))
ORDER BY o.createdAt DESC, o.id DESC
""")
List<Order> findNextPage(OrderStatus status, Instant lastCreatedAt, UUID lastId, Limit limit);The (createdAt, id) tuple breaks ties so the cursor is stable when timestamps collide. Index (status, created_at DESC, id DESC).
Batch Inserts
Saving a list one row at a time is N round-trips. Enable JDBC batching so Hibernate groups them:
spring:
jpa:
properties:
hibernate:
jdbc.batch_size: 50
order_inserts: true
order_updates: trueCaveat: GenerationType.IDENTITY silently disables insert batching (Hibernate needs the generated key
per row). GenerationType.UUID or a pooled sequence preserves it — another reason to prefer UUIDs.
Gotchas
- Agent uses
FetchType.EAGER— always useLAZYon@ManyToOneand@ManyToMany - Agent uses
@Enumerated(EnumType.ORDINAL)— always useSTRING - Agent uses
LongIDs — useUUID - Agent calls
findAll()for list endpoints — always usePageable - Agent uses
OFFSETpagination on huge tables — switch to keyset for deep pages - Agent adds setters to entities — use behavior methods instead
- Agent forgets
orphanRemoval = trueon@OneToMany— child records become orphans - Agent writes N+1 without realizing — check for
itemsaccess in loops - Agent batches inserts with
GenerationType.IDENTITY— batching is silently off; useUUID/sequence
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

