javaoracle-databasehqlnamed-queryquarkus-panache

Named query for entity that does not associate with a specific table


I'm using Quarkus and Hibernate / Panache.

For this example, I have 3 tables (table_a, table_b, table_c) that I am joining together using a native query. In the project I'm working on, it's around 5 JOIN tables to retrieve the information I'm looking for.

table_b is purely a mapping / join table for table_a and table_c:

SELECT 
  a.id,
  a.name, 
  c.login_date 
FROM 
  table_a a 
  JOIN table_b b ON b.a_id = a.id 
  JOIN table_c c ON b.c_id = c.id 
WHERE 
  c.login_date > '01-MAY-21'

I'm porting the above to HQL. I've mapped all my @Entity classes with their respective @Table, along with their @Column names. We're good in that department.

SELECT 
  a.id,
  a.name, 
  c.loginDate 
FROM 
  TableA a 
  JOIN TableA b ON b.aId = a.id 
  JOIN TableB c ON b.cId = c.id 
WHERE 
  c.loginDate > '01-MAY-21'

I'm only looking for name and login_date. There is a bunch of other information stored in table_a and table_c that I don't want for this specific query. So I created an entity for this call:

@Entity
@IdClass(LoginDetailsPk.class)
@NamedQuery(
    name = "LoginDetails.findFromDate",
    query = "FROM TableA a " +
            "JOIN TableA b ON b.aId = a.id " +
            "JOIN TableB c ON b.cId = c.id " +
            "WHERE c.loginDate > '01-MAY-21'"
)
public class LoginDetails extends PanacheEntityBase {

    @Id
    private int id;

    @Id
    private String name;

    @Id
    private String loginDate;

    public static List<LoginDetails> findFromDate(String fromDate) {
        // Eventually pass fromDate into find()
        return find("#LoginDetails.findFromDate").list();
    }

}

I'm having a hard time trying to understand why the return even works. When I invoke LoginDetails.findFromDate(...) and store it in a List<LoginDetails>, it works fine. However, when I try to access the list, I get a ClassCastException error.

List<LoginDetails> details = LoginDetails.findFromDate(null);

for(LoginDetails detail : details) { // <------ Throws a class cast exception
   //...
}

After debugging, I'm noticing that generic type stored in my List isn't even my LoginDetails class; rather, it's an array of objects (List<Object[]>) with all my @Entities and the irrelevant information I'm not looking for.

I'm lost. Would it make more sense to move back to a native query?


Solution

  • Your HQL is creating a Object[] for every row in the result, because you are not specifying any SELECT, and by default all the objects in the FROM clause are included in that Object array. If you want to return a LoginDetails object you need to create a constructor with all the attributes:

    public LoginDetails(int id, String name, String loginDate) {
      this.id = id;
      this.name = name;
      this.loginDate = loginDate;
    }
    

    And then change the query to:

    query = "SELECT new LoginDetails(a.id, a.name, c.loginDate) "
      "FROM TableA a " +
      "JOIN TableA b ON b.aId = a.id " +
      "JOIN TableB c ON b.cId = c.id " +
      "WHERE c.loginDate > '01-MAY-21'"
    

    See https://docs.jboss.org/hibernate/core/3.5/reference/en/html/queryhql.html#queryhql-select