JPA: No join method for Fetch interface - java

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.

Related

QueryDSL, specify projection for join

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);

Hibernate JPA Meta Model -- reference nested properties?

Suppose an entity model where an Employee has a Supervisor who has an id. Using hibernate-jpamodelgen to generate the meta model for the entities, how can I query a nested field?
For instance, "get all employees whose supervisor has id 4", using JpaSpecificationExecutor:
Page<Employee> getEmployeesBySupervisorId(int id) {
return findAll((root, query, criteriaBuilder) -> {
return criteriaBuilder.equal(root.get(Employee_.supervisor.id), id);
});
}
Note that Employee_ is the model meta class for Employee (and was generated by Hibernate).
This code will produce an error because the id symbol cannot be found on type SingularAttribute<Employee, Supervisor>. I get that, but it seems like these should somehow be chainable. I can't find great examples of how to do this cleanly.
In order to navigate to related entities, you must use From#join() join method, which works well with MetaModel:
CriteriaQuery<Employee> cq = criteriaBuilder.createQuery(Employee.class);
Root<Employee> from = cq.from(Employee.class);
Predicate p = criteriaBuilder.equal(from.join(Employee_.supervisor).get(Supervisor_.id), id);
See also
Oracle's Java EE Tutorial - Using the Criteria API and Metamodel API to Create Basic Typesafe Queries
Yes, I also stumbled upon this problem that the Metamodel classes are not offering deeper visibility to relationships > 1.
While accessing A.b is possible, A.b.c is not.
But there is another possibility besides Joins:
Just concatenate by using several getter(). For this you will need a root element (= CriteriaQuery & CriteriaBuilder).
return criteriaBuilder.equal(root.get(Employee_.supervisor).get(Supervisor_.id), id);
While this still ensures type safety, the whole path should be correct as it is not validated until runtime.
Also for sorting a resultset using the Metamodel there is a similar solution. Say you want to sort by the Supervisor's id:
Use JpaSort and JpaSort.Path
JpaSort.of(JpaSort.Direction.ASC, JpaSort.path(Employee_.supervisor).dot(Supervisor_.id));

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

Hibernate Lazy Loaded Entites Id

Question, am currently using hibernate and I was wondering if there was any way to get the value of a lazy loaded entities id without hitting the DB again? For example we currently have an entity called group that has a ManyToOne relationship with another entity Organization.
when we call a simple repository method
groupsRepository.findByUser(id) it returns a list of groups based on given user. The query looks like this
select
groups0_.PersonId as PersonId1_118_1_,
groups0_.GroupId as GroupId2_8_1_,
groups1_.GroupID as GroupID1_74_0_,
groups1_.CreatedAt as CreatedA2_74_0_,
groups1_.CreatedBy as CreatedB3_74_0_,
groups1_.Description as Descript4_74_0_,
groups1_.Name as Name5_74_0_,
groups1_.OrganizationID as Organiza9_74_0_,
groups1_.Role as Role6_74_0_,
groups1_.Status as Status10_74_0_,
groups1_.TouchedAt as TouchedA7_74_0_,
groups1_.TouchedBy as TouchedB8_74_0_
from
Groupsofpeople groups0_
inner join
groups groups1_
on groups0_.GroupId=groups1_.GroupID
where
groups0_.PersonId=?
Later on I need to see if the groups org is equal to another org which I do by comparing the OrganizaitonId's, a value that has already been fetched from the db. But every time I do this I have fetch the org from the db, is there anyway to prevent that so that the org attached to group will be prepopulated with it's id since it is already being fetched anyway? Or is this just a concession I have to make if I am using hibernate?

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