HQL - Select nested objects - java

Consider the following scenario: two database tables (producers and produce), with a one to many relation (one produce is created by one producer, one producer has many products), each table with multiple fields (including name and id).
Also, consider I have two managed entities (Producer and Produce) and two smaller versions of the these objects (unmanaged) used as DTOs (ProducerDTO and ProduceDTO) with only name and ID (and the relation).
I want to select using an HQL query a specific product (as a DTO) with it's producer set as well. More clearly:
SELECT new ProduceDTO(produce.id, new ProducerDTO(producer.id, producer.name), produce.name) FROM Produce produce JOIN produce.producer producer WHERE ...
But I get a QuerySyntaxException: unexpected token: , after produce.id in the query. So my question would be: is it possible to select nested custom objects in HQL and if so, what are the rules / limitations for this? For a single custom object, it works just fine, but when I try to nest 2 or more, I'm having issues.
Note: the question is to better understand the phenomenon, not necessarily for this specific case (I already have a workaround for my case with my actual classes).
Thanks!

The full JPA query language syntax specification can be found in official Java EE 7 documentation here: http://docs.oracle.com/javaee/7/tutorial/persistence-querylanguage005.htm#BNBUF
Specifically, the parts that answer your question are:
constructor_expression ::= NEW constructor_name(constructor_item {, constructor_item}*)
constructor_item ::= single_valued_path_expression
| aggregate_expression
So this basically means
You can't use a null constructor.
Each parameter can be either single_valued_path_expression or aggregate_expression.
Further, constructor_expression is present only in select_expression that can be used only as a top-level query construct, so per standard JPQL you can't do tricks with nested select ... statements. I couldn't find a complete specification of HQL online, so not sure about that one.

Related

Can createNativeQuery of EnityManager return resultClass that is dojo class

I can have the following script:
1. I have an entity class - EntityClass (mapping with EntityTable)
2. I have a dojo class - DojoClass
3. Both of the classes have the same variables.
Question:
What will be returned when I call the following method:
A- entityManager.createNativeQuery("SELECT * FROM EntityTable",EntityClass.class);
B- entityManager.createNativeQuery("SELECT * FROM EntityTable",Dojo.class);
Are they correct?
This is quite an unusual use case and i am guessing only theoretical.
I have made configuration just as you have, most notably that both of the entities have exactly the same basic fields (these are the ones that will be taken under consideration while mapping the results to the classes).
I am using JPA 2.1 and Hibernate 5.x as the implementation under the hood.
To my bewilderment both queries run properly and assign the results as expected to corresponding fields in each of the classes.
I went a bit deeper into the implementation and basically what is happening behind the scenes is:
Creation of a list of type List<Object[]> as the first step, which is the standard behavior for the createNativeQuery(String) method without the class specified.
Setting the fields of a class using reflection. So it does not really matter from which table we are pulling the results.. the column names and types must match..
And few tips for the end:
Class specified as the second parameter has to always be a managed entity or embeddable
When i tried to query for only certain column(s) i was getting errors with the missing columns lacking in the query. So i guess you always have to select everything
Hope this clarifies things.

Spring Data JPA difference between findBy / findAllBy

Is there any difference when using Spring Data JPA keywords between:
List<SomeEntity> findBySomeCondition();
and
List<SomeEntity> findAllBySomeCondition();
No, there is no difference between them, they will execute exactly the same query, the All part is ignored by Spring Data when deriving the query from the method name. The only important bit is the By keyword, anything following it is treated as a field name (with the exception of other keywords like OrderBy which incidentially can lead to some strange looking method names like findAllByOrderByIdAsc).
This means something like this is perfectly valid:
List<SomeEntity> findAnythingYouWantToPutHereBySomeCondition();
And will execute exactly the same SQL query as:
List<SomeEntity> findBySomeCondition();
or
List<SomeEntity> findAllBySomeCondition();
The documentation for the 2.3.6 release of Spring Data discusses this feature:
Any text between find (or other introducing keywords) and By is considered to be descriptive unless using one of the result-limiting keywords such as a Distinct to set a distinct flag on the query to be created or Top/First to limit query results.
The purpose of feature was explained in a blog post about the then-upcoming 2.0 release of Spring Data:
Spring Data’s method parsing uses prefix keywords like find, exists, count, and delete and a terminating By keyword. Everything you put in between find and By makes your method name more expressive and does not affect query derivation.
To illustrate the difference lets look at the two functions:
1. Set<Policy> findAllByRoleIn(Iterable<Role> role);
2. Set<Policy> findByRoleIn(Iterable<Role> role);
The query generated by 1st function:
1. select policy.id, policy.role from policy where (policy.role in (? , ? , ? , ?))
The query generated by 2nd function:
2. select policy.id, policy.role from policy where (policy.role in (? , ? , ? , ?))
Conclusion: Clearly, if we look at the queries generated by both functions. We can clearly see, there is no difference between the two function definitions, they execute exactly the same query.
one difference is that with findAllBy Hibernate filters (#Filters from org.hibernate.annotations) are applied and so a different sql.
Actually, the difference between findallBy and findby, is that :
findAllBy returns a Collection but
findBy returns Optional.
so it's preferable to write List findAllBy instead of writing List findBy (but it will work also :p).
and to write Optional findBy instead of Optional findAllBy.
check this doc https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.core-concepts
findBy method is used if we want to find by name or some other criteria like findByFirstName(String firstName);
findAll methods generally finds by providing specification
List<T> findAll(Specification<T> spec);
Please see docs below for more clarity:
http://docs.spring.io/spring-data/jpa/docs/1.4.3.RELEASE/reference/html/jpa.repositories.html

Why does Hibernate throw a QuerySyntaxException for this HQL?

While building a query using Hibernate, I noticed something rather odd. If I use sequential named parameters for the ORDER BY clause, Hibernate throws a QuerySyntaxException (the colon prefix being an unexpected token):
createQuery("FROM MyEntity ORDER BY :orderProperty :orderDirection");
However, when this is done with a plain SQL query the query is created without a problem:
createSQLQuery("SELECT * FROM my_entity_table ORDER BY :orderProperty :orderDirection");
I know Hibernate is doing more String evaluation for the HQL query, which is probably why the SQL query is created without an error. I am just wondering why Hibernate would care that there are two sequential named parameters.
This isn't a huge issue since it is simple to work around (can just append the asc or desc String value to the HQL instead of using a named paramater for it), but it struck my curiosity why Hibernate is preventing it (perhaps simply because 99% of the time sequential named parameters like this result in invalid SQL/HQL).
I've been testing this in my local, and I can't get your desired outcome to work with HQL.
Here is quote from the post I linked:
You can't bind a column name as a parameter. Only a column value. This name has to be known when the execution plan is computed, before binding parameter values and executing the query. If you really want to have such a dynamic query, use the Criteria API, or some other way of dynamically creating a query.
Criteria API looks to be the more useful tool for your purposes.
Here is an example:
Criteria criteria = session.createCriteria(MyEntity.class);
if (orderDirection.equals("desc")) {
criteria.addOrder(Order.desc(orderProperty));
}
else {
criteria.addOrder(Order.asc(orderProperty));
}
According to the answer accepted in this question, you can only define parameters in WHERE and HAVING clauses.
The same answer also gives you some ways to have a workaround for your problem, however I will add one more way to do this:
Use the CASE - WHEN clause in your ORDER BY, this would work by the following way:
SELECT u FROM User u
ORDER BY
CASE WHEN '**someinputhere**' = :orderProperty
AND '**someotherinput**' = :orderDirection
THEN yourColumn asc
ELSE yourColumn desc END
Please, note that in this approach would required you to write all the possible inputs for ordering. Not really beautiful but really useful, especially because you would not need to write multiple queries with different orderings, plus with this approach you can use NamedQueries, which would be possible by writing the query dinamically using string concats.
Hope this can solve your problem, good luck!

How to define a where clause in a onetomany class definition

I am new to Java Persistence etc
I have a one to many relation defined and it works, but I cannot define the where clause in the many entity.
For instance, my search is returning a list of orders and a collection of order items per order.
But, how do I apply a where clause in the LineItem entity class e.g.
The native SQL will look like this (roughly)
SELECT Orders.OrderNumber, LineItems.Quantity, LineItems.Description
FROM Orders, LineItems WHERE Orders.OrderID = LineItems.OrderID
AND LineItems.Description IN ('XXX1', 'XXXXX2','XXXX3')`
AND LineItems.Quantity = 5
I dont know how to define the :
AND LineItems.Description IN ('XXX1', 'XXXXX2','XXXX3')
AND LineItems.Quantity = 5
in the LineItems class.
Please Help.
This is more of a workaround than an answer to your question, but it may be the only way to do it: Create a NamedQuery that restricts the results the way you want and use the NamedQuery instead. Here's a tutorial on how to do that.
The answers to this question imply that my suggestion is the only way to accomplish what you need. Those answers are Hibernate specific.

Java ORM with functions similar to Django's default ORM?

I'm planning to switch from django to Java, don't ask why :). I've decided to use Play Framework. What I need is an ORM which will allow to mix object oriented approach with native sql.
Something like django's example: https://docs.djangoproject.com/en/1.3/ref/models/querysets/#extra
Is there anything in the Java world that has these capabilities?
Example from Django's ORM world, two models: blog and blogEntry (1->N relationship). We are selecting all the blogs with counted blogEntry elements.
Blog.objects.extra(
select={
'entry_count': 'SELECT COUNT(*) FROM blog_entry WHERE blog_entry.blog_id = blog_blog.id'
},
)
You can do something quite like this with JPA, which is the standard persistence API in Java, using its JPQL query language.
Say you have a persistent class called Blog, which has a property called entries which refers to a set of instances of a class called BlogEntry.
You can retrieve all the Blogs on their own with this query:
select b from Blog b
You can then also retrieve the entry count by joining through the entries property and counting the results grouped by Blog:
select b, count(e) from Blog b join b.entries e group by b
Now, doing that means that the query will return a list of object arrays, where each array contains a Blog and a Long for the count. You might like to make this a little more typesafe. If you wrote a class called BlogWithCount which had a constructor like this:
public BlogWithCount(Blog b, long count)
Then you can use a constructor expression:
select new org.example.BlogWithCount(b, count(e)) from Blog b join b.entries e group by b
This query returns a list of BlogWithCount objects, from which you can then retrieve your results in a nice neat way.
When i first wrote this answer, i thought it would be possible to write a simpler version of the count query, like this:
select b, count(b.entries) from Blog b
But this doesn't work, at least in Hibernate 4.1.4. Looking at the spec, it seems like perhaps it should work, so this might be a bug. I'm not sure.

Categories

Resources