Convert inner join native query to jpql - spring-boot

I have this method
#Query("select * from feed_tbl feed inner join view_tbl viewers on feed.id <> viewers.feed_id where viewers.user_id = :userId", nativeQuery = true)
fun findAll(#Param("userId") userId: Long): List<Feed>
entities:
User,
Feed
view_tbl is JoinTable in user entity

You should be able to use not in construct:
select f from Feed f where f not in (
select u.feeds from User u where u.id = :userId
)
Of cause you will need to map User to Feed as many-to-many

Related

Inner join with more than one OR conditions in spring boot

I am using spring boot specification and trying to execute a query that looks like this -
SELECT DISTINCT
p.name
FROM
partner p
INNER JOIN
detail d ON p.detail_id = d.id
INNER JOIN
account a ON d.account_id = a.id
OR d.crm_id = a.top_parent
OR d.crm_id = a.global_partner
I have used the code
Join<Partner, Detail> details = root.join("detail");
Join<Detail, Account> account = details.join("account");
Predicate global = cb.equal(details.get("crm_id "), account.get("top_parent"));
Predicate top = cb.equal(details.get("crm_id "), account.get("global_partner"));
account.on(cb.or(global, top));
However, it creates the query
SELECT DISTINCT
p.name
FROM
partner p
INNER JOIN
detail d ON p.detail_id = d.id
INNER JOIN
account a ON d.account_id = a.id
AND (d.crm_id = a.top_parent
OR d.crm_id = a.global_partner)
Notice the AND operator in the query...I need to replace it OR operator
Another use case I am struggling with
#Entity
public class Detail {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#OneToMany(mappedBy = "detail", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<Skill> skills;
}
I am trying to generate the query below using specifications
order by (select count(s.detail_id) from skill s where detail.id = s.detail_id AND s.category is not null) desc
I have used the code below
cq.orderBy(cb.desc(cb.size(details.get("skills"))));
But the query it generates is
order by (select count(s.detail_id) from skill s where detail.id = s.detail_id) desc
Notice that I am unable to add an extra AND to the order by clause
I believe you can not change that AND.
could you change the query in the following way
SELECT DISTINCT p.name
FROM partner p INNER JOIN detail d ON p.detail_id = d.id, account a
where d.account_id = a.id
OR d.crm_id = a.top_parent
OR d.crm_id = a.global_partner
and the jpa criteria similar to
Join<Partner, Detail> details = root.join("detail");
Root<Account> account = criteria.from(Account.class);
Predicate global = cb.equal(details.get("crm_id"), account.get("top_parent"));
Predicate top = cb.equal(details.get("crm_id"), account.get("global_partner"));
Predicate byId = cb.equal(details.get("account").get("id"), account.get("id"));
Predicate or = cb.or(global, top, byId);
criteria.where(or);

LINQ to SQL multiple tables left outer join w/condition in 'on' clause

SELECT u.username, COUNT(r.id)
FROM users u
LEFT JOIN userroles ur ON u.id = ur.userid
LEFT JOIN roles r ON ur.roleid = r.id AND r.name = 'Managers'
GROUP BY u.username
ORDER BY u.username
The goal is very simple, the above SQL runs fine, now I need to figure out how to convert it into LINQ code. I have the left joins working, the two things I don't know how to do is the count and the r.name = 'Managers'. Here is what I have so far, how do I finish it off?
var result =
from user in _context.Users
join userRole in _context.UserRoles on user.Id equals userRole.UserId into userUserRoleGroup
from u in userUserRoleGroup.DefaultIfEmpty()
join role in _context.Roles
on u.RoleId equals role.Id into roleUserRoleGroup
from r in roleUserRoleGroup.DefaultIfEmpty()
select new UserRole { Username = user.UserName, RoleName = r.Name };
You can do the count like this:
var count = from role in _context.Role
group role by role.UserId into groupedRoles
select new
{
RoleId = groupedRoles.Key,
Total = groupedRoles.Count()
}
and then you can join the variable "count" in your query.
It will have the property "Total" that is your count.

Select from junction table

everybody!
What should I do if I need to make select from junction table?
For example, I develop project and I need to make chats between users. I have two entities: User and Chat, and many-to-many relation between them (accordingly, I have three tables: user, chat, chat_user). I try to get all chats, which user is member, and to get all users from these chats.
I made the following SQL query:
SELECT *
FROM chat c
INNER JOIN chat_user cu ON c.id = cu.chat_id
INNER JOIN user u ON u.id = cu.user_id
WHERE c.id IN (SELECT chat_id
FROM chat_user
WHERE user_id = <idUser>);
But I don't know how to translate in DQL subquery SELECT chat_id FROM chat_user WHERE user_id = <idUser>, because a haven't additional entity for table chat_user.
And I tried to add entity ChatUser and get data in ChatRepository smt. like this:
public function getChatsData($idUser)
{
$subQuery = $this->getEntityManager()
->getRepository(ChatUser::class)
->createQueryBuilder('chus')
->select('chus.chat')
->andWhere('chus.user = :idUser')
->setParameter('idUser', $idUser)
;
$qb = $this->createQueryBuilder('c');
return $qb
->innerJoin('c.chatUsers', 'cu')
->addSelect('cu')
->innerJoin('cu.user', 'u')
->addSelect('u')
->innerJoin('c.messages', 'm')
->addSelect('m')
->andWhere('u.id = :idUser')
->andWhere($qb->expr()->in(
'c.id',
$subQuery->getDQL()
))
->setParameter('idUser', $idUser)
->getQuery()
->getResult()
;
}
but it doesn't work. I get error [Semantical Error] line 0, col 12 near 'chat FROM App\Entity\ChatUser': Error: Invalid PathExpression. Must be a StateFieldPathExpression.
Have Doctrine standard tools for such tasks?

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 :)

Changing DB query for specific user

In our project, we are using spring, JPA & hibernate over Mysql.
I have a DB table that contains permissions per each user. this table has a lot of implications all over the system.
I need to add a "super user" / admin, that has permissions for everything.
I want that every query that made by this admin user - will "simulate" like the permissions table contains permissions to this admin user.
For example:
The DB table is -
entityId | userId
If we have 2 entities, and user#17 has permissions to them, the rows would be
1 | 17
2 | 17
Lets say the admin user is user#20. I don't want to add rows to this table. I want that every access to this table will add "temporary rows".
For example, if I have the following query:
select e from MyEntity e where e.id in (select p.entityId from PermissionEntity p where p.userId = :userId)
I want that if the logged in user is admin user, the query will look like:
select e from MyEntity e where e.id in (select p.entityId from PermissionEntity p)
or
select e from MyEntity e where e.id in (select p.entityId from PermissionEntity p where 1=1 or p.userId = :userId)
Any advice how to implement this?
I suppose you have a central method where you check the rights. If so, simply try:
TypedQuery<MyEntity> query = null;
if (crtUser.getId() != 20) {
query = entityManager.createQuery("select e from MyEntity e where e.id in (select p.entityId from PermissionEntity p p.userId = :userId)", MyEntitiy.class);
}
else {
query = entityManager.createQuery("select e from MyEntity e", MyEntitiy.class);
}
If you don't have a central method for this logic, but have a single method, then you could try the following:
query = entityManager.createQuery("select e from MyEntity e where e.id in (select p.entityId from PermissionEntity p p.userId = :userId) OR :userId=20", MyEntitiy.class);

Resources