JPA Entity Relationships, Naming Strategy, and Advanced Queries
Physical Naming Strategy
Section titled “Physical Naming Strategy”A Physical Naming Strategy controls how Java entity and field names map to physical database table and column names.
Example entity:
@Entityclass EmployeeDetails {}Without naming strategy:
EmployeeDetailsWith Spring Physical Naming Strategy:
employee_detailsBenefits
Section titled “Benefits”- Consistent naming convention
- Automatic camelCase to snake_case conversion
- Reduces manual
@Columnannotations - Matches enterprise database naming standards
Configuration:
spring.jpa.hibernate.naming.physical-strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategyNamed Queries and the Specification API
Section titled “Named Queries and the Specification API”Beyond derived methods, JPQL, and native queries, two more query mechanisms are worth knowing:
Named Query
Section titled “Named Query”@NamedQuery( name="Employee.findByDept", query="SELECT e FROM Employee e WHERE e.department=:dept")Specification API
Section titled “Specification API”Specification<Employee> spec = (root, query, cb) -> cb.equal(root.get("department"), "IT");Specifications let you build queries programmatically and compose them (e.g. combine multiple Specifications with .and()/.or()), which is useful for dynamic filtering that derived methods and static JPQL can’t express cleanly.
Range and Aggregate Queries
Section titled “Range and Aggregate Queries”Employees with Salary Between Two Values
Section titled “Employees with Salary Between Two Values”Repository method:
List<Employee> findBySalaryBetween(Long minSalary, Long maxSalary);Usage:
employeeRepo.findBySalaryBetween(3000000L, 3500000L);JPQL:
@Query("SELECT e FROM Employee e WHERE e.salary BETWEEN :min AND :max")List<Employee> findEmployees(Long min, Long max);Average Salary
Section titled “Average Salary”@Query("SELECT AVG(e.salary) FROM Employee e")Double findAverageSalary();Entity Relationships
Section titled “Entity Relationships”Example: one Customer can have many Orders.
erDiagram
CUSTOMER ||--o{ ORDER : places
Customer:
@Entitypublic class Customer {
@Id private Long id;
private String name;
@OneToMany(mappedBy = "customer", cascade = CascadeType.ALL) private List<Order> orders;}Order:
@Entitypublic class Order {
@Id private Long id;
@ManyToOne @JoinColumn(name="customer_id") private Customer customer;}Relationship types: One-to-One, One-to-Many, Many-to-One, Many-to-Many.
JOIN Queries
Section titled “JOIN Queries”A JPQL join lets you filter on a related entity’s fields directly, without needing to fetch the related entities eagerly:
@Query("SELECT o FROM Order o JOIN o.customer c WHERE c.name = :name")List<Order> findOrdersByCustomer(String name);This is distinct from a JOIN FETCH (used to solve the N+1 problem) — a plain JOIN is for filtering/joining conditions, not for eagerly loading the association into the result.
Key Takeaways
Section titled “Key Takeaways”- A Physical Naming Strategy is what converts
EmployeeDetailstoemployee_detailsautomatically, so you don’t need@Table/@Columnoverrides just to follow a snake_case DB convention. - Named Queries centralize JPQL alongside the entity; the Specification API is better suited to dynamic, composable filtering that a fixed query string can’t express.
- Entity relationships (
@OneToMany/@ManyToOne) are declared on both sides — the “many” side owns the foreign key (@JoinColumn), while the “one” side usesmappedByto point back at it. - A plain JPQL
JOINfilters on a related entity’s fields; useJOIN FETCHinstead when you actually need to eagerly load the association to avoid N+1 queries.