Criteria API ignores JOIN - java

I have a simple criteria api query with a (inner) join
public void find(Category category) {
CriteriaBuilder b = getQueryBuilder();
CriteriaQuery<Product> q = createQuery();
Root<Product> root = q.from(Product.class);
Join<Product, Category> myCategory= root.join("category");
q.where(b.equal(myCategory, category));
entityManager.createQuery(q).getResultList();
}
The query works, but if I enable in persistence.xml the sql logging I can see that the query is a
SELECT * FROM product, category WHERE ...
and no
SELECT * FROM product join category on category.id = product.category ...
Any idea what the reason for this is? The where statement is very slow, so a real join would be really better.
I'm using eclipselink 2.5.1, Java EE7 and postgres
I also have a superclass from Product
#Entity
#Audit
public class SuperClass {}
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public class Product extends SuperClass {}
But this should be no problem?

A join is actual being perform, however the SQL used to perform the join is written in implicit join notation. The join will have the same performance as explicit join notation (notation using JOIN and ON). A "real" join is being performed, however it is just not in the explicit join notation (aka ANSI SQL 92) you expect.

You are selecting from Root with Product.class, but you need to select from join.
And I think result of you join must be mapped to some class, containing both entities Product and Category. If you like to get only product from join you can write something like this:
CriteriaBuilder b = getQueryBuilder();
CriteriaQuery<Product> q1 = createQuery();
CriteriaQuery<Product> q2 = createQuery();
Root<Product> root = q1.from(Product.class);
q2.select(root.as(Product.class)).from(root.join("category"))
entityManager.createQuery(q2).getResultList();

Related

Spring Data Specification Join by specific field

I have a Join looking like:
Join<RepairEntity, RepairAssignEntity> joinRepairAssignEntities = root.join(RepairEntity_.repairAssignEntities, LEFT);
when it converted to SQL it looks like
left outer join
repair_assign repairassi1_
on repairenti0_.id=repairassi1_.repair_id
I want it to be like:
left outer join
repair_assign repairassi1_
on repairenti0_.PARENT_ID=repairassi1_.repair_id
If I add condition to that join:
Predicate equal = cb.equal(root.get(RepairEntity_.parentId), repairEntityPath.get(RepairEntity_.id));
joinRepairAssignEntities = joinRepairAssignEntities.on(equal);
it makes it
left outer join
repair_assign repairassi1_
on repairenti0_.id=repairassi1_.repair_id
and (
repairenti0_.parent_id=repairassi1_.repair_id
)
How do I get rid of
on repairenti0_.id=repairassi1_.repair_id
?
A priori it seems that the entities are not well related, if you want to join by the parent field it should be something like this:
Entity RepairEntity
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="PARENT_ID")
RepairAssignEntity repairAssignEntity;
Entity RepairAssignEntity
#OneToMany(mappedBy="repairAssignEntity", fetch=FetchType.LAZY)
List<RepairEntity> RepairEntities;
if not, you would have to restructure the query to adapt it to the JPA entity modeling.
Regarding to get rid of the condition "on repairenti0_.id = repairassi1_.repair_id" you cannot because it is the implicit one of the relationship defined in the entity, but if you modify the modeling as I indicate, only the condition "repairenti0_.parent_id = repairassi1_ will appear. .repair_id "

jpa query join fetch from a onetomany

I have the following 2 classes using JTA transaction type with openjpa & a derby embedded db. What I want is to get the parent Organisation & the requested WSpace in one query when I only have the WSpace id. I am quite new to JPA so am learning as I go and have been struggling with 'q2' query. I have been using queries 'q0' & 'q1' to debug and check the items do exist in the db. 'q0' returns 1 object as does 'q1', whereas 'q2' returns 0 objects
I have tried a variety of entity setups and different queries but nothing has worked yet.
Orignally the WSpace class did not have an Organisation field as it didn't seem necessary for persisting or selecting, but I added it (along with the mappedby parameter) incase it was needed for the query to work, but nothing has changed.
back to the original question how can I get this to work so it returns the parent object with the single child being requested
SELECT o FROM Organisation o JOIN FETCH o.spaces w WHERE w.id = :id
Here are my classes
#Entity
public class Organisation implements MyObjects
{
#Id
#NotNull
private Integer id;
private String name;
#OneToMany( mappedBy = "organisation",
cascade = { CascadeType.PERSIST, CascadeType.MERGE } )
private List<WSpace> spaces;
//getters/setter below
}
And
#Entity
public class WSpace implements MyObjects
{
#Id
#NotNull
private Integer id;
private String name;
#ManyToOne
private Organisation organisation;
#OneToMany
private List<Application> apps;
//getters/setter below
}
class DAO
{
...
public void foo( Integer id )
{
....
String q0 = "SELECT o FROM Organisation o WHERE o.id = 49068";
List<Organisation> res0 = em.createQuery( q0, Organisation.class ).getResultList();
String q1 = "SELECT w FROM WSpace w WHERE w.id = " + id;
List<WSpace> res1 = em.createQuery( q1, WSpace.class ).getResultList();
String q2 = "SELECT o FROM Organisation o "
+ "JOIN FETCH o.spaces w WHERE w.id = " + id;
List<Organisation> res2 = em.createQuery( q2, Organisation.class ).getResultList();
...
}
}
Have you tried to look in the logs for output of your q2 query?
I am learning JPA too and was dealing with Criteria and QL queries quite recently.
So after having pretty same problems with joins, I started checking logs and it was pretty clear, what the issues were, since logs showed up translated to SQL queries.
Another thing to look, how are you generating your Entities? I used Netbeans generating it for me. Also, many to many relations mignt have helper class generated too, I saw it in one of the projects.
Hope it helps..
The query you're looking for is probably this:
SELECT w FROM WSpace w LEFT JOIN FETCH w.organisation where w.id=:id
with query.setParameter("id", id); to bind the id parameter to the query. This effectively tells the persistence provider to fetch the WSpace.organisation relation while querying for WSpace entities in the same query. Using the LEFT [OUTER] keword (OUTER being optional) in front of the JOIN FETCH keywords tells your persistence provider to select WSpace objects even if there are no matching Organisation records for your WSpace.organisation relation.

DOT node with no left-hand-side using HQL with join

I am trying to find the maximum value of a Date column in mySQL DB using hibernate query language with join
#Query("select o.techid, CAST(MAX(o.last_modified) AS DATE)
from com.dw.model.user.User as u
left join com.dw.model.order.Order as o
on u.username=o.techid group by o.techid")
List<User> findUsers();
Model class =
#Entity(name = "orders")
#Scope("prototype")
#Component
#JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class)
public class Order {
#Id
private long orderNumber;
private Date last_modified;
I am getting this error :-
Caused by: java.lang.IllegalStateException: DOT node with no left-hand-side!
Can anyone please help me out by telling me how to write this in Hibernate?
Remove your package names. An entity is defined by its name only. Dots are used for properties and links between tables (when defined as a #ManyToOne* property).
select o.techid, CAST(MAX(o.last_modified) AS DATE)
from User as u
left join Order as o
on u.username=o.techid group by o.techid
Think Classes and Properties, not columns, when you write HQL.
Try following solution, this should work
SELECT o.techid, CAST(MAX(o.last_modified) AS DATE)
FROM User u LEFT JOIN u.order o GROUP BY o.techid

Simple hql named query that uses a inner join

I want to do something like this in my domain/entity object :
#Entity
#NamedQueries({
#NamedQuery(name="favouriteCats", query="from Cat c inner join on UserCat uc where uc.isFavourtie = true and uc.user = :user")
})
public final class Cat extends BaseTable
So that in my service layer I can do this :
Query query = session.getNamedQuery("favouriteCats")
query.setParameter(0, MyUser);
return query.list();
However, my syntax in HQL is incorrect - and aftern ten minutes looking at official docs I have decided to give up and ask here ... ?
My usercat table is joined like so :
#ManyToOne(cascade = CascadeType.MERGE)
#JoinColumn(name="cat_fk", insertable=false, updatable=false)
private cat
The sql is this, it works fine at my db command prompt:
select c.*
from cat as c inner join usercat as uc on c.id = uc.cat_fk
and uc.isFavourite = 1 //bit field
and uc.user_fk = 74 //just user id
Is it just me or is the hibernate documentation rather painful, and do you find yourself often wondering whether it would be quicker just to write normal jdbc prepared statements to populate your pojos/domain objects/dto's... ?
I think this might work for you, but I am guessing your Usercat class here:
select c from Usercat as uc inner join uc.cat as c where uc.isFavourtie = true and uc.user = :user
Case Issue, Right query would be:
from Cat c inner join on Usercat uc where uc.isfavourtie = true and uc.user = :user
Note : C in Cat is capital, U in Usercat is capital where as c in Usercat is small and f in isfavourite is small.

JPA #NamedQuery with two tables, possible?

I'm having a kind of dummy problem, I need to make a #NamedQuery with a join with other table, some simple thing.
But in all my #NamedQuery I'm only dealing with my mapped Object/Table.
For example in my Object/Table Mapped cars:
#NamedQuery(name = Cars.GET_AVAILABLE_CARS,
query = "select c.id from Cars c where c.status = (select d.status from CarStatus d where d.color=red)")
I'm trying to use: #SecondaryTables but no success for now.
One other thing that is working is give all things from other table as a parameter, but I don't think this will be good in performance.
Like:
#NamedQuery(name = Cars.GET_AVAILABLE_CARS, query =
"select c.id from Cars c where c.status = :" + CarStatusParam)
Any tips?
Thanks in advance
A named query can use everything that an API query can use, so can clearly do subqueries. Which implementation of JPA ?
I guess that you have something like this:
#Entity
public class Cars{
private String status;
//... something else
}
#Entity
public class CarStatus{
private String status;
private String color;
//... something else
}
if so, change this
#Entity
public class Cars{
private CarStatus status; //<--THIS!!
//... something else
}
#Entity
public class CarStatus{
private String color;
//... something else
}
and then update your NamedQuery to this:
query ="select c.id from Cars c where c.status.color = 'red'"
If I am wrong and the tables should not be related that way, then you should change your query tu use a join instead of a subquery. Something like this:
query = "select c.id from Cars c join CarStatus d where c.status = d.status and d.color = 'red'"
What you are trying to do is not a join. It is a subselect. And in terms of performance it is as (in)efficient as getting the param beforehand.
If you insist, however, to use the subselect, the JPA Query Language support it. As it supports joins.
Take a look here (at 7.11 8.11 . Subqueries).
The answer is yes it's possible. You need to make sure your columns define which table to look in. See the below code, you named query should work after that addition.
#Column(table = "SECONDARY_TABLE", name = "EXAMPLE_COLUMN_NAME")
private String example;

Categories