Join condition in JPQL query - sql

I'm trying to create this query using JPA repositories:
SELECT * FROM api_centros c
LEFT JOIN api_centros_usuarios cu ON (c.id = cu.centro_id AND cu.usuario_id = ?)
WHERE cu.usuario_id IS NULL OR cu.paciente = 1
I've tried:
#Query("SELECT c FROM CentroEntity c LEFT JOIN c.relacionUsuarios AS r ON r.centro = c AND r.usuario = :usuario WHERE r.usuario IS NULL OR r.paciente = TRUE")
But this throws the following error:
Caused by: java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.InvalidWithClauseException: with clause can only reference columns in the driving table [SELECT c FROM es.plexus.api.vo.CentroEntity c LEFT JOIN c.relacionUsuarios AS r ON r.centro = c AND r.usuario = :usuario WHERE r.usuario IS NULL OR r.paciente = TRUE]
Tried to reformat the query in antoher way but I can't figure out how. In short, I've got 3 entities. 2 of them are related by the third (join table with each id from the other entities and a true/false value). I need all the entities (table api_centros in the query) related with a certain user (the other table) that has that third value to true OR is not related at all (or, looking at it the other way, I need everything (related and not related) from the first entity except the ones related and with that value to false).
The entities are:
Centro (only need the id), Usuario (only need the id) and CentroUsuario with 3 fields: centro_id, usuario_id and paciente (true/false).
Any ideas? Thanks!
*Edit:
This is the UsuarioCentroEntity (relationship entity):
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "usuario_id", nullable = false)
private UsuarioEntity usuario;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "centro_id", nullable = false)
private CentroEntity centro;
private boolean paciente;

Related

How to integrate variable in native sql query in Spring Data

Tables
#Data
#Entity
#Table(name = "A")
class A {
private Long id;
private Long bId;
}
#Data
#Entity
#Table(name = "B")
class B {
private Long id;
#Enumerated(EnumType.STRING)
private Status status;
}
enum Status {
NEW,
VALIDATED,
ARCHIVE
}
I have a following native query
#Query(value = "SELECT cast(a.id as varchar) FROM a
INNER JOIN b on a.id = b.id
WHERE a.id = :id AND
b.status = 'NEW'", // here I need smth like b.status = '" + Status.NEW + "' ",
nativeQuery = true)
Set<UUID> findIdByStatuses(#Param UUID id)
Instead of b.status = 'NEW', I need smth like
b.status = '" + Status.NEW + "' ",
to get a compile error when name of Status changes. But it is not working like this.
And the question is how to reach that desirable concatenation inside of native query without method argumetns

How to use inbuild db functions, sub query and join in a single query using criteria builder in JPA

I am trying to use few postgres inbuild functions, sub query and join in a single query in JPA where the result set (List<CampaignDTO>) is different from the actual entity (Campaign). I could achieve this by using native query and result transformation by "alias to bean".
Due to the deprecation I wanted to avoid the approach. Is there any way to achieve it using the criteria builder?
Here is the query:
select
"table1"."attemptedDate" "attemptedDate",
coalesce("table2"."messageAttempted", 0) "messageAttempted",
coalesce("table2"."messageSuccess", 0) "messageSuccess",
coalesce("table2"."messageFailure", 0) "messageFailure"
from
(
select
distinct generate_series(date '2020-11-10', date '2020-11-17', '1 day') "attemptedDate"
from
campaign) as "table1"
full join (
select
campaign_date "attemptedDate",
sum(coalesce(attempted_count, 0)) "messageAttempted",
sum(coalesce(delivered_count, 0)) "messageSuccess",
sum(coalesce(failed_count, 0)) "messageFailure"
from
campaign
where
channel = 'abc'
and campaign_date between '2020-11-10' and '2020-11-17'
group by
1) as "table2" on
"table1"."attemptedDate" = "table2"."attemptedDate"
order by
"attemptedDate"
Entity Class:
#Entity
#Table(name = "campaign")
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
public class Campaign{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
#Column(name = "channel", nullable = false)
private String channel;
#Column(name = "attempted_count", nullable = false)
private Integer attemptedCount;
#Column(name = "delivered_count", nullable = false)
private Integer deliveredCount;
#Column(name = "failed_count", nullable = false)
private Integer failedCount;
#Column(name = "campaign_date", nullable = false)
private Date campaignDate;
}
DTO Class:
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
public class CampaignDTO{
private Date attemptedDate;
#Builder.Default
private Integer messageAttempted = 0;
#Builder.Default
private Integer messageSuccess = 0;
#Builder.Default
private Integer messageFailure = 0;
}
Or is there any way to avoid deprecation while using transformation?
You will have to restructure the query or focus it in another way to implement it with criteriabuilder, since among other things it does not allow a query in the From clause.
Regarding the functions you can use them using the criteriabuilder function method (CriteriaBuilder function method)

Problem trying to construct JPA query from SQL

I have a query which works fine in SQL but I can't seem to create the equivalent in JPA. The CriteriaBuilder "or" doesn't appear to execute an "or".
These are the tables ...
Table A
id : Long : primary key
b_id : Long : foreign key to id of Table B
c_id : Long : foreign key to id of Table C
Table B
id: Long : primary key
loc_id: Long
Table C
id: Long : primary key
d_id : Long : foreign key to id of Table D
Table D
id: Long : primary key
loc_id: Long
This is the data in the tables (in order of the fields listed) ...
Table A:
1, 1, null
2, 2, null
3, null, 1
Table B:
1, 5
2, 6
Table C:
1, 1
Table D:
1, 5
And my sql query finds all the records in table A where the loc_id is 5 in Table B or Table D via the foreign keys. (2 results - row id 1 and row id 3)
select * from A
LEFT JOIN B on B.id = a.b_id
LEFT JOIN C on C.id = a.c_id
LEFT JOIN D on D.id = c.d_id
WHERE B.loc_id = 5 or D.loc_id = 5
However I am struggling to code in JPA the same results.
I have tried below which creates 2 predicates which both find 1 record when executed individually but when I "or" them it produces 0 records. How can that be right?
EntityManager em = getEntityManager();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery cq = cb.createQuery();
Root<A> rt = cq.from(A.class);
List<Predicate> predicates = new ArrayList<>();
Join<B, A> join1 = rt.join("b_id", JoinType.INNER);
Join<C, A> join2 = rt.join("c_id", JoinType.INNER);
Join<D, C> join3 = join2.join("d_id", JoinType.INNER);
predicates.add(cb.or(cb.equal(join1.get("loc_id"), 5), cb.equal(join3.get("loc_id"), 5)));
CriteriaQuery x = cq.select(rt).where(predicates.toArray(new Predicate[predicates.size()]));
Query q = em.createQuery(x);
List<A> = q.getResultList();
All suggestions gratefully accepted, thanks ....
Here is solution
EntityManager em = getEntityManager();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<A> cq = cb.createQuery(A.class);
Root<A> rt = cq.from(A.class);
Join<B, A> joinB = rt.join("b_id", JoinType.LEFT);
Join<C, A> joinC = rt.join("c_id", JoinType.LEFT);
Join<D, C> joinD = joinC.join("d_id", JoinType.LEFT);
Predicate bPredicate = cb.equal(joinB.get("loc_id"), 5);
Predicate dPredicate = cb.equal(joinD.get("loc_id"), 5);
Predicate predicate = cb.or(bPredicate, dPredicate);
CriteriaQuery<A> x = cq.select(rt).where(predicate);
TypedQuery<A> q = em.createQuery(x);
List<A> result = q.getResultList();

Linq GROUP BY or manually grouping in a loop?

Yesterday I asked this question on stackoverflow. Today I realize that if I do a GROUP BY I also need to create a new type of object.
Let's say I have some data that looks like this:
var1 var2 var3 qty
1 a 1a 50
1 a 1a 25
2 b 2b 10
2 b 2b 15
2 b 2b 10
3 a 3a 25
Here is my working LinQ query
From j In MyTable
Where j.var1 = "xxx"
Group j By Key = New With {Key .var1 = j.var1, Key .var2= j.var2, Key .var3 = j.var3} Into Group
Select New With {.var1 = Key.var1, .var2 = Key.var2, .var3 = Key.var3, .qty = Group.Sum(Function(x) x.qty)}
Actually I use Entity Framework and the code look more like this
Dim foo = (From j In dbContext.MyTable
Where j.var1 = anotherVariable
Group j By Key = New With {Key .var1 = j.var1, Key .var2= j.var2, Key .var3 = j.var3} Into Group
Select New With {.var1 = Key.var1, .var2 = Key.var2, .var3 = Key.var3, .quantity = Group.Sum(Function(x) x.Qty)}).ToArray()
foo is a new type that doesn't exist in my generated Entities. But I have an entity generated by my entity framework that can contains these. It's MyTable itself. I use a GROUP BY only to sum a column of MyTable. I query a MyTable entities and I can put the result in a MyTable entity too.
My question are
1) Can I write something like this
Dim foo = (From j In dbContext.MyTable
Where j.var1 = anotherVariable
Group j By Key = New With {Key .var1 = j.var1, Key .var2= j.var2, Key .var3 = j.var3} Into Group
Select New MyTable With {.var1 = Key.var1, .var2 = Key.var2, .var3 = Key.var3, .qty = Group.Sum(Function(x) x.qty)}).ToArray()
In this case do I need to explicitely write all the mappings ?
2) Should I change my mind. Do a simpler query without GROUP BY and try to group and sum in a VB.NET loop (For Each). Or two queries ? On to get all MyTable with a WHERE clause and another to group ?
Dim foo = dbContext.MyTable.Where(Function(p As MyTable) p.var1 = anotherVariable).ToArray()
For Each bar In foo
'Code to group and sum or another query
Next
You won't be able to instantiate MyTable in an LINQ to Entities query but you can simply enumerate the results of the projection with ToArray and then construct the entities with another Select call.

Unable to convert SQL which is meant for greatest N per group to HQL

I have two tables in Oracle namely product and product_image. As the names imply they have a one-to-many relationship from product to product_image.
The product entity:
#Entity
#Table(name = "PRODUCT", catalog = "", schema = "WAGAFASHIONDB")
public class Product implements java.io.Serializable
{
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#Column(name = "PROD_ID", nullable = false, precision = 35, scale = 0)
#SequenceGenerator(name = "productIdSequence", sequenceName = "PRODUCT_SEQ", allocationSize=1, initialValue=1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "productIdSequence")
private Long prodId;
#Column(name = "PROD_NAME", length = 50)
private String prodName;
#Column(name = "PROD_CODE", length = 50)
private String prodCode;
#Lob
#Column(name = "PROD_DESC")
private String prodDesc;
#Basic(optional = false)
#Column(name = "MARKET_PRICE", nullable = true, precision = 35, scale = 2)
private BigDecimal marketPrice;
#Basic(optional = false)
#Column(name = "SALE_PRICE", nullable = true, precision = 35, scale = 2)
private BigDecimal salePrice;
#Column(name = "PROD_FEATURED")
private Short prodFeatured;
#Column(name = "EXPRESS_DELIVERY")
private Short expressDelivery;
#Basic(optional = false)
#Column(name = "PROD_WEIGHT", nullable = true, precision = 35, scale = 2)
private BigDecimal prodWeight;
#Column(name = "PROD_OCCASSION", length = 50)
private String prodOccassion;
#Basic(optional = false)
#Column(name = "QTY_AVAILABLE", nullable = true)
private BigInteger qtyAvailable;
#Column(name = "LATEST")
private Short latest;
#Column(name = "VISIBLE")
private Short visible;
#JoinTable(name = "PRODUCT_SIZE", joinColumns = {
#JoinColumn(name = "PROD_ID", referencedColumnName = "PROD_ID")}, inverseJoinColumns = {
#JoinColumn(name = "SIZE_ID", referencedColumnName = "SIZE_ID")})
#ManyToMany(fetch = FetchType.LAZY)
private Set<SizeTable> sizeTableSet;
#ManyToMany(mappedBy = "productSet", fetch = FetchType.LAZY)
private Set<Colour> colourSet;
#OneToMany(mappedBy = "prodId", fetch = FetchType.LAZY)
private Set<Measurement> measurementSet;
#OneToMany(mappedBy = "prodId", fetch = FetchType.LAZY)
private Set<Wish> wishSet;
#OneToMany(mappedBy = "prodId", fetch = FetchType.LAZY)
private Set<Cart> cartSet;
#OneToMany(mappedBy = "prodId", fetch = FetchType.LAZY)
private Set<ProductImage> productImageSet; //<--------
#OneToMany(cascade = CascadeType.ALL, mappedBy = "product", fetch = FetchType.LAZY)
private Set<OrderItem> orderItemSet;
#JoinColumn(name = "SUB_CAT_ID", referencedColumnName = "SUB_CAT_ID")
#ManyToOne(fetch = FetchType.LAZY)
private Subcategory subCatId;
#JoinColumn(name = "FABRIC_ID", referencedColumnName = "FABRIC_ID", nullable = false)
#ManyToOne(optional = false, fetch = FetchType.LAZY)
private Fabric fabricId;
#JoinColumn(name = "BRAND_ID", referencedColumnName = "BRAND_ID")
#ManyToOne(fetch = FetchType.LAZY)
private Brand brandId;
#OneToMany(mappedBy = "prodId", fetch = FetchType.LAZY)
private Set<Inquiry> inquirySet;
#OneToMany(mappedBy = "prodId", fetch = FetchType.LAZY)
private Set<Rating> ratingSet;
}
The ProductImage entity:
#Entity
#Table(name = "PRODUCT_IMAGE", catalog = "", schema = "WAGAFASHIONDB")
public class ProductImage implements java.io.Serializable
{
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#Column(name = "PROD_IMAGE_ID", nullable = false, precision = 35, scale = 0)
#SequenceGenerator(name = "productIdSequence", sequenceName = "PRODUCT_IMAGE_SEQ", allocationSize=1, initialValue=1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "productIdSequence")
private Long prodImageId;
#Lob
#Column(name = "PROD_IMAGE")
private String prodImage;
#JoinColumn(name = "PROD_ID", referencedColumnName = "PROD_ID")
#ManyToOne(fetch = FetchType.LAZY)
private Product prodId; //<--------------
}
I need to fetch only rows for the maximum prodId for each group of rows (actually each group of products) in ProductImage i.e the greatest N per group.
The Oracle native SQL can be constructed as follows.
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, pi.prod_id FROM product_image pi GROUP BY pi.prod_id) prod_image
ON pi.prod_image_id=prod_image.prod_image_id
This works exactly as intended but I couldn't convert this SQL to HQL. Trying to execute directly as HQL in the createQuery() method causes the QuerySyntexException, unexpected token at the very first opening parentheses. Could you please given me a hint to convert this SQL to HQL?
EDIT:
Some questions like the following haven't yet answered to accomplish this as it actually means:
Best way to use hibernate for complex queries like top n per
group
hql query to retrieve top n from each group
Using HQL with MySQL how can I order the result set before the group
by so the right record is picked?
I think, there is no way to achieve this in Hibernate through HQL. I may need to break this SQL to appropriate two or more HQL statements using some DTO classes. If it is possible through a single HQL statement like the SQL above, then please don't forget answer here, since I have such requirements at too many places in my application.
Take a look at this:
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
)
which translates to:
select
productima0_.prodImageId as col_0_0_,
productima0_.prodId_prodId as col_1_0_,
product1_.prodId as prodId18_,
product1_.name as name18_
from
ProductImage productima0_
inner join
Product product1_ on productima0_.prodId_prodId=product1_.prodId
where
productima0_.prodImageId in (
select max(productima3_.prodImageId)
from
Product product2_ inner join ProductImage productima3_ on product2_.prodId=productima3_.prodId_prodId
group by product2_.prodId
)
which is what you want i guess?