api-contract-review
Review REST API contracts for HTTP semantics, versioning, backward compatibility, and response consistency. Use when user asks…
JPA/Hibernate patterns and common pitfalls (N+1, lazy loading, transactions, queries). Use when user has JPA performance issues, LazyInitializationException, or asks about entity relationships and fetching strategies.
Claude Code
$ npx -y skills add decebals/claude-code-java --skill jpa-patterns --agent claude-code
Codex
$ npx skills add decebals/claude-code-java --skill jpa-patterns -a codex -y
Cursor
$ npx skills add decebals/claude-code-java --skill jpa-patterns -a cursor -y
Gemini CLI
$ npx skills add decebals/claude-code-java --skill jpa-patterns -a gemini-cli -y
GitHub Copilot
$ npx skills add decebals/claude-code-java --skill jpa-patterns -a github-copilot -y
OpenCode
$ npx skills add decebals/claude-code-java --skill jpa-patterns -a opencode -y
Windsurf
$ npx skills add decebals/claude-code-java --skill jpa-patterns -a windsurf -y
name: jpa-patterns description: JPA/Hibernate patterns and common pitfalls (N+1, lazy loading, transactions, queries). Use when user has JPA performance issues, LazyInitializationException, or asks about entity relationships and fetching strategies. license: MIT
Best practices and common pitfalls for JPA/Hibernate in Spring applications.
---
| Problem | Symptom | Solution | |---------|---------|----------| | N+1 queries | Many SELECT statements | JOIN FETCH, @EntityGraph | | LazyInitializationException | Error outside transaction | Open Session in View, DTO projection, JOIN FETCH | | Slow queries | Performance issues | Pagination, projections, indexes | | Dirty checking overhead | Slow updates | Read-only transactions, DTOs | | Lost updates | Concurrent modifications | Optimistic locking (@Version) |
---
> The #1 JPA performance killer
// ❌ BAD: N+1 queries
@Entity
public class Author {
@Id private Long id;
private String name;
@OneToMany(mappedBy = "author", fetch = FetchType.LAZY)
private List<Book> books;
}
// This innocent code...
List<Author> authors = authorRepository.findAll(); // 1 query
for (Author author : authors) {
System.out.println(author.getBooks().size()); // N queries!
}
// Result: 1 + N queries (if 100 authors = 101 queries)// ✅ GOOD: Single query with JOIN FETCH
public interface AuthorRepository extends JpaRepository<Author, Long> {
@Query("SELECT a FROM Author a JOIN FETCH a.books")
List<Author> findAllWithBooks();
}
// Usage - single query
List<Author> authors = authorRepository.findAllWithBooks();// ✅ GOOD: EntityGraph for declarative fetching
public interface AuthorRepository extends JpaRepository<Author, Long> {
@EntityGraph(attributePaths = {"books"})
List<Author> findAll();
// Or with named graph
@EntityGraph(value = "Author.withBooks")
List<Author> findAllWithBooks();
}
// Define named graph on entity
@Entity
@NamedEntityGraph(
name = "Author.withBooks",
attributeNodes = @NamedAttributeNode("books")
)
public class Author {
// ...
}// ✅ GOOD: Batch fetching (Hibernate-specific)
@Entity
public class Author {
@OneToMany(mappedBy = "author")
@BatchSize(size = 25) // Fetch 25 at a time
private List<Book> books;
}
// Or globally in application.properties
spring.jpa.properties.hibernate.default_batch_fetch_size=25# Enable SQL logging to detect N+1
spring:
jpa:
show-sql: true
properties:
hibernate:
format_sql: true
logging:
level:
org.hibernate.SQL: DEBUG
org.hibernate.type.descriptor.sql.BasicBinder: TRACE---
@Entity
public class Order {
// LAZY: Load only when accessed (default for collections)
@OneToMany(mappedBy = "order", fetch = FetchType.LAZY)
private List<OrderItem> items;
// EAGER: Always load immediately (default for @ManyToOne, @OneToOne)
@ManyToOne(fetch = FetchType.EAGER) // ⚠️ Usually bad
private Customer customer;
}// ✅ GOOD: Always use LAZY, fetch when needed
@Entity
public class Order {
@ManyToOne(fetch = FetchType.LAZY) // Override EAGER default
private Customer customer;
@OneToMany(mappedBy = "order", fetch = FetchType.LAZY)
private List<OrderItem> items;
}// ❌ BAD: Accessing lazy field outside transaction
@Service
public class OrderService {
public Order getOrder(Long id) {
return orderRepository.findById(id).orElseThrow();
}
}
// In controller (no transaction)
Order order = orderService.getOrder(1L);
order.getItems().size(); // 💥 LazyInitializationException!**Solution 1: JOIN FETCH in query**
// ✅ Fetch needed associations in query
@Query("SELECT o FROM Order o JOIN FETCH o.items WHERE o.id = :id")
Optional<Order> findByIdWithItems(@Param("id") Long id);**Solution 2: @Transactional on service method**
// ✅ Keep transaction open while accessing
@Service
public class OrderService {
@Transactional(readOnly = true)
public OrderDTO getOrderWithItems(Long id) {
Order order = orderRepository.findById(id).orElseThrow();
// Access within transaction
int itemCount = order.getItems().size();
return new OrderDTO(order, itemCount);
}
}**Solution 3: DTO Projection (recommended)**
// ✅ BEST: Return only what you need
public interface OrderSummary {
Long getId();
String getStatus();
int getItemCount();
}
@Query("SELECT o.id as id, o.status as status, SIZE(o.items) as itemCount " +
"FROM Order o WHERE o.id = :id")
Optional<OrderSummary> findOrderSummary(@Param("id") Long id);**Solution 4: Open Session in View (not recommended)**
# Keeps session open during view rendering
# ⚠️ Can mask N+1 problems, use with caution
spring:
jpa:
open-in-view: true # Default is true---
@Service
public class OrderService {
// Read-only: Optimized, no dirty checking
@Transactional(readOnly = true)
public Order findById(Long id) {
return orderRepository.findById(id).orElseThrow();
}
// Write: Full transaction with dirty checking
@Transactional
public Order createOrder(CreateOrderRequest request) {
Order order = new Order();
// ... set propertiesAgent Skills for Java projects, following the open Agent Skills specification This project is not affiliated with Anthropic.
Review REST API contracts for HTTP semantics, versioning, backward compatibility, and response consistency. Use when user asks…
Analyze Java project architecture at macro level - package structure, module boundaries, dependency direction, and layering. Use…
Generate changelogs from git commits. Use when user says "generate changelog", "update changelog", "what changed since last…
Clean Code principles (DRY, KISS, YAGNI), naming, function design and readability. Use when code is hard to read, with long…
Review Java concurrency code for thread safety, race conditions, deadlocks, and modern patterns (Virtual Threads,…
Common design patterns with Java examples (Factory, Builder, Strategy, Observer, Decorator, etc.). Use when user asks "implement…
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
Ask which skill or flow fits your situation. A router over the skills in this repo.