JPQL returning no results when one of the fields is null - spring

I have a query which looks like this:
#Query(value = "SELECT e FROM Entity e") which returns all of the entities (currently 15). I want to only select certain fields to make the query far more memory efficient (has lots of embedded entities I don't need).
I have 2 many to one relationships, a and b, of which one is normally null and the other has a value. If I do #Query(value = "SELECT e.a FROM Entity e") I get the 10 results where a is not null, and when I do #Query(value = "SELECT e.b FROM Entity e") I get the other 5 where b is not null. However if I do #Query(value = "SELECT e.a, e.b FROM Entity e") I get 0 results back. I would assume it would all 15, returning it as a List<Object[]>. Why does this happen, and can I change it so #Query(value = "SELECT e.a, e.b FROM Entity e") returns all of the entities?

If e.a and e.b are entities you need left join
select a, b from Entity e left join e.a a left join e.b b

Related

Springboot jpa, get rows with specific fields using two joins

I have entities :Student, Class, Room.
student M-M class, student M-M rooms, thus two junction tables. Now I want to get all students assigned to class X and room Y. Thus I've made:
#Query("SELECT s from Student s INNER JOIN s.classes c INNER JOIN s.rooms r WHERE c.id LIKE ?1 AND r.id LIKE ?2")
Page<Student> findAllInClassAndRoom(final Long classId, final Long roomId, final Pageable pageable);
But it gives me wrong results. Is there an error in my query ?
The only error in your query is the LIKE statement. Just change the equal sign "=". As below:
#Query("SELECT s from Student s INNER JOIN s.classes c INNER JOIN s.rooms r WHERE c.id = ?1 AND r.id = ?2")
Page<Student> findAllInClassAndRoom(final Long classId, final Long roomId, final Pageable pageable);
The LIKE statement allows a greater mass of data. Because it would allow any Room or Class that part of the code is the id entered as a parameter.
LIKE statement specification

Failed to join three entity models in Query annotation via Spring JPA Data

I have three tables and mapping JPA entity models as followings in jsf 2.x application.
Foo Foo.java
Bar Bar.java
Zoo Zoo.java
The Foo has #oneToMany relationship to both Bar and Zoo in entity model context. In native sql, I was able to join three of them which worked fine.
select f.*, b.*, z.*
from Foo f
inner join Bar b
on f.foo_id = b.foo_id
inner join Zoo z
on z.foo_id = b.foo_id
where b.name = 'barName' and z.type = 'zooType";
I was trying to translate native sql in Query annotation via Spring JPA Data however I was keep getting org.hibernate.hgql.internal.ast.QuerySyntaxException: unexpected token.
Can someone kindly enough to point out what I am doing wrong? I tried having "one inner join" but I got same exception.
#Query("select f from Foo f inner join f.bars b inner join f.zoos z " +
"where b.name = ?1 " +
"where z.type = ?2")
List<Foo> findFoo(String name, String type);
This is because, you write two where in the #Query block, maybe you should use
#Query("select f from Foo f inner join f.bars b inner join f.zoos z " +
"where b.name = ?1 " +
"and z.type = ?2")
List<Foo> findFoo(String name, String type);
instead :)

HQL map with distinct not work

I have a trouble using HQL (in Groovy):
A simple query works without problems, like below (please note that I am using distinct):
def eqLiquid = liq.Liquidacion.executeQuery(
"""SELECT
distinct b.id ,
l.nombre as name
FROM Liquidacion l JOIN l.detalles ll JOIN ll.bioquimico b
WHERE l.id=:liqid
""", [liqid: liqid])
But I need to return these data as a map, then I have modified the query (only add new map()) :
def eqLiquid = liq.Liquidacion.executeQuery(
"""SELECT new map(
distinct b.id ,
l.nombre as name
)
FROM Liquidacion l JOIN l.detalles ll JOIN ll.bioquimico b
WHERE l.id=:liqid
""", [liqid: liqid])
Then I get an error: "unexpected token: distinct near line 2, column 17 [SELECT new map(
distinct b.id ,"
If I quit the distinct in the last query, it works.
Anyone had this problem?
It seems that MAP and DISTINCT can not work togheter in an HQL query

The greatest row per group - further combining the result set with left join with other related table in Hibernate - HQL. Is it possible?

There two tables in my Oracle database product and product_image. They have one-to-many relationship from product to product_image. Therefore, the relationship can be mapped in Hibernate something like the following.
The product entity:
#Entity
#Table(name = "PRODUCT", catalog = "", schema = "WAGAFASHIONDB")
public class Product implements java.io.Serializable
{
#OneToMany(mappedBy = "prodId", fetch = FetchType.LAZY)
private Set<ProductImage> productImageSet;
}
#Entity
#Table(name = "PRODUCT_IMAGE", catalog = "", schema = "WAGAFASHIONDB")
public class ProductImage implements java.io.Serializable
{
#ManyToOne(fetch = FetchType.LAZY)
private Product prodId;
}
I need to query that can fetch a list of rows with the maximum prod_image_id (primary key of the prduct_image table) from each group of products in the product_image table.
This was my previous question. This can be done with the following SQL.
SELECT
pi.prod_image_id,
pi.prod_id, pi.prod_image
FROM
product_image pi
INNER JOIN (
SELECT
MAX(pi.prod_image_id) AS prod_image_id
FROM
product_image pi
GROUP BY
pi.prod_id
) prod_image
ON pi.prod_image_id=prod_image.prod_image_id
The answer to that question corresponds to the following correct HQL.
SELECT
pi.prodImageId,
pi.prodId
FROM
ProductImage pi
WHERE
pi.prodImageId in (
SELECT
MAX(pis.prodImageId)
FROM
Product p
INNER JOIN
p.productImageSet pis
GROUP BY
p.prodId
)
This brings the following result exactly as intended.
PROD_IMAGE_ID PROD_ID PROD_IMAGE
662 284 3562298873030291049_Winter.jpg
644 283 7551758088174802741_9392401244_SS_2505.jpg
595 124 298082252715152799_SS_5012.jpg
566 62 7826143854352037374_SS_5004-A.jpg
But what I actually need is that the result set retrieved by the above SQL/HQL needs to be combined with the product table with LEFT OUTER JOIN so that it can retrieve each product from the product table regardless of their images in the product_image table something like the following.
PROD_IMAGE_ID PROD_ID PROD_IMAGE
662 284 3562298873030291049_Winter.jpg
644 283 7551758088174802741_9392401244_SS_2505.jpg
595 124 298082252715152799_SS_5012.jpg
- 101 -
- 81 -
566 62 7826143854352037374_SS_5004-A.jpg
This could be done by the following native SQL but doesn't seem possible with HQL which allows a subquery only in the SELECT and the WHERE clauses, a subquery in the FROM clause is disallowed in HQL.
SELECT
t.prod_image_id,
p.prod_id,
t.prod_image
FROM
product p
LEFT OUTER JOIN(
SELECT
pi.prod_image_id,
pi.prod_id,
pi.prod_image
FROM
product_image pi
INNER JOIN (
SELECT
MAX(pi.prod_image_id) AS prod_image_id
FROM
product_image pi
GROUP BY
pi.prod_id
) prod_image
ON pi.prod_image_id=prod_image.prod_image_id
)t ON p.prod_id=t.prod_id ORDER BY p.prod_id DESC;
My Google search says this is not feasible with a single HQL statement. Is this possible somehow with HQL? Please confirm me.
You're right, you can't use subqueries in from clause.
http://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html_single/#queryhql-subqueries
But you can use a separate query like:
select p
from Product p
where p.productImageSet is empty
to find the products that has no product image.
I would create an Oracle view that implements your query that gives you what you want with a simple select in HQL.
I'm currently having the same table relationship in MySQL. I'm currently trying to learn JPA. Accordingly, I'm using JPA 2.0 provided by Hibernate - version 4.2.7 final. The following JPQL,
select
p.prodId,
pi.productImageId,
p.prodName,
pi.prodImage
from
Product p
left join
p.productImageSet pi
where
pi.productImageId in (
select
max(productImageId) as productImageId
from
ProductImage
group by
prodId
)
or pi.productImageId is null
order by p.prodId
and the corresponding criteria query,
CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple>criteriaQuery=criteriaBuilder.createTupleQuery();
Metamodel metamodel=entityManager.getMetamodel();
EntityType<Product>entityType=metamodel.entity(Product.class);
Root<Product>root=criteriaQuery.from(entityType);
SetJoin<Product, ProductImage> join = root.join(Product_.productImageSet, JoinType.LEFT);
List<Selection<?>>selections=new ArrayList<Selection<?>>();
selections.add(root.get(Product_.prodId));
selections.add(root.get(Product_.prodName));
selections.add(join.get(ProductImage_.prodImage));
selections.add(join.get(ProductImage_.productImageId));
criteriaQuery.multiselect(selections);
Subquery<Long>subquery=criteriaQuery.subquery(Long.class);
Root<ProductImage> subRoot = subquery.from(ProductImage.class);
subquery.select(criteriaBuilder.max(subRoot.get(ProductImage_.productImageId)));
subquery.groupBy(subRoot.get(ProductImage_.prodId).get(Product_.prodId));
Predicate []predicates=new Predicate[2];
predicates[0]=criteriaBuilder.in(join.get(ProductImage_.productImageId)).value(subquery);
predicates[1]=join.get(ProductImage_.productImageId).isNull();
criteriaQuery.where(criteriaBuilder.or(predicates));
criteriaQuery.orderBy(criteriaBuilder.desc(root.get(Product_.prodId)));
TypedQuery<Tuple> typedQuery = entityManager.createQuery(criteriaQuery);
List<Tuple> tuples = typedQuery.getResultList();
Both of them, JPQL and criteria query produce the following SQL query.
select
product0_.prod_id as col_0_0_,
productima1_.product_image_id as col_1_0_,
product0_.prod_name as col_2_0_,
productima1_.prod_image as col_3_0_
from
social_networking.product product0_
left outer join
social_networking.product_image productima1_
on product0_.prod_id=productima1_.prod_id
where
productima1_.product_image_id in (
select
max(productima2_.product_image_id)
from
social_networking.product_image productima2_
group by
productima2_.prod_id
)
or productima1_.product_image_id is null
order by
product0_.prod_id desc
resulting in fetching the desired result set as mentioned in the question - not in the way we can see in the usual RDBMS systems but it works.
This can be done, if the query statement is slightly modified. Accordingly the following criteria query does what exactly is needed.
CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder();
CriteriaQuery<ProductUtils>criteriaQuery=criteriaBuilder.createQuery(ProductUtils.class);
Metamodel metamodel = entityManager.getMetamodel();
Root<Product> root = criteriaQuery.from(metamodel.entity(Product.class));
ListJoin<Product, ProductImage> join = root.join(Product_.productImageList, JoinType.LEFT);
List<Selection<?>>selections=new ArrayList<Selection<?>>();
selections.add(root.get(Product_.prodId));
selections.add(root.get(Product_.prodName));
selections.add(join.get(ProductImage_.prodImage));
selections.add(join.get(ProductImage_.productImageId));
criteriaQuery.select(criteriaBuilder.construct(ProductUtils.class, selections.toArray(new Selection[0])));
Subquery<Long> subquery = criteriaQuery.subquery(Long.class);
Root<ProductImage> prodImageRoot = subquery.from(metamodel.entity(ProductImage.class));
subquery.select(prodImageRoot.get(ProductImage_.productImageId));
subquery.where(criteriaBuilder.equal(root, prodImageRoot.get(ProductImage_.prodId)), criteriaBuilder.lessThan(join.get(ProductImage_.productImageId), prodImageRoot.get(ProductImage_.productImageId)));
criteriaQuery.where(criteriaBuilder.exists(subquery).not());
criteriaQuery.orderBy(criteriaBuilder.desc(root.get(Product_.prodId)));
List<ProductUtils> list = entityManager.createQuery(criteriaQuery).getResultList();
This criteria query produces the following desired SQL query that does equally the same function which is needed as specified in the question.
select
product0_.prod_id as col_0_0_,
product0_.prod_name as col_1_0_,
productima1_.prod_image as col_2_0_ ,
productima1_.product_image_id as col_3_0_
from
social_networking.product product0_
left outer join
social_networking.product_image productima1_
on product0_.prod_id=productima1_.prod_id
where
not (exists (select
productima2_.product_image_id
from
social_networking.product_image productima2_
where
product0_.prod_id=productima2_.prod_id
and productima1_.product_image_id<productima2_.product_image_id))
order by
product0_.prod_id desc

How to get entities in a many-to-many relationship that do NOT have a corresponding linked entity with DQL and Doctrine?

I have a standard many-to-many relationship set up. Entity A can have many of Entity B, and vice versa.
I'm trying to get a list of all Entity A that do NOT have any corresponding Entity B. In SQL, I'd run a query like this:
SELECT a.* FROM entity_a a LEFT JOIN a_b r ON r.AID = a.id WHERE r.BID IS NULL
In this query, a_b is the linking table.
I'm trying to write a DQL statement (or use some other method) to get the same result, but the following does not work:
SELECT s FROM VendorMyBundle:EntityA s LEFT JOIN VendorMyOtherBundle:EntityB u WHERE u IS NULL
How can I achieve what I'm trying to do?
First, I have to underline that usually you should JOIN on the property of the entity (i.e. s), e.g. instead of:
SELECT s FROM VendorMyBundle:EntityA s
LEFT JOIN VendorMyOtherBundle:EntityB u WHERE u IS NULL
you should have something like:
SELECT s FROM VendorMyBundle:EntityA s
LEFT JOIN s.mylistofb u WHERE u IS NULL
where I'm supposing that in entity A you have defined your relationship as:
class A{
// ...
/**
* #ManyToMany(targetEntity="Vendor\MyBundle\Entity\EntityB")
* #JoinTable(name="as_bs",
* joinColumns={#JoinColumn(name="a_id", referencedColumnName="id")},
* inverseJoinColumns={#JoinColumn(name="b_id", referencedColumnName="id", unique=true)}
* )
**/
private $mylistofb;
This stated, if the query is not working yet, then try the following:
SELECT s FROM VendorMyBundle:EntityA s
WHERE SIZE(s.mylistofb) < 1
It is simplest than the previous and also comes from the official docs (i.e. see "phonenumbers example").

Resources