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();
Related
We have below employee data in our table and we need to find all employees where flag is false and if the reporting person flag is true we should exclude manger even if this flag is false
Sample Data
id name manager_name flag
1 a null false
2 b a true
3 c d false
4 e null false
5 f e false
Output should be
id name manager_name flag
3 c d false
4 e null false
5 f e false
How to achieve the above requirement using Hibernate Criteria builder?
In SQL this is achieved using below query
select * from employee where flag = false and id not in (
select e1.id from employee e1, employee e2 where e1.name = e2.manager_name
and e2.flag= true)
Add to the Employee class --->
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="managerName", referencedColumnName="name",insertable=false,
updatable=false)
private Employee manager;
The criteria Query --->
CriteriaBuilder cb = em.getCriteriaBuilder() ;
CriteriaQuery<Employee> cq = cb.createQuery(Employee.class);
Root<Employee> root = cq.from(Employee.class);
Subquery<Integer> sub = cq.subquery(Integer.class);
Root<Employee> subRoot = sub.from(Employee.class);
Join <Employee,Employee> empJoin = subRoot.join("manager",JoinType.INNER);
Predicate subP2 = cb.equal(subRoot.get("flag"),true);
sub.where(subP2);
sub.select(empJoin.get("id"));
Predicate flagP = cb.equal(root.get("flag"),false);
Predicate idNot = cb.not(root.get("id").in(sub)) ;
Predicate finalP = cb.and(flagP,idNot);
cq.where(finalP);
TypedQuery<Employee>query = em.createQuery(cq.select(root));
List<Employee> result = query.getResultList();
Hibernate Query generated --->
select
employee0_.id as id1_0_,
employee0_.flag as flag2_0_,
employee0_.manager_name as manager_3_0_,
employee0_.name as name4_0_
from
employee employee0_
where
employee0_.flag=?
and (
employee0_.id not in (
select
employee2_.id
from
employee employee1_
inner join
employee employee2_
on employee1_.manager_name=employee2_.name
where
employee1_.flag=?
)
)
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;
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.
How do I write the following sql join in linq?
select Campaign.CampaignName, COUNT(*) as total
from Campaign join CampaignAsset
on CampaignAsset.CampaignId=Campaign.CampaignId
where Campaign.UserProfileId=65
Group By Campaign.CampaignName
Try This :
YourDatabaseName dataContext = new YourDatabaseName();
var result = from c in dataContext.Campaign
join ca in dataContext.CampaignAsset on c.CampaignId equals ca.CampaignId into j1
from j2 in j1.DefaultIfEmpty()
where c.UserProfileId = 65
group j2 by c.CampaignName into grouped
select new { CampaignName = grouped.Key, Count = grouped.Count() };
I have,
A list, MyList, of objects with fields:
string A;
string B;
Conceptually, this is similar to a two column SQL Table with columns A, B.
I'm trying to create a linq expression that would produce the three column result set of this T-SQL on such a conceptual table:
SELECT A, B, COUNT(B)
FROM T1
GROUP BY A, B
That is, if I had a table such as:
A B
----------
x g
x g
x g
x s
y g
y g
I would expect:
A B COUNT(B)
-------------------------
x g 3
x s 1
y g 2
My best efforts were this:
var result = from MyObjs in MyList
group MyObjs by new { MyObjs.A, MyObjs.B } into g
select new { g.Key.A, g.Key.B, g.Key.B.Count() }
But the count appears to return the total number of B's not the number of B's per multiple column group. How can this be fixed?
Try this.... (off the top of my head)
var result = from MyObjs in MyList
group MyObjs by new { MyObjs.A, MyObjs.B } into g
select new { g.Key.A, g.Key.B, MyCount = g.Count() }
Or if you prefer...
var result = MyList.GroupBy(x => new {x.A, x.B})
.Select(g => new {g.Key.A, g.Key.B, MyCount = g.Count()});