Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Practical HQL Query Patterns: From Single-Table Retrieval to Complex Joins

Tech 1

Entity Maping Configuration

Consider a scenario mapping a bidirectional association between Division and Personnel. The Division entity represents organizational units, while Personnel represents employees assigned to those units.

@Entity
@Table(name = "organizational_unit")
public class Division {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(name = "unit_designation")
    private String unitName;
    
    @OneToMany(mappedBy = "division", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    private Set<Personnel> members = new HashSet<>();
    
    // Accessors omitted for brevity
}

@Entity
@Table(name = "employee")
public class Personnel {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long pk;
    
    private String fullName;
    private String role;
    private Double annualSalary;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "unit_id", nullable = true)
    private Division division;
    
    // Accessors omitted for brevity
}

Single-Table Query Patterns

Full entity retrieval loads all persistent properties of the mapped class:

String query = "from Personnel p";
List<Personnel> staff = session.createQuery(query, Personnel.class)
    .getResultList();

For retrieving specific attributes rather than complete entities, use projection. This returns a list of object arrays where each index corresponds to the selected field order:

String projection = "select p.fullName, p.annualSalary from Personnel p";
List<Object[]> attributes = session.createQuery(projection, Object[].class)
    .getResultList();

for (Object[] row : attributes) {
    String name = (String) row[0];
    Double salary = (Double) row[1];
}

To eliminate duplicate values from result sets:

String distinctRoles = "select distinct p.role from Personnel p";
List<String> uniquePositions = session.createQuery(distinctRoles, String.class)
    .getResultList();

Conditional Filtering and Sorting

Where clauses support logical operators, comparison predicates, and pattern matching:

String conditional = "from Personnel p where p.annualSalary between :min and :max " +
                    "and p.division is not null and p.fullName like :pattern";
List<Personnel> filtered = session.createQuery(conditional, Personnel.class)
    .setParameter("min", 60000.0)
    .setParameter("max", 120000.0)
    .setParameter("pattern", "S%")
    .getResultList();

Sorting results requires the order by clause:

String ordered = "from Personnel p order by p.annualSalary desc, p.fullName asc";
List<Personnel> sorted = session.createQuery(ordered, Personnel.class)
    .getResultList();

Pagination and Aggregation

Limit result sets using firstResult and maxResults:

String paginated = "from Personnel p order by p.pk";
List<Personnel> page = session.createQuery(paginated, Personnel.class)
    .setFirstResult(20)
    .setMaxResults(10)
    .getResultList();

Aggregate functions compute values across result sets:

String aggregates = "select count(p), avg(p.annualSalary), max(p.annualSalary) from Personnel p";
Object[] statistics = session.createQuery(aggregates, Object[].class)
    .getSingleResult();

Grouping operations categorize results, with having clauses filtering aggregated groups:

String grouped = "select p.role, count(p), avg(p.annualSalary) from Personnel p " +
                "group by p.role having count(p) > 2";
List<Object[]> roleStatistics = session.createQuery(grouped, Object[].class)
    .getResultList();

Multi-Table Join Operations

Inner joins return records with matching values in both entities:

String innerJoin = "select p.fullName, d.unitName from Personnel p join p.division d";
List<Object[]> innerResults = session.createQuery(innerJoin, Object[].class)
    .getResultList();

Outer joins preserve records from one table regardless of matches. Left outer joins retain all division records even without assigned personnel:

String leftOuter = "select d.unitName, p.fullName from Division d left join d.members p";
List<Object[]> outerResults = session.createQuery(leftOuter, Object[].class)
    .getResultList();

Handling Non-Entity Result Sets

When queries combine fields from multiple tables without targeting a specific entity, scalar results provide generic data access:

String scalar = "select p.fullName, d.unitName, p.annualSalary from Personnel p, Division d " +
               "where p.division.id = d.id";
List<Object[]> tuples = session.createQuery(scalar, Object[].class).getResultList();

for (Object[] tuple : tuples) {
    System.out.printf("Employee: %s, Unit: %s, Compensation: %s%n", 
                     tuple[0], tuple[1], tuple[2]);
}

Alternatively, constructor expressions map results directly to Data Transfer Objects:

public class PersonnelView {
    private String employeeName;
    private String departmentName;
    private Double compensation;
    
    public PersonnelView(String employeeName, String departmentName, Double compensation) {
        this.employeeName = employeeName;
        this.departmentName = departmentName;
        this.compensation = compensation;
    }
}

String constructorQuery = "select new com.example.PersonnelView(" +
                         "p.fullName, d.unitName, p.annualSalary) " +
                         "from Personnel p join p.division d";
List<PersonnelView> views = session.createQuery(constructorQuery, PersonnelView.class)
    .getResultList();

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.