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 ?
Related
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.
I would like to create conditions from this select in JOOQ. Because in my case I want to declare a dynamic query and check if TABLE_C.FIELDC contains "foo" only when I need...
Example:
create.select().from(TABLE_A).join(TABLE_B).onKey(Keys.FK_TABLEA_TABLEB)
.join(TABLE_C).onKey(Keys.FK_TABLEB_TABLEC)
.where(TABLE_C.FIELDC.containsIgnoreCase("foo");
to:
SelectFinalStep select = create.select().from(TABLEA);
if (isFooSearched) {
query.addCondition( <JOIN> and <CONTAINS> like first example)
}
How can I do this?
There are several ways to solve this:
Using implicit joins
In relatively simple cases, when the optional join follows a to-one relationship, you may be able to use an implicit join (if you're using the code generator):
create.select()
.from(TABLE_A)
.join(TABLE_B).onKey(Keys.FK_TABLEA_TABLEB)
.where(isFooSearched
? TABLE_B.tableC().FIELDC.containsIgnoreCase("foo")
: noCondition())
.fetch();
Using SEMI JOIN instead of INNER JOIN, which makes dynamic SQL much easier
create.select()
.from(TABLE_A)
.where(
isFooSearched
? TABLE_A.TABLE_B_ID.in(
select(TABLE_B.ID)
.from(TABLE_B)
.join(TABLE_C).onKey(FK_TABLEB_TABLEC)
.where(TABLE_C.FIELDC.containsIgnoreCase("foo"))
)
: trueCondition())
.fetch();
Note that a semi join is also more formally correct in this case than an inner join, as you will not get any duplicate rows on TABLE_A for any matches in to-many relationships (removing them with DISTINCT might be wrong and certainly is inefficient).
Side-note: Not all databases recognise semi-joins in EXISTS or IN syntax, and may thus not optimally run this statement, compared to a JOIN based solution.
Using INNER JOIN as you asked for
// I'm assuming DISTINCT is required here, as you
// - are not interested in TABLE_B and TABLE_C results (semi join semantics)
// - do not want any duplicate TABLE_A values
create.selectDistinct(TABLE_A.fields())
.from(
isFooSearched
? TABLE_A
.join(TABLE_B).onKey(FK_TABLEA_TABLEB)
.join(TABLE_C).onKey(FK_TABLEB_TABLEC)
)
: TABLE_A)
.where(
isFooSearched
? TABLE_C.FIELDC.containsIgnoreCase("foo")
: trueCondition())
.fetch();
I've mad a few assumptions here, including the fact that DISTINCT usage could be correct on your joined variant of the query, but it is hurting you (probably) on your "default" query variant, so perhaps, shoe horning this into a single dynamic query might be overkill.
Thus...
Using two different queries
For my taste, the two queries are simple enough to allow for some duplication and simply run two different queries depending on the flag:
if (isFooSearched)
create.select().from(TABLE_A) /* joins or semi joins here */ .fetch();
else
create.select().from(TABLE_A).fetch();
Side note
All solutions are assuming you have these static imports in your code:
import static org.jooq.impl.DSL.*;
Is there any way to write criteria query on entities not having explicit joins ? By explicit join I mean that 2 tables in database have no foreign key relationship but some columns need to be fetched from both the tables so joins are required in query. I know that queries having join can be written with 'in' clause and criteria queries can be written with "In" criteria. I have written HQL for this case but please tell me how to write criteria query for this case.
Thanks in advance
In this case, the cross join would be solution, but that is possible ONLY with HQL. Check doc (small cite):
16.2. The from clause
Multiple classes can appear, resulting in a cartesian product or "cross" join.
from Formula, Parameter
from Formula as form, Parameter as param
And, also, we can filter on any of these two Entities inside of the WHERE clause, to narrow the cartesian product...
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.
I would need to make an aggregation of certain data stored in database via Hibernate, so I would like to use HQL to get data I need.
Transaction
id
userEmail
moneyPaid
Now.. what is the best way to aggregate all moneyPaid by user determined by userEmail. I would like to get back entities that will have user email, with aggregated moneyPayd property.
What is the best and most elegant way to do that?
And possibly if I have another entity say User:
User
id
email
address
Is there any convenient way not involving to much code that would join User with Transaction, aggregate moneyPaid and returned list of entities of totaly different type like:
AggregatedData
email
totalMoneyPaid
address
I know some ways to do this but they are all but elegant and concise, any ideas are appreciated.
select u.email, sum(t.moneyPaid), u.address from Transaction t
left join t.user u
group by u.email, u.address;
I'am ok with this query, if you don't want a List<Object[]> as return, you can write this :
select new org.sample.RequestResult(u.email, sum(t.moneyPaid), u.address) from Transaction t
left join t.user u
group by u.email, u.address;
Create an object org.sample.RequestResult with a constructor String email, Long (Or other, the type of your entity's field) moneyPaid, and String address
The query will return a List<RequestResult>
You can else just write select new map(...) which return will a Map from aliases to selected values
select u.email, sum(t.moneyPaid), u.address from Transaction t
left join t.user u
group by u.email, u.address;
For this to work, the Transaction entity must not have a userEmail property, but an ManyToOne association to the User entity.
The hibernate reference contains everything you need to know about associations and aggregate functions. They're similar to SQL aggregate functions, obviously.
Note that the above query doesn't return entities, but a List<Object[]>, each Object[] containing three elements : the email, the total money paid, and the address. Up to you to loop over the list and crate a list of AggregatedData instances.
Do you mean you want to use HQL to query the result?
If so, I think group by statement in HQL would do.
select t.userEmail, sum(t.moneyPaid)
from Transaction t
group by t.userEmail
I am not sure what the returned type is(you can debug to find it).
But I guess List<List<Object>> might work.
(it is weakly typed, so you have to do down cast before using it)
refer to hibernate website:
Query with HQL