JPA entity design, relationships, migrations, indexing, optimistic locking, and query optimization.
This skill provides the canonical database and JPA patterns for the project.
@Entity
@Table(name = "recipes")
public class Recipe {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 255)
private String name;
@Column(columnDefinition = "TEXT")
private String description;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;
@OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Ingredient> ingredients = new ArrayList<>();
@Version
private Integer version;
@CreationTimestamp
@Column(updatable = false)
private Instant createdAt;
@UpdateTimestamp
private Instant updatedAt;
}
GenerationType.IDENTITY@Version for optimistic locking on all mutable entities.@CreationTimestamp and @UpdateTimestamp for audit columns.FetchType.LAZY for all @ManyToOne and @OneToMany relationships.CascadeType.ALL + orphanRemoval = true for owned collections.nullable = false on required columns.length on VARCHAR columns.User ──< Recipe (OneToMany)
Recipe ──< Ingredient (OneToMany, cascade ALL, orphanRemoval)
public interface RecipeRepository extends JpaRepository<Recipe, Long> {
Page<Recipe> findAllByUserId(Long userId, Pageable pageable);
Optional<Recipe> findByIdAndUserId(Long id, Long userId);
boolean existsByNameAndUserId(String name, Long userId);
}
JpaRepository<Entity, Long>.@Query with JPQL for complex queries.-- Index foreign keys
CREATE INDEX idx_recipes_user_id ON recipes(user_id);
-- Index commonly searched/sorted columns
CREATE INDEX idx_recipes_name ON recipes(name);
CREATE INDEX idx_recipes_created_at ON recipes(created_at DESC);
ddl-auto=create or ddl-auto=update in production.V2__add_ingredients_table.sql.Pageable for all list queries — never return unbounded result sets.@EntityGraph or JOIN FETCH to avoid N+1 queries.spring.jpa.show-sql=true in development.