JPQL Query for ElementCollection - java

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.

Related

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

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

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.

Select subset of the list contained in an entity

Say I want to get all rows of the MyEntity that have an id lower than 10. This entity contains a list of Another entity. I would like this list to be fetched only by a subset of the listAnother. This subset containing only Another where the user contained in it is a specific one.
Basically in SQL it would be like this :
SELECT * FROM myentity m
LEFT JOIN another a
ON m.idTable=a.my_entity
AND a.user = "test1"
WHERE m.idTable < 10;
I didn't manage however to translate this query to jpql.
My entities being like this :
#Entity
public class MyEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int idTable;
#OneToMany(mappedBy = "myEntity")
private List<Another> listAnother;
}
#Entity
public class Another implements Serializable {
#Id
private int idAnother;
// bi-directional many-to-one association to Thethread
#ManyToOne(fetch = FetchType.LAZY)
private MyEntity myEntity;
#ManyToOne(fetch = FetchType.LAZY)
private User user;
}
#Entity
public class User implements Serializable {
#Id
private String username;
}
In jpa I could do this :
SELECT m FROM MyEntity where m.idTable < 10;
And then for each entity I get from this list call this:
SELECT a FROM Another Where a.user.username=:'test' AND a.myEntity=:entity;
However I would like to do it all at once in one query. Can I do this with criteria ? I didn't take the time to learn it but if it's possible then I will.
JPQL and Critaria API are equal in terms of what you can express with them. What is possible with JPQL is possible with Criteria and vice versa.
With JPQL, you can simply combine your 2 queries into one in this way:
SELECT a FROM Another a Where a.user.username=:test AND a.myEntity.idTable < 10
You can use dot notation (.) to join multiple entities in the query, provided that the relationship is X-to-one. If you have X-to-many relationship, you need to use JPQL JOIN, which is not very complicated. Example with (LEFT) JOIN:
SELECT m FROM MyEntity m LEFT JOIN m.listAnother a Where a.user.username=:test AND m.idTable < 10
The result is of course not equal - in first case you will get list of Another entities and you can get MyEntity by a.myEntity, in the second case you will get list of MyEntity, which all have at least one Another entity with given user
In hibernate you can use Filters and FilterJoinTable. Here you can read how to do that. Similar problem was solved here.
You need to extend the logic which you applied to check the username (a.user.username=:'test') which was for many-to-one relation between anything and user by taking it one level up to myEntity and then using it for one-to-many relation as well -
SELECT m FROM MyEntity where m.idTable < 10 and (m.listAnother.user.username=:'test')
The join condition "m.listAnother.myEntity=:entity" wouldn't be needed now as in our query we have started from the specific myEntity and then moved down to listAnother.user.username .
I don't have the table definitions to try this query myself, exact SQL may require some tweaks - but logically it should work like the way I showed above, i.e. just the way you joined Another with User, the same way you can join MyEntity with Another by just traversing down the child listAnother.

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)

Strange JPQL issue: adding less restrictive query parameters returns less rows

We've got an entity model like this (non-related fields omitted for brevity):
#Entity
public class Invoice {
//The other portion of the sum that is invoiced from another payer.
#OneToOne(mappedBy = "parentInvoice", cascade = CascadeType.ALL)
private Invoice otherPayersInvoice;
//The patient's invoice, which is the parent of this other payer's invoice.
#OneToOne(cascade = CascadeType.ALL)
private Invoice parentInvoice;
#ManyToOne
private Payer payer;
}
So, basically we will split invoice's into parts and store the relations within the entities.
The following query works and returns all invoices by payerId:
SELECT i FROM Invoice i WHERE i.payer.id = :payerId
However, when i extend the query to also list all "child" invoices that the payer may have (Invoices invoiced from another payer), I get no results at all and no error or warning anywhere whatsoever.
SELECT i FROM Invoice i WHERE i.payer.id = :payerId OR
(i.parentInvoice IS NOT NULL AND i.parentInvoice.payer.id = :payerId)
What could possibly be the issue here?
i.parentInvoice.payer.id
This is doing an INNER join from Invoice to its parent to its payer, which will filter all rows without a parent or payer.
You need to use an outer-join instead.
SELECT i FROM Invoice i join i.parentInvoice parent join p.payer parentPayer join i.payer payer WHERE payer.id = :payerId OR (parentPayer.id = :payerId)

Categories