QueryDSL, specify projection for join - java

I'm trying to use QueryDSL to fetch a collection of entities (table Category) where each entity has a #OneToMany association with an other type of entity (table User), and a join is used to get it all in one go.
Problem is that the second entity (the users) contains CLOB fields which should not be included. I don't want to include them in my query. Given the following QueryDSL query :
JPAQuery<CategoryEntity> baseQuery = new JPAQuery<>(entityManager)
.select(QCategoryEntity.categoryEntity)
.from(QCategoryEntity.categoryEntity)
.leftJoin(QCategoryEntity.categoryEntity.users, QUserEntity.userEntity)
.where(somePredicate);
QueryDSL will generate something like
SELECT categoryen0_.id, (...), useren0_.id, (...)
FROM category categoryen0
LEFT OUTER JOIN user useren0 ON ...
WHERE ...
How can a specific projection be applied to this query such that the CLOB data is excluded?
Notes :
I'm trying to avoid native queries and changing the domain model.
I have not found a way to use Projections on the join
itself.
Using subqueries inside joins is not supported by JPQL hence
it's not supported by QueryDSL either.

Turns out this was not working well due to my use of fetch joins, which prevented me from using projections. Solving it was a matter of using a basic join instead and manually reconstructing the relationship.
Collection<Tuple> tuples = new JPAQuery<>(entityManager)
.select(QCategoryEntity.categoryEntity, Projections.constructor(UserEntity.class, <constructor arguments>)
.from(QCategoryEntity.categoryEntity)
.join(QCategoryEntity.categoryEntity.users, QUserEntity.userEntity)
.where(somePredicate);

Related

JPA: No join method for Fetch interface

Via JPA Criteria Api
a Join can in turn be joined either via Join.join(...) or via Join.fetch(...).
but a Fetch in turn can only be joined via Fetch.fetch(...):
What is the reason? Why an attribute of a fetched entity in turn can not be joined (without fetch)?
For example:
root.join(entity1_attribute, JoinType.INNER)
.join(entity2_attribute, JoinType.INNER)
.fetch(entity3_attribute, JoinType.INNER)
is possible.
But
root.join(entity1_attribute, JoinType.INNER)
.fetch(entity2_attribute, JoinType.INNER)
.join(entity3_attribute, JoinType.INNER) //<--
is not possible.
It is this way for the same reason that the JPQL BNF does not allow aliasing fetch joins, because nested fetches do not have to be supported according to the JPA specification. You can ask on the JPA mailing list or create an issue to request making Fetch extend Join or something like that if you want.

Ebean query operator "Fetch" isn't working when I use more than one relationship path

It is my understanding that when I use fetch when querying an object, Ebean will try to fetch the relationship using a join.
e.g.
Ebean.find(ProjectRequest.class)
.fetch("attachments")
.findList();
This works as expected when trying to fetch one relationship.
But when I try to fetch more than one relation it doesn't query any of the relationships with a join and fetches all the relationships with a separate query.
e.g.
Ebean.find(ProjectRequest.class)
.fetch("projectConstructionCosts")
.fetch("attachments")
.fetch("projectRequestComments")
.fetch("additionalContacts")
.where()
.in("project_status", projectStatusValues)
.findList();
I should be able to do this based on the code examples in this documentation page.
https://ebean-orm.github.io/apidoc/10/io/ebean/FetchConfig.html
I use "*" for fetchProperties and work for me
Ebean.find(ProjectRequest.class)
.fetch("projectConstructionCosts", "*")
.fetch("attachments", "*")
.fetch("projectRequestComments", "*")
.fetch("additionalContacts", "*")
.where()
.in("project_status", projectStatusValues)
.findList();
it doesn't query any of the relationships with a join and fetches all the relationships with a separate query
The reason Ebean does not use a SQL join but instead creates additional separate queries to load those associations is because the relationships are ToMany relationships and Ebean will never generate SQL Cartesian product (because it is deemed too risky / expensive).
That is, Ebean will only allow 1 of the ToMany relationships to be included in the "root query" (in order to avoid sql cartiesian product) and the other relationships are then loaded as "secondary queries".
Additionally note that if the query includes a max rows limit then Ebean will honor this in generated SQL. In this case Ebean can't include a SQL join to a ToMany and that would become a "secondary query" (not part of the root SQL query).
Reference: https://ebean-orm.github.io/docs/query/fetch

Left join on unrelated tables in Query DSL and JPA

I have two unrelated tables, each one with field email. I need a query which introduces column taken from second table if emails match or will be null if no match is found. In SQL this is easy:
SELECT tableA.id, tableA.email, tableB.name
FROM tableA
LEFT JOIN tableB ON tableA.email=tableB.email
ORDER BY tableB.name
Unfortunately JPA doesn't allow joins over unrelated entities so I converted it to:
SELECT tableA.id, tableA.email,
(SELECT tableB.name FROM tableB WHERE tableB.email=tableA.email) AS aname
FROM tableA
ORDER BY aname
Now, it works as JPA query but we are using Query DSL so off we go to converting it:
JPQLQuery query = new JPAQuery(em);
List<Dto> items=query.from(qTableA)
.list(new QDto(qTableA.id, qTableA.email,
new JPASubQuery().from(qTableB)
.where(qTableB.email.eq(qTableA.email)).unique(qTableB.name)))
It works but now I have no idea how to implement sorting and filtering by field introduced by subquery.
Dto is a POJO used to collect results, QDto is a class autogenerated from Dto.
Question is: how to join two unrelated tables using Query DSL and JPA and avoiding native SQL? Is it possible? Sorting and filtering on fields from tableA and tableB.name is a requirement.
Join on unrelated entities is not covered by latest JPA spec (2.1)
However Hibernate 5.1.0+ and EclipseLink 2.4.0+ support ad hoc joins. http://blog.anthavio.net/2016/03/join-unrelated-entities-in-jpa.html

Dynamic Named Query in Entity class using JPQL example

I have a named query as below;
#NamedQuery(name = "MyEntityClass.findSomething", query = "SELECT item FROM MyTable mytbl")
Now I want to append dynamic sort clause to this query (based on UI parameters)
Can I get an example using JPQL for doing the same (like how to set a dynamic ORDER BY in the Entity class)
I have already tried using CriteriaQuery, but was looking for a JPQL implementation now.
NamedQueries are by definition NOT dynamic, it is not correct to change them programmatically.
So the way to go is to create a JPQL query (but not a named query) like this:
TypedQuery<MyEntity> query = em.createdQuery("SELECT item FROM MyEntity item ORDER BY "+sortingCol, MyEntity.class);
On the other hand, if you REALLY want to use the named query, you could do that the following way:
#NamedQuery(name = "MyEntityClass.findSomething", query = MyEntity.NAMED_QUERY)
#Entity
public class MyEntity {
public static final NAMED_QUERY= "SELECT item FROM MyTable mytbl";
//+your persistent fields/properties...
}
//and later in your code
TypedQuery<MyEntity> query = entityManager.createQuery(MyEntity.NAMED_QUERY + " ORDER BY " + sortingCol, MyEntity.class);
Complementing for JPA 2.1
As of JPA 2.1 it is possible to define named queries programmatically.
This can be achieved using entityManagerFactory.addNamedQuery(String name, Query).
Example:
Query q = this.em.createQuery("SELECT a FROM Book b JOIN b.authors a WHERE b.title LIKE :title GROUP BY a");
this.em.getEntityManagerFactory().addNamedQuery("selectAuthorOfBook", q);
// then use like any namedQuery
Reference here
This can be useful, for instance, if you have the orderby field defined as a application parameter. So, when the application starts up or on the first run of the query, you could define the NamedQuery with the defined OrderBy field.
On the other side, if your OrderBy can be changed anytime (or changes a lot), then you need dynamic queries instead of NamedQuery (static). It would not worth to (re)create a NamedQuery every time (by performance).
#NamedQuery
Persistence Provider converts the named queries from JPQL to SQL at deployment time.
Until now, there is no feature to create/update the query with #NamedQuery annotation at runtime.
On the other hand, you can use Reflection API, to change the annotation value at runtime. I think It is not solution, also it is not you wanted .
em.createQuery()
Persistence Provider converts the dynamic queries from JPQL to SQL every time it is invoked.
The main advantage of using dynamic queries is that the query can be created based on the user inputs.

How to bulk delete from element collection in jpa

I'm using jpa 2.0 and I have the following entity:
#Entity
public class Folder{
#ElementCollection
#CollectionTable(name="folder_files")
private Set<String> files;
// .....
}
Given a file name, I would like to delete all entries where files == theGivenFileName. In sql it would be something like this:
Delete from folder_files where files = XXX
Is there a way to perform this query using criteria-api?
If not, is there a way to perform this query using jpql?
UPDATE:
I think my question was not clear enough:
Since jpql uses entities (and not tables) I cannot just perform the sql written above plus since I'm using #ElementCollection I don't know how to address this variablr or even deal with it. I would like to delete all entries in that collection (in my case, the files set) which holds a given value, from all entities. Is that possible using jpql or (even better) criteria-api?
The Delete FROM clause requires an Entity, so there is no way to delete from an element collection from what I understand.
You can use a native SQL query, or you can map the element collection as a OneToMany to an Entity instead.
You can use the like query just the syntax is slightly changed.
query = em.createQuery("SELECT i FROM Item i WHERE UPPER(i.name) LIKE :keyword ");
query.setParameter("keyword", "%" + keyword.toUpperCase() + "%");
You can read more on following link,
https://forums.oracle.com/forums/thread.jspa?threadID=423742
Updated:
#Noam you can do it: Like in Criteria API
List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "Fritz%") )
.add( Restrictions.between("weight", minWeight, maxWeight) )
.list();
Kindly read more on it at following link:
http://ctpconsulting.github.com/query/1.0.0.Alpha3/criteria.html
http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/querycriteria.html
This cannot be done. Via JPQL it does not work because DELETE statement can only be applied to entities. Excerpt from JPA 2.0 specification:
Bulk update and delete operations apply to entities of a single entity
class (together with its subclasses,if any).
...
delete_statement ::= delete_clause [where_clause]
delete_clause ::= DELETE FROM entity_name [[AS] identification_variable]
Also it doesn't work via Criteria API. CriteriaQuery supports only selecting - not updates or removals.
You have to go for native SQL.

Categories