Hibernate HQL "Path expected for join!" #ManyToOne relationship - java

Suppose there are two entities - Owner
#Entity
#NamedQueries({
#NamedQuery(name = "Owner.findOwnerForPetId", query = "select o from Owner o inner join Pet p on o.ownerId=p.owner.ownerId where p.petId= :petId")
})
public class Owner {
#Id
#Column(name = "ownerId")
private Long ownerId;
#Column
private String name;
// scaffolding code...
}
and Pet
#Entity
public class Pet {
#Id
#Column(name = "petId")
private Long petId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ownerId")
private Owner owner;
#Column
private String name;
// scaffolding code...
}
where one Owner can have multiple pets (original class were renamed), but one Pet can only belong to one Owner. What I would like to do is find the Owner that owns a Pet that has some id, like:
select Owner.ownerId, Owner.name from Owner inner join Pet on Owner.ownerId=Pet.ownerId where Pet.petId=3;
This works fine when executed in pure SQL. However, I have tried these two queries in HQL and they both give the error Path expected for join!
select o from Owner o inner join Pet p on o.ownerId=p.owner.ownerId where p.petId= :petId
and
from Owner o join Pet p where p.petId= :petId
Note that there are no #OneToMany or Collection<Pet> pets in Owner. I would like to do it with only a #ManyToOne on the Pet side.
Any hints on what I have missed?

Try this one
select o from Pet p inner join p.owner o where p.petId= :petId

When working with HQL you must use the relations between entities not just entities
so for INNER JOIN and LEFT JOIN for example you should use the relation direct
For example next are valid queries
SELECT o FROM Pet p inner join p.owner o WHERE p.petId= :petId (same as #rathna accepted answer)
SELECT p FROM Pet p WHERE p.owner.ownerId = :ownerId

For the sake of completeness, if you need to LEFT JOIN but have the #ManyToOne attribute on the right side and thus can't specify a path, you can transform the query into a RIGHT JOIN with the same effect (i.e. not losing rows where the other table doesn't have matching rows and filtering the other table without losing null rows).
Suppose you want to get all owners that don't have pets ignoring pets named Charly:
You can't specify
SELECT o
FROM Owner o
LEFT JOIN o.pet p (<-------- ERROR) WITH p.name != 'Charly'
GROUP BY o.ownerId
HAVING count(p.petId) = 0
But you could transform this into:
SELECT o
FROM Pet p
RIGHT JOIN p.owner o WITH p.name != 'Charly'
GROUP BY o.ownerId
HAVING count(p.petId) = 0

Related

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.

Table is specified twice, both as a target for 'UPDATE' and as a separate source for data

I use spring-jpa with hibernate implementation. I use mariadb and I try to do an update subquery
My object structure
#Entity
public class Room {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long roomId;
#ManyToOne
#JoinColumn(name = "appartment_id")
private Appartment appartment;
}
#Entity
public class Appartment {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long appartmentId;
#OneToMany
#JoinColumn(name="appartment_id")
private Set<Room> roomList;
}
update Room r1
set r1.available = :availability
where r1.roomId in
( select r2.roomId from Room r2 JOIN r2.appartment a1 WHERE a1.appartmentId = :appartmentId )
I get this error
java.sql.SQLException: Table 'room' is specified twice, both as a target for 'UPDATE' and as a separate source for data
This is a restriction in MySQL:-
http://dev.mysql.com/doc/refman/5.7/en/update.html
You cannot update a table and select from the same table in a subquery.
There is a fudge you can do sometimes do to hide the sub query in a a further level of sub query that might work. Something like this (not tested):-
UPDATE Room r1
SET r1.available = :availability
WHERE r1.roomId IN
SELECT roomId
FROM
(
SELECT r2.roomId
FROM Room r2
JOIN r2.appartment a1
WHERE a1.appartmentId = :appartmentId
)
Note that your query possibly has an error. In the sub query you are joining the table Room aliased as r2 to a table called appartment on a database called r2. Also your sub query does a JOIN without a join condition.
However you can quite possibly just do the join in the UPDATE statement without the need for a sub query:-
UPDATE Room
INNER JOIN r2.appartment a1
ON Room.roomId = a1.roomId
SET r1.available = :availability
WHERE a1.appartmentId = :appartmentId
A solution will be to select roomIds seperatly and pass them as a parameter.
Select id list
List<Long> roomIdList = entityManager.createQuery(
"""SELECT r2.roomId
FROM Room r2
JOIN r2.appartment a1
WHERE a1.appartmentId = :appartmentId""", Long.class)
.setParameter("appartmentId", appartmentId)
.getResultList();
Pass it as a parameter
int updated = entityManager.createQuery(
"""UPDATE Room r1
SET r1.available = :availability
WHERE r1.roomId IN :roomIds""")
.setParameter("availability", availability)
.setParameter("roomIds", roomIdList)
.executeUpdate();

Hibernate returns wrong result set and generates wrong query

I have a table which can referance on itself.
organisation(id,child_from_Org, ...)
Hibernate Mapping
#Entity
#Table(name = "organisation")
public class Organisation implements java.io.Serializable {
private Integer id;
private Organisation organisation;
private ...
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "child_from_Org")
public Organisation getOrganisation() {
return this.organisation;
}
public void setOrganisation(Organisation organisation) {
this.organisation = organisation;
}
...
}
If i execute a a hibernate query like "from Organisation", then i'll get all organisations.
from Organisation o where o.id = 1
Then i get the organisation with id 1
from Organisation o where o.id = 1 or o.organisation.id = 1
Returns organisation with id 1 and all child organisations
from Organisation o where o.id = 1 or o.organisation.id = 1 or o.organisation.organisation.id = 1
Does not return the organisation with id 1!!!! Why?
I tried all possible combinations with braclets (() or () or ()).
I looks like a wrong SQL is getting generated from hibernate:
select organisati0_.id as id470_,
organisati0_.child_from_Org as ist21_470_,70_,
,...
from
organisation organisati0_ cross
join
organisation organisati1_
where
organisati0_.child_from_Org=organisati1_.id
and (
organisati0_.id=1
or organisati0_.child_from_Org=1
or organisati1_.child_from_Org=1
)
How can i resolve that problem?
Because you have an implicit join from Organisation to itself, and in hibernate implicit joins are inner joins:
http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/queryhql.html#queryhql-joins-forms
The implicit form does not use the join keyword. Instead, the associations are "dereferenced" using dot-notation. implicit joins can appear in any of the HQL clauses. implicit join result in inner joins in the resulting SQL statement.
Try explicit left joins, e.g.:
select o from Organisation as o left join o.organisation as parent left join parent.organisation as grandparent where o.id = 1 or parent.id = 1 or grandparent.id = 1
(NOTE: I've assumed organization.organisation is a parent relationship. If that's not the case you can(and should) replace the parent and grandparent aliases with something more appropriate)
EDITED TO ADD:
It seems what's happening is that the inner join is ruling out org with id of 1, probably because org with id of 1 has a null organisation
EDITED AGAIN TO ADD:
Added select per comment below.

JPQL Query for ElementCollection

Assuming I have the following Entities and relationship:
#Entity
#Table(name = "CUSTOMER")
public class Customer {
#Id
private long id;
#ElementCollection
#CollectionTable( name="ORDER", joinColumns=#JoinColumn(name="ORDER_ID") )
private List<Order> orders;
}
#Embeddable
public class Order {
private long price;
}
Now I want to get all customers who don't have any orders or their orders price are below $100. I tried to it in many ways but it always generate something like:
SELECT t0.ID FROM CUSTOMER t0, ORDERS t1 WHERE ((t1.PRICE > 100) AND (t0.COACH_ID = t1.ID))
The problem is that it return all customers who have orders above 100 but not the ones who didn't make any order yet. It is caused because of the t0.COACH_ID = t1.ID.
I am using eclipse link 2.5 and I tried all options but couldn't make it.
Any help will be appreciated.
Have you tried something like this:
SELECT DISTINCT c FROM Customer c
LEFT JOIN c.orders o
WHERE o.price < 100
OR c.orders IS EMPTY
I believe LEFT JOIN will do an outer-join, so it should still include customers without orders.

Hibernate Query.list returns actual Object instance instead of expected type

When performing a hibernate query in HQL using a join and subsequently calling query.list to return the list of matched objects, I am ending up with a list of actual Object instances (i.e. query.list().get(0).getClass() == Object.getClass()) instead of instances of the expected object.
Running the query without the join returns objects of the expected type correctly and they can be cast and used appropriately.
So far my searches have not turned up anything about what could be causing this. Is there something else I need to do when using a join in hql to ensure the object is mapped correctly?
Edit: Added code excerpts below. I had to change all the names and attempt to extract only the relevant portions (the real code is not really about cars).
Working query:
from Car car where car.name like :name
Non-working query:
from Car car left join car.occupants occupant where car.name like :name OR (occupant.name like :oname)
Car entity:
#Entity
#Table(uniqueConstraints = {#UniqueConstraint(columnNames = {"someId"}),
#UniqueConstraint(columnNames = {"someOtherId"})})
public class Car extends SomeParentEntity
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable = false, length = 64)
private String someId;
#Column(length = 64)
private String name;
// ... Many columns and mappings removed ...
#OneToMany(mappedBy = "car", fetch = FetchType.LAZY)
private List<Occupant> occupants;
// ...
}
Occupant entity:
#Entity(name = "car.Occupant")
#Table(uniqueConstraints = {#UniqueConstraint(columnNames = { "name" }) })
public class User extends SomeParentEntity
{
#ManyToOne
#JoinColumn(name = "carId", nullable = false)
private Car car;
#Column(length = 64, nullable = false)
private String name;
// ... Many mappings / columns removed ...
}
Your JOIN in the HQL is making Hibernate retrieve two types of objects. You can see this if you activate the SQL logging.
Assuming you have a X-to-X relation in your entity, the problem should go away if you change the query to use
... JOIN FETCH entity.relation ...
Why are you using explicit left join btw.
from Car car left join car.occupants occupant where car.name like :name OR (occupant.name like :oname)
I think we can simple use:
from Car car where car.name like :name or car.occupant.name like :oname
Then the query.list should give you List of object which should be casted back to List of car
+1's all around to the other answerers for your help, it is much appreciated.
However, the solution turned out to be very simple. The query just needed to have the select specified at the beginning for the join case in order to narrow the field (I assume).
So the non-working query:
from Car car left join car.occupants occupant where car.name like :name OR (occupant.name like :oname)
Becomes:
select car from Car car left join car.occupants occupant where car.name like :name OR (occupant.name like :oname)

Categories