JPA2 Criteria Query JOIN - jpa-2.0

I have following class ( entity )
#Entity
public class Magazine {
private int id ;
private String magazine;
//getters and setters
}
// tracking the magazine arrival
#Entity
public class MagazineIn {
private int id;
private java.util.Date inDate;
private java.util.Date magdate;
#OneToOne
private Magazine mag ;
//getters and setters
}
Now i want to get all arrival status of all magazine , whether
magazine has in or not
using criteria query
following is code
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Object[]> cq = cb.createQuery(Object[].class);
Root<MagazineIn> magIn = cq.from(MagazineIn.class);
Join<Magazine,MagazineIn> mag = magIn.join("mag" , JoinType.LEFT);
cq.multiselect(mag.get("magazine") , magIn.get("magdate") ,
magIn.get("inDate"));
TypedQuery<Object[]> q = em.createQuery(cq);
But i am not getting all the magazine listing with indate and magdate null.RIGHT JOIN is not supported. What's wrong?

If the entity relationships changes are not possible you could consider changing the root of your query to Magazine and use a subquery which correlates on the 'mag' variable.

What's wrong is that you would indeed need a right join to do this. You could just have the reverse OneToOne association from Magazine to MagazineIn, and query from the magazine to the magazineIn with a left join.

I have solved this using collection in Magazine
#OneToMany(mappedBy="mag",FETCHTYPE.LAZY)
List<MagazineIn> arrivals

Related

JPQL left Join and Criteria API joins (Left and inner) throwing extra db queries to initialise associations

I am having below two entities .Instructor entity has relationship oneToMany with Vehicle entity.
public class Instructor {
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "native")
#GenericGenerator(name = "native", strategy = "native")
private int id;
#Version
#Column(columnDefinition = "int(11) not null default 0")
private int version = 0;
#OneToMany(mappedBy = "instructor", orphanRemoval = true, cascade = CascadeType.ALL)
private Set<Vehicle> vehicles = new HashSet<>();
.....
public class Vehicle {
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator =
"native")
#GenericGenerator(name = "native", strategy = "native")
private int id;
#ManyToOne(fetch = FetchType.LAZY)
private Student student;
#ManyToOne(fetch = FetchType.LAZY)
private Instructor instructor;
I want to fetch all instructors along with their vehicles in one query .
I have tried below four ways for doing this but i am not able to do this with test case 2 , 3 and 4 .
Test Case 1 : With JPQL join fetch clause.
#Test
#Transactional
#Rollback(false)
public void fetchTest1(){
List<Instructor> instructorsJpqlJoinFetch = em
.createQuery("select distinct i from
Instructor i join fetch i.vehicles v ",
Instructor.class)
.getResultList();
print(instructorsJpqlJoinFetch);
}
private void print(List<Instructor> instructors) {
instructors.forEach(i -> {
System.out.println("######Instructor Name : " + i.getName());
i.getVehicles().forEach(v -> {
System.out.println("######Instructor Vehicle
Number : " + v.getVehicleNumber());
});
});
Db Query gone to DB for case 1 :
select
distinct instructor0_.id as id1_2_0_,
vehicles1_.id as id1_5_1_,
instructor0_.address as address2_2_0_,
instructor0_.birth_date_time as birth_da3_2_0_,
instructor0_.birth_date_time_zone_offset as birth_da4_2_0_,
instructor0_.created_date as created_5_2_0_,
instructor0_.day_off_time as day_off_6_2_0_,
instructor0_.day_start_time as day_star7_2_0_,
instructor0_.father_name as father_n8_2_0_,
instructor0_.mother_name as mother_n9_2_0_,
instructor0_.name as name10_2_0_,
instructor0_.photo as photo11_2_0_,
instructor0_.monthly_salary as monthly12_2_0_,
instructor0_.updated_date as updated13_2_0_,
instructor0_.version as version14_2_0_,
vehicles1_.creation_date as creation2_5_1_,
vehicles1_.instructor_id as instruct8_5_1_,
vehicles1_.purchased_date_time as purchase3_5_1_,
vehicles1_.purchased_date_zone_offset as purchase4_5_1_,
vehicles1_.student_id as student_9_5_1_,
vehicles1_.updated_date as updated_5_5_1_,
vehicles1_.vechicle_type as vechicle6_5_1_,
vehicles1_.vehicle_number as vehicle_7_5_1_,
vehicles1_.instructor_id as instruct8_5_0__,
vehicles1_.id as id1_5_0__
from
instructor instructor0_
inner join
vehicle vehicles1_
on instructor0_.id=vehicles1_.instructor_id
As a result All instructors are fethched from db along with their vehicles
and after executing i.getVehicles() no query goes to db again . Which should be a correct behaviour . I am getting this behaviour with JPQL join fetch clause .
Test Case 2 : Tried same with Criteria API like below :
#Test
#Transactional
#Rollback(false)
public void fetchTest3() {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Instructor> cq =
cb.createQuery(Instructor.class);
Root<Instructor> root = cq.from(Instructor.class);
root.join(Instructor_.vehicles);
List<Instructor> instructorsWithCriteria =
em.createQuery(cq.distinct(true).select(root)).
getResultList();
print(instructorsWithCriteria);
}
Db query gone to db for case 2 :
select distinct instructor0_.id as id1_2_,
instructor0_.address as address2_2_,
instructor0_.birth_date_time as birth_da3_2_,
instructor0_.birth_date_time_zone_offset as birth_da4_2_,
instructor0_.created_date as created_5_2_,
instructor0_.day_off_time as day_off_6_2_,
instructor0_.day_start_time as day_star7_2_,
instructor0_.father_name as father_n8_2_,
instructor0_.mother_name as mother_n9_2_,
instructor0_.name as name10_2_,
instructor0_.photo as photo11_2_,
instructor0_.monthly_salary as monthly12_2_,
instructor0_.updated_date as updated13_2_,
instructor0_.version as version14_2_
from
instructor instructor0_
inner join
vehicle vehicles1_
on instructor0_.id=vehicles1_.instructor_id
**As a result all instructors are fethched from db . As soon as i hit
i.getVehciles() in print method a query to get vehicles of this instructor goes to db . Same happens for all instructors one by one .
Same behaviour happens for case 3 and 4 below too .
What should be passed in select clause of case 2, 3, 4 so that vehicle columns also got selected in query ?
**
Test case 3: JPQL left join
#Test
#Transactional
#Rollback(false)
public void fetchTest2() {
List<Instructor> instructorsJpqlLeftJoin = em
.createQuery("select distinct i from Instructor i left join
i.vehicles v ", Instructor.class)
.getResultList();
print(instructorsJpqlLeftJoin);
}
Db Query to db for Case 3 :
select
distinct instructor0_.id as id1_2_,
instructor0_.address as address2_2_,
instructor0_.birth_date_time as birth_da3_2_,
instructor0_.birth_date_time_zone_offset as birth_da4_2_,
instructor0_.created_date as created_5_2_,
instructor0_.day_off_time as day_off_6_2_,
instructor0_.day_start_time as day_star7_2_,
instructor0_.father_name as father_n8_2_,
instructor0_.mother_name as mother_n9_2_,
instructor0_.name as name10_2_,
instructor0_.photo as photo11_2_,
instructor0_.monthly_salary as monthly12_2_,
instructor0_.updated_date as updated13_2_,
instructor0_.version as version14_2_
from
instructor instructor0_
left outer join
vehicle vehicles1_
on instructor0_.id=vehicles1_.instructor_id
Case 4 : Criteria API Left Join :
#Test
#Transactional
#Rollback(false)
public void fetchTest4() {
CriteriaBuilder cbLeftJoin = em.getCriteriaBuilder();
CriteriaQuery<Instructor> cqLeftJoin =
cbLeftJoin.createQuery(Instructor.class);
Root<Instructor> rootLeftJoin = cqLeftJoin.from(Instructor.class);
rootLeftJoin.join(Instructor_.vehicles, JoinType.LEFT);
List<Instructor> instructorsWithCriteriaLeftJoin = em
.createQuery(cqLeftJoin.distinct(true).
select(rootLeftJoin)).getResultList();
print(instructorsWithCriteriaLeftJoin);
}
Db Query for case 4 :
select
distinct instructor0_.id as id1_2_,
instructor0_.address as address2_2_,
instructor0_.birth_date_time as birth_da3_2_,
instructor0_.birth_date_time_zone_offset as birth_da4_2_,
instructor0_.created_date as created_5_2_,
instructor0_.day_off_time as day_off_6_2_,
instructor0_.day_start_time as day_star7_2_,
instructor0_.father_name as father_n8_2_,
instructor0_.mother_name as mother_n9_2_,
instructor0_.name as name10_2_,
instructor0_.photo as photo11_2_,
instructor0_.monthly_salary as monthly12_2_,
instructor0_.updated_date as updated13_2_,
instructor0_.version as version14_2_
from
instructor instructor0_
left outer join
vehicle vehicles1_
on instructor0_.id=vehicles1_.instructor_id
What should i do in case 2,3,4 so that vehicles columns also got selected in the same query so that no secondory selects go to db?

Mysema QueryDSL JPAQuery for SQL groupby clause and order by

I have Items class:
#Entity
#Table(name ="Items")
Class Items{
#ID
private long id;
private String upc;
private long itemNo;
private int qty;
-----
}
I need to make below sql statement from JPAQuery of QueryDSL.
select itemNo, upc, count(*) t from Items group by ITEM_NO, UPC order by t;
QueryDSL sample needs modification for order by clause:
QItems items = QItems.items;
query.from(items)
.groupBy(items.itemNo,items.upc)
.orderby(<Dont Know How to sort on count>).list(items.itemNo,items.upc,items.count());
Need help to draft this query properly?
This should work
NumberPath<Long> count = Expressions.numberPath(Long.class, "c");
QItems items = QItems.items;
query.from(items)
.groupBy(items.itemNo,items.upc)
.orderby(count.asc())
.list(items.itemNo,items.upc,items.count().as(count));

how works createQueryBuilder and leftJoin?

I dont get how to make it work.
I have:
a table partner with fields id and name
a table partner_address with two fields: id_partner and id_address
a table address with fields id and external key id_town which references town(id)
a table town with fields id, a name, and postal_code
I want to select all partners that are in towns with specific postal_code
This query works:
SELECT p.nom, v.nom
FROM partner p
JOIN partner_address pa
ON pa.id_partner=p.id
JOIN address a
ON pa.id_address = a.id
JOIN town t
ON a.id_town=t.id
WHERE t.postal_code='13480';
Now I want to "translate" it into Doctrine 2 full syntax, following the documentation.
So I've made a custom repository:
src/Society/Bundle/MyProjectBundle/Repository/PartnerRepository.php
In this repository, I'm trying to create the corresponding function:
<?php
namespace HQF\Bundle\PizzasBundle\Repository;
use Doctrine\ORM\EntityRepository;
class PartenaireRepository extends EntityRepository
{
/**
* Get all active partners from a given postal code.
*/
public function findAllActiveByCp($cp)
{
return $this->createQueryBuilder('p')
->where('p.dateVFin IS NULL')
->andWhere('p.cp=:cp')
->addOrderBy('p.cp', 'DESC')
->setParameter('cp', $cp);
}
}
Nota: the query in the code is not the right one but this code works in another custom repository I've made, so I'm trying to start from this code.
I'm trying something like this but it doesn't work:
public function findAllActiveByCp($cp)
{
$qb = $this->createQueryBuilder('p');
return $qb
->leftJoin('partner_address pa ON pa.id_partner=p.id')
->leftJoin('address a ON pa.id_address = a.id')
->leftJoin('town t ON a.id_ville=t.id')
->where('p.dateVFin IS NULL')
->andWhere('t.cp=:cp')
->addOrderBy('t.cp', 'DESC')
->setParameter('cp', $cp);
}
I get this error:
Warning: Missing argument 2 for Doctrine\ORM\QueryBuilder::leftJoin(),
called in
/blabla/Repository/PartenaireRepository.php
on line 18 and defined in
/blabla/symfony/vendor/doctrine/orm/lib/Doctrine/ORM/QueryBuilder.php
line 767
You have to join only properties, that the selected entity have.
In first parameter of join() or leftJoin() or xxxJoin() you pass the attribute name related to selected object, and in the second - alias for joined entity.
Try similar to this:
$q = $this->em()->createQueryBuilder();
$q->select(['item', 'itemContact'])
->from('ModuleAdmin\Entity\CustomerEntity', 'item')
->leftJoin('item.contacts', 'itemContact')
->andWhere($q->expr()->like('item.name', ':customerNameStart'));
Of course, the CustomerEntity contains OneToMany relation in field contacts.
Remember, that in select statement you have to select the root entity (in my example CustomerEntity aliased as item).
Edit by Olivier Pons to add how I found out the solution, and to mark this answer as valid, because it put me on the right track, thank you Adam!
In the file PartenaireRepository.php I've used the createQueryBuilder('p') properly. Here's how to make two joins in a row, using createQueryBuilder():
class PartenaireRepository extends EntityRepository
{
/**
* Retrieval of all partners given for a given postal code.
*/
public function findAllActiveByCp($cp)
{
return $this->createQueryBuilder('p')
->leftJoin('p.adresses', 'a')
->leftJoin('a.ville', 'v')
->where('v.cp=:cp')
->setParameter('cp', $cp);
... blabla
}
}
I believe for what you're doing, you will need to provide four arguments to the leftJoin method.
->leftJoin('partner_address', 'pa', 'ON', 'pa.id_partner = p.id')
So your query builder chain should look like this
public function findAllActiveByCp($cp)
{
$qb = $this->createQueryBuilder('p');
return $qb
->leftJoin('partner_address', 'pa', 'ON', 'pa.id_partner = p.id')
->leftJoin('address', 'a', 'ON', 'pa.id_address = a.id')
->leftJoin('town', 't', 'ON', 'a.id_ville = t.id')
->where('p.dateVFin IS NULL')
->andWhere('t.cp=:cp')
->addOrderBy('t.cp', 'DESC')
->setParameter('cp', $cp)
;
}

How to get the first member of the related collection in JPQL

I have Product table which has a related table Images with a relation 1:M.
Class Product {
private Integer productId;
private String productName;
....
....
....
private List<Image> productImageList;
....
....
....
}
Class Image{
private Integer imageId;
private String imageName;
}
Class ProductLite{
private Integer productId;
private String productName;
private String imageName;
}
I am trying a JPQL query where I want to query to fetch products and the first image from the productImageList and returning a ProductLite object using the new constructor.
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public List<ProductLite> getAllProductLite() {
Query q = em.createQuery("SELECT NEW com.mycomp.application.entity.ProductLite(p.productId, p.productName, p.productImageList.get(0).getImageName())"
+ " from Product p"
+ " ORDER by p.productName");
List<ProductLite> prods = q.getResultList();
return prods;
}
But for some reason I am not able to get it to work. I get a NoViableException. So I tried moving the logic of getting the first image (getImage() method) to the Product Entity so in the query I could just call the getImage(). Even that does not seem to work.
java.lang.IllegalArgumentException: An exception occurred while creating a query in EntityManager:
Exception Description: Syntax error parsing the query [SELECT NEW com.meera.application.entity.ProductLite(distinct p.productId, p.productName, p.getImage()) from Product p, IN(p.productImageList) pil where p.category.categoryCode = :categoryCode ORDER by p.productName ], line 1, column 52: unexpected token [distinct].
Internal Exception: NoViableAltException(23#[452:1: constructorItem returns [Object node] : (n= scalarExpression | n= aggregateExpression );])
Any help is appreciated.
First, you cannot call methods in entity class from your JP QL query. Second, to use the order of entities in list, you need persisted order.
To create column for order to the join table between image and product, you have to add
#OrderColumn-annotation to the productImageList. For example:
#OrderColumn(name = "myimage_order")
//or dont't define name and let it default to productImageList_order
#OneToMany
private List<Image> productImageList;
Then you have to modify query to use that order to choose only first image:
SELECT NEW com.mycomp.application.entity.ProductLite(
p.productId, p.productName, pil.imageName)
FROM Product p JOIN p.productImageList pil
WHERE INDEX(pil) = 0
ORDER by p.productName

Need help coding a CriteriaQuery "in" predicate

I have a query that I have been trying to turn into a CriteraQuery, but I don't get how to code the "IN" term of the query.
The JPA Query that works looks like this:
#NamedQuery(name = "User.fromSearchID",
query = "SELECT q FROM User q,"
+ " IN (q.data) AS s WHERE s.data LIKE :search"
+ " ORDER BY q.id")
And the entity that it works on looks like this:
#Entity
public class User {
#Id private Integer id;
#OneToMany private List<UserData> data;
... }
And the referenced entity is
#Entity
public class UserData {
#Id private Long id;
private String key;
private String data;
... }
The intended result (the NamedQuery works) is to pull out all User entities that have some match in their list of data attributes. For some reason the CriteraQuery syntax escapes my intuition. Any help?
I assume users should be unique?
Then JP QL version without rather old fashion IN is:
String searchArgument = "data1";
String query = " SELECT DISTINCT(u) " +
" FROM User u JOIN u.data ud " +
" WHERE ud.data LIKE :search ORDER BY u.id";
List<User> result =em.createQuery(query, User.class).
setParameter("search", searchArgument).getResultList();
And same via Criteria API:
String searchArgument = "data1";
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<User> cq = cb.createQuery(User.class);
Root<User> user = cq.from(User.class);
Predicate dataLike = cb.like(user.join("data").<String>get("data"),
cb.parameter(String.class, "search"));
cq.distinct(true).select(user)
.where(dataLike)
.orderBy(cb.asc(user.get("id")));
TypedQuery<User> findUniqueUserByUserDataData = em.createQuery(cq);
findUniqueUserByUserDataData.setParameter("search", searchArgument);