For example, I have a base entity called 'MyEntityParent' and two sub types 'ZChild' and 'AChild'.
When using the following HQL, this will sort the result list by the type's internal integer value (same as the special class property):
select e from MyEntityParent e
order by type(e)
However, I need to sort the result list by their entity type's name. I.e., first, instances of type 'AChild', then, instances of type 'ZChild'.
Try
select e from MyEntityParent e order by e.class
From the Hibernate 3.6 documentation:
The special property class accesses the discriminator value of an instance in the case of polymorphic persistence. A Java class name embedded in the where clause will be translated to its discriminator value.
http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/queryhql.html#queryhql-where
But this is deprecated in Hibernate 4.1
Also this is not exactly your wish as it returns the discriminator value, not the exact type name if i read the docs correctly.
There is no straightforward solution, because HQL is translated to SQL and database is not aware of how the value maps to the name of the class. There is way to make it, but it is not nice:
Query gets complex, because we have to define order in query.
Select list contains additional value that defines order, so we cannot return list of entities.
When model changes, query should be modified
Query is:
SELECT e,
CASE TYPE(e) WHEN AChild THEN 1
WHEN ZChild THEN 2
ELSE 3
END AS orderValue
FROM MyEntityParent e ORDER BY orderValue
And as said, result is no more list of entities, it is list of object arrays, so accessing entity is bit more hard. Entity itself is first item in array (index 0) and ordedValue in second:
List<Object[]> result = em.createQuery("JPQL given above").getResultList();
for (Object[] resultRow : result) {
MyEntityParent myEntityParent = (MyEntityParent) resultRow[0];
}
Solution is valid JPQL, so it is not Hibernate specific, but works also with other JPA providers.
Related
We're in the process of converting our java application from using SOLR/Lucene to Elasticsearch 5.6.6.
In 1 particular area, we would previously build 1 Lucene Search BooleanQuery and run it against 3 different entities, looking for matches. We didn't need to specify the entity it would be used against until you ran the actual query.
BooleanQuery luceneQuery = myQueryBuilder.buildMyQuery();
session.createFullTextQuery(luceneQuery, entityOne);
session.createFullTextQuery(luceneQuery, entityTwo);
session.createFullTextQuery(luceneQuery, entityThree);
One sub-query within [luceneQuery] above searched on taxId, which entityOne doesn't have (no taxId indexed field) but the other 2 entities do have. However it all worked fine, no exceptions were given, I believe it just ignored the unknown/un-indexed field, not exactly sure how it worked, but it did.
Now we're converting over to Elasticsearch DSL, we need to give the entity up front so I (for better or worse) build the query 3 different times, against each entity, like so:
QueryBuilder entityOneQB = session.getSearchFactory().buildQueryBuilder().forEntity(EntityOne.class).get();
QueryBuilder entityTwoQB = session.getSearchFactory().buildQueryBuilder().forEntity(EntityTwo.class).get();
QueryBuilder entityThreeQB = session.getSearchFactory().buildQueryBuilder().forEntity(EntityThree.class).get();
// Create 3 exact (except for which entity they point at) queries
Query entityOneQuery = myQueryBuilder.buildMyQuery(entityOne);
Query entityTwoQuery = myQueryBuilder.buildMyQuery(entityTwo);
Query entityThreeQuery = myQueryBuilder.buildMyQuery(entityThree);
Where buildMyQuery() has a number of sub-queries but the one dealing with taxId looks something like:
qb.bool().should(
qb.keyword()
.onField("taxId")
.matching(taxId)
.createQuery()
);
However, now, since, entityOne doesn't have taxId as an indexed column/field, createQuery() throws an exception:
SearchException: Unable to find field taxId in EntityOne
My questions are:
Is there some way to tell Lucene to ignore the field if the entity doesn't have it?
If not, is there some way, using the passed in QueryBuilder to determine what the entity is, so that, within the taxId subquery code, I can basically say if (entityType == EntityOne) {return null;} so that this particular sub-query won't be included in the overall query?
Is there some way to tell Lucene to ignore the field if the entity doesn't have it?
Just a clarification: it's Hibernate Search that implements the DSL and throws exceptions, not Lucene. Lucene is the underlying technology, and doesn't perform much validation.
If your goal is to retrieve all three entities in a single result list, and if fields with the same name in different entity types are configured similarly (e.g. field "name" appears in entity 1 and 2, but has the same analyzer), you could simply build a single query and retrieve all three types in that single query. You will have to:
Make sure, when building the single Lucene query, to always use the query builder of an entity type that actually defines the field your targeting: if targeting taxId for instance, you can use the query builder for EntityTwo or for EntityThree, but not the one for EntityOne. Yes, that's right: you can mix multiple query builders in a single query, as long as fields with the same name are configured similarly in all targeted entities.
build the FullTextQuery that way: session.createFullTextQuery(luceneQuery, EntityOne.class, EntityTwo.class, EntityThree.class);.
If not, is there some way, using the passed in QueryBuilder to determine what the entity is, so that, within the taxId subquery code, I can basically say if (entityType == EntityOne) {return null;} so that this particular sub-query won't be included in the overall query?
No, there is not. You could pass add a parameter to your method to pass the entity type, though: buildMyQuery(Class<?> type, QueryBuilder queryBuilder) instead of buildMyQuery(QueryBuilder queryBuilder).
I have a situation where I need to return only few fields of a POJO.
Here is a SOF Question: retrieve-single-field-rather-than-whole-pojo-in-hibernate question regarding the same, but few things still seems to be obscure.
1) The answer suggests to use -
String employeeName = session.createQuery("select empMaster.name from EmployeeMaster empMaster where empMaster.id = :id").setInteger("id",10).uniqueResult();
So, here is my concern - Every pojo field is normally private, so "empMaster.name" will simply not work. And am not sure if empMaster.getName() is the solution for this. Will calling the getter methods work?
2) If i am querying multiple fields, (which is my case) (assuming getter methods work) the query will be some thing like -
List<String> employeeDetails = session.createQuery("select empMaster.getName(), empMaster.getDesignation() from EmployeeMaster empMaster where empMaster.id = :id").setInteger("id",10).uniqueResult();
Note the return type has changed from String to List<String>.
2(a) Hope this is right?
2(b) what if i am interested in age/salary of employee which will be of int type. I think the return type will be List<String> or List<Object>. Well any how in the application i can recast the String or Object to the proper type int or float etc. So this should not be a problem.
3) Now what if I am querying multiple employee details (no where clause), so the query will be something like - (not sure if the part of query after from is correct)
List<List<<String>> employeesDetails = session.createQuery("select empMaster.getName(), empMaster.getDesignation() from EmployeeMaster;
Anyway, point here is to emphasise the change in the return type to : List<List<<String>> employeesDetails. Does it work this way ???.
(The question quoted above also has answers pointing to use Projections. I have questions about it but will post them on another question, don't want to mixup.)
I will list the points in the order you mentioned them:
The query has nothing to do with the POJO's field visibility. You are doing a simple query to the database, as if you were doing a query using SQL, and columns in a table have nothing to do with the fact that their mapped POJOs' fields in an application are public or private. The difference is only the language that you're using: now you're using the Hibernate Query Language (HQL), which allows you to express your query with respect to the POJOs' definitions instead of the database's tables' definitions. In fact, doing
session.createQuery("select empMaster.getName() from EmployeeMaster...");
will throw a syntax error: there can be no parenthesis in an object's field name.
By the way, you have to parse your query result to a String, otherwise there would be a compiler semantics error.
String name = (String) session.createQuery("select empMaster.name from EmployeeMaster...").setYourParameters().uniqueResult();
When you do a query whose SELECT clause contains more than one field, and you call uniqueResult(), you're obtaining an array of Objects (Object[]). You iterate through each element in the array and cast it to the corresponding type.
First of all, you probably forgot to add a .list() method call at the end of your query.
session.createQuery("select empMaster.name, empMaster.designation from EmployeeMaster").list();
Otherwise, the query will not be executed, and you would just have a QueryImpl object waiting to be triggered.
Now, when you're expecting multiple results in your query and selecting several fields, you're getting back a List<Object[]>. The elements of the list will be the array of fields for a specific record, and the array per se will be the fields.
How would you get a list/array of the distinct values in a table using JPA?
Lets say I have an entity and repository of a Foo object from a table that has columns a, b, c. All I want is to determine all unique(distinct) values in column b where c equals "bar" and have it (the distinct values of b)returned as a list of Strings (not Foo objects).
Other ORM's I have found it very simple to do, but I can't seem to figure out how to do this via JPA. It's a query that's not mapped to an object, but rather just extracting values as a simple list of Strings.
Can this be done in JPA, if so, how?
Just use this simple JPQL query:
select distinct foo.b from Foo foo where foo.c = 'bar'
query is one way as said by JB Nizet
Another way with java8, i would define these methods in repository itself. Find distinct Foos and then convert to list of b and return.Call this method with any parameter you want.
public interface FooRepository extends CrudRepository.. {
// name this method whatever you want
default List<String> findDistinctbs(final String b) {
// note following is jpa convention to get list of Foos..
List<Foo> fooList = findDistinctByb(String b);
List<String> bList = fooList.stream().map(Foo::getB).collect(Collectors.toList());
return bList;
}
I'm trying to do something which is easy as pie in PHP & Co:
SELECT COUNT(x) as numItems, AVG(y) as average, ... FROM Z
In PHP I would get a simple array like [{ numItems: 0, average: 0 }] which I could use like this:
echo "Number of Items: " . $result[0]['numItems'];
Usually in JPQL you only query single objects or single columns and get Lists types, for example List<SomeEntity> or List<Long>. But what do you get, when querying multiple columns?
You get an Object[] (or a List<Object[]>). From the section 4.8.1 Result Type of the SELECT Clause of the JPA 1.0 specification:
The result type of the SELECT clause
is defined by the the result types of
the select_expressions contained in
it. When multiple select_expressions
are used in the SELECT clause, the
result of the query is of type
Object[], and the elements in this
result correspond in order to the
order of their specification in the
SELECT clause and in type to the
result types of each of the
select_expressions.
If you want strong typing, you can use a constructor expression in the SELECT clause. From the section 4.8.2 Constructor Expressions in the SELECT Clause:
A constructor may be used in the
SELECT list to return one or more Java
instances. The specified class is not
required to be an entity or to be
mapped to the database. The
constructor name must be fully
qualified.
If an entity class name is specified
in the SELECT NEW clause, the
resulting entity instances are in the
new state.
SELECT NEW com.acme.example.CustomerDetails(c.id, c.status, o.count)
FROM Customer c JOIN c.orders o
WHERE o.count > 100
You can also use Tuple and return a list of Tuple (List<Tuple>) which you can use as a list of Map.
I'm doing some weird reporting from a JPA data store.
I need to select (using EJBQL) a list of objects. These objects contain collection of entities. I have a class that is constructed by:
FOOBean(String param1,
String param2,
List<Entity> listParam)
(notice that third parameter is a list)
And I want to select a list of these beans using GROUP BY, fetching listParam along, so I would like to write a query that works like this:
SELECT new FOOBean(
e1.param1,
e1.param2,
e1)
FROM Entity e1
GROUP BY e1.param1, e1.param2
So that grouped entities are fetched into a list and inserted into the third parameter. Is it possible to do it that way or do I have to create two queries that selects distinct pairs of param1 and param2; and fetches all entities with appropriate param values respectively?
It is not possible, at least in JPA 1.0 (and I doubt that in JPA 2.0 it is different).
I think it would be much better to retrieve the object based on your condition & then use #oneToMany annotaion in your entity to set up the list.