Is it possible to perform a join with CriteriaBuilder on a table that is not referenced by the selected entity? Since CriteriaBuilder.join() expects as parameter the attribute name, it seems like it won't work.
To be a bit clearer, the original query looks like this:
select Vehicle v left join VehicleStatus vs on v.id = vs.vehicleId...
Vehicle does not define a relationship to VehicleStatus. And changes to the database are currently undesired though possible if needed.
Currently the code I have
final Join<Vehicle, VehicleStatus> vs = vehicle.join("vs", JoinType.LEFT);
vs.on(cb.equal(vs.get("vehicleId"), vehicle.get("id")));
fails with java.lang.IllegalArgumentException: Unable to locate Attribute with the the given name [vs] on this ManagedType
No, you need a mapped association to create a join clause with the CriteriaBuilder.
With Hibernate, you can join 2 unassociated entities in a JPQL query. The syntax is almost identical to SQL. But it's a Hibernate-specific feature and not part of the JPA standard.
Related
Situation: "Parent" entity has multiple "Child" entities (#OneToMany, #Lazy) - two way relationship. No foreign key ("Child#parentId") field on entity.
Goal: Avoid N+1 problem by retrieving fully loaded Parent collection using sub-selects. If I understand theory of Subselect, this is my goal (2 resulting SQL queries):
select * from Parent ...;
select * from Child where parent_id in ...;
Question 1: What is the best practice to achieve this? Could you provide examples in both JPQL/HSQL and Criteria?
Question 2 (bonus): Can API manage second query division into "batches" - e.g. limit batches to 500: if 1st query loads 1000 Parents, 2a. loads Children for 500 Parents, 2b. loads for next 500.
I have tried:
Both result in SQL JOINs, it seems that I cannot use Child's foreign key without JOIN.
// 2nd query:
criteria
.createAlias("parent", "p")
.add(Property.forName("p.id")
.in(parentCriteria.setProjection(Projections.property("id"))))
.list();
// 2nd query (manual):
criteria
.createAlias("parent", "p")
.add(Property.forName("p.id").in(parentIdList))
.list();
Update (2015-04-05)
I checked that what it indeed works in EclipseLink via hints:
query.setHint("eclipselink.batch.type", "EXISTS");
This link http://blog.ringerc.id.au/2012/06/jpa2-is-very-inflexible-with-eagerlazy.html suggests that this is not possible via Hibernate and suggests manual fetching. However I cannot understand how to achieve it via HQL or Criteria, specifically how to get child.parent_id column that is not on Entity, but exists only on Database. That is, avoiding JOIN that would result from child.parent.id.
To avoid N+1 queries you can annotate relationship with
#BatchFetch(BatchFetchType.JOIN) //in eclipselink or
#BatchSize //in hibernate.
Inside queries, you can add fetch to join clause:
select p from Parent p join fetch p.children c where ...
You can add also query hints
query.setHint("eclipselink.batch", "p.children");
Or use EntityGraphs.
I have the following entities:
Machines {
id,
name
}
Favorites {
userId,
objectId,
objectType
}
Now I want to return list of machines ordered by favorites, name.
Favorites does not have any relation with Machines entities, its a generic entity which can hold various favoritable objects.
I got the sorting to work by the following raw sql. Is there any way to get this working using hibernate criterias. Basically ability to add alias for Favorites though Machines doesn't have any reference to it.
select m.* from Machines m left outer join Favorites f on m.id=f.objectId and f.userId =#userId order by f.userId desc, m.name asc
There are two ways to do this:
Use an SQL query or an SQL restriction for a criteria query.
Map the relation between Favorites and Machines as an Any type association.
Since you already have the query, executing it as a native SQL query through hibernate is the simplest solution:
sess
.createSQLQuery("SELECT ... ")
.addEntity(Machine.class)
.list();
The Any type association however, will probably benefit you in other situations where you want to query favoritables.
As said by #Maarten Winkels, you can use native SQL query that you have. You can do that but, if you want change your database then syntax may differs. So, it is recommended not to use native SQL query.
You cannot perform outer join using Hibernate criteria without any association between tables.
If you change your mind & want to add an association between these tables, then You can do something like below using criteria
List machines = session.createCriteria( Machines.class )
.createAlias("Machines", "m")
.createAlias("Favorites", "f", Criteria.LEFT_JOIN,
Restrictions.and(Restrictions.eq("m.id", "f.objectId"),
Restrictions.eq("f.userId", "#userId")))
.addOrder(Order.desc("f.userId"))
.addOrder(Order.asc("m.name"))
.list();
If for some reason you don't want to add any relationship between these tables, then you can use INNER JOIN or CROSS JOIN to get all machines with whatever criteria you want.
from Machines m, Favorites f where m.id = f.objectId and f.userId = #userId order by f.userId desc, m.name asc;
With inner joins there is one problem, if there is outer join relation in database then it doesn't work.
You can also write subqueries or separate queries & perform manual conditional checks between the results returned by those queries.
And one thing I want to ask you, What is the meaning of #userId in your SQL query ? I keep it as it is in my answer but, I didn't understand for what purpose # is there ?
I am attempting to create a domain/entity class based on a complex query. The query unions a bunch of tables together and unfortunately I am not able to create a view on the database for this query. I have been trying to set up the entity object but I am unsure of how to ensure that the marshaling works properly (and ensure the entity acts as read-only object).
As an example of the query, I am doing something like this:
Select
U_T.a,
U_T.b,
U_T.c,
C_T.a
FROM
(select
A_T.a,
null as b,
A_T.c,
1 as ind
from A_T
UNION
select
B_T.a,
B_T.b,
null,
0 as ind
FROM B_T
) U_T
left outer join C_T on C_T.fk_a = U_T.a;
The other issues are that this union can result in instances where there is no unique key column. This is fine as this data is for viewing only, and never editing. However the #Entity annotation wants a column to be listed with the #ID annotation. Another issue is that I do not believe I can use the other entity classes as the goal is to reduce the number of DB transactions from this query (as the actual one can result in hundreds of recursive queries being performed).
If I need to give any more information please let me know.
Example:
List<Object[]> list = em.createQuery(
"SELECT 'Foo', 123 FROM IrcEvent ev", Object[].class).getResultList();
What I don't like in that example:
How do I know the table name? Can't I specify the entity class instead?
How do I know the column name? jOOQ provides auto-completion by creating a DSL from the database schema.
There could be syntax errors everywhere.
What I basically want is something like
entityManager.deleteAll(EntityClass.class);
to delete the rows of an antire table (for example).
JPA 2 Criteria API http://docs.oracle.com/javaee/6/tutorial/doc/gjitv.html is for creating type safe queries programmatically, but it does not support deletes
Please help me with these Hibernate querying issues.
Consider the following structure:
#Entity
class Manager {
#OneToMany
List<Project> projects;
}
0) there are 2 possible ways of dynamic fetching in HQL:
select m from Manager m join m.projects
from Manager m join fetch m.projects
In my setup second one always returns a result of cartesian product with wrong number of objects in a list, while the first one always returns correct number of entities in a list. But the sql queries look the same. Does this mean that "select" clause removes redundant objects from the list in-memory? In this case its strange to see an advice in a book to use select distinct ... to get rid of redundant entities, while "select" does the job. If this is a wrong assumption than why these 2 queries return different results?
If I utilize dynamic fetching by one of the 2 methods above I see a classic n+1 select problem output in my hibernate SQL log. Indeed, FetchMode annotations (subselect or join) do not have power while fetching dynamically. Do I really can't solve the n+1 problem in this particular case?
Looks like Hibernate Criteria API does not support generics. Am I right? Looks like I have to use JPA Criteria API instead?
Is it possible to write HQL query with an entity name parameter inside? For example "from :myEntityParam p where p.id=1" and call setParameter("myEntityParam", MyClass.class) after this. Actually what I want is generic HQL query to replace multiple non-generic dao's by one generic one.
0) I always use a select clause, because it allows telling what you want to select, and is mandatory in JPQL anyway. If you want to select the managers with their projects, use
select distinct m from Manager m left join fetch m.projects
If you don't use the distinct keyword, the list will contain n instances of each manager (n being the number of projects of the manager): Hibernate returns as many elements as there are rows in the result set.
1) If you want to avoid the n + 1 problem, fetch the other association in the same query:
select distinct m from Manager m
left join fetch m.projects
left join fetch m.boss
You may also configure batch fetching to load 10 bosses (for example) at a time when the first boss is accessed. Search for "batch fetching" in the reference doc.
2) The whole Hibernate API is not generified. It's been made on JDK 1.4, before generics. That doesn't mean it isn't useful.
3) No. HQL query parameters are, in the end, prepared statement parameters. You must use String concatenation to do this.