Unexpected Cross Join Generated SQL Using EntityManager with hibernate - sql

I'm Trying to get the number of user in a specific group, I've done the following :
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Long> q = cb.createQuery(Long.class);
q.select(cb.count(q.from(User.class))).where(cb.equal(q.from(User.class).get("userGroup"), groupId));
But instead of getting the number of user in a group , I get this number multiplied by the number of all users in my table, the generated ORM request looks like this :
Hibernate: select count(*) as col_0_0_ from app_user user0_ cross join app_user user1_ where user1_.user_group=1
EDIT :
This is my user Model :
public class User {
private String userFirstName;
private String userLastName;
/* some stuff */
#ManyToOne
private Group userGroup;
}
And my group model has an int attribute annotated with #Id and named id. How Can I get the number of user by group id in this case ?

This is the expected behaviour. CriteriaQuery is mutable and every time you call from(), it
Create and add a query root corresponding to the given entity, forming a cartesian product with any existing roots. (Javadoc of Java EE)
To achieve predictable result please create Root once and use the reference.
CriteriaQuery<Long> q = cb.createQuery(Long.class);
Root<User> u = q.from(User.class);
q.select(cb.count(u)).where(cb.equal(u.get("userGroup"), groupId));
Within above code u plays same role as in this query
SELECT u.name FROM User u
Explaining it further, calling from() twice would be equivalent of
SELECT u1, u2 FROM User u1, User u2

Related

Matching Many To Many relation

I have the following entities:
#Entity
class User {
#ManyToMany(type => Group)
#JoinTable()
groups: Group[];
}
#Entity
class MediaObject {
#ManyToMany(type => Group)
#JoinTable()
groups: Group[];
}
#Entity
class Group {
// [...]
}
Now I want to select every MediaObject which has at least one group in common with the one specific User.
Example:
User 1 MediaObject 1
-----------------------------
Group 1 |--- Group 2
Group 2 ---| Group 3
User 1 has at least one same group as MediaObject
How can I build a where sql query for this? I use typeorm for building my queries, yet every sql query would help. Also, I want to understand how.
Typeorm joins the tables like this
LEFT JOIN "group" "groups" ON "groups"."id" = "media_groups"."groupId"
Using a simple JOIN you may retrieve MediaObject Id's that share at least one group with the user. Than use IN to retrieve the MediaObject's
select *
from MediaObject mo
where mo.id in
(
select moJoin.mediaObjectId
from media_object_groups_group moJoin
join user_groups_group uJoin on moJoin.groupId = uJoin.groupId
where uJoin.userId = 1
)
If there can be multiple overlapping groups between same MediaObject and the same User, an EXISTS semi-join might be faster than using IN:
SELECT m.*
FROM "MediaObject" m
WHERE EXISTS (
SELECT -- select list can be empty here
FROM user_groups_group gu
JOIN media_object_groups_group gm USING ("groupId")
WHERE gu."userId" = 1
AND gm."mediaObjectId" = m.id
);
Else, Radim's query should serve just fine after adding some double-quotes.
This assumes that referential integrity is enforced with foreign key constraints, so it's safe to rely on user_groups_group."userId" without checking the corresponding user even exists.
It's unwise to use reserved words like "user" or "group" or CaMeL-case strings as identifiers. Either requires double-quoting. ORMs regularly serve poorly in this respect. See:
Are PostgreSQL column names case-sensitive?

Join table groups on authentication in laravel 5.2

I am using laravel 5.2 to develop an application. Now what I need is I have two tables first one is users and second one is groups. Groups table is the parent table of the users table where groups_id is the column with foreign key with groups table (group_id). Now when login I am using the code as follows:
$users = User::leftJoin('groups', 'groups.group_id', '=', 'users.groups_id')->where('email', base64_decode(base64_decode(trim($request->input('user_login')))))->first();
where i used join to join the groups table. But It is executing perfectly but does not show the data in the session (Auth::user()).
So please suggest me how to add the second table in the session but only join the row with foreign key records.
Thanks.
You have define a relation in User Model like this
class User extends Authenticatable
{
......
/*
* Relation to Group Model
*
*/
public function group()
{
return $this->belongsTo('App\Models\Group','group_id','group_id');
}
......
}
Now you can access authored user group at anytime , anywhere by these line of code
auth()->user()->group()
or
$user = User::find($id);
$user->group();
and to retrieve users from a group you can define reverse of this relation in Group Model like this
class Group extends Mdoels
{
..........
public function Users()
{
return $this->hasMany('App\Models\Users','group_id','group_id');
}
..........
}
you can find more information on docs here

how to write a hql to get all records without permissions

I have three tables
Project
id, name
ProjectAuthorization
id, project_id, user_id , permission
User
id, name
Project.java
#OneToMany(mappedBy = "project", cascade = CascadeType.ALL)
private List<ProjectAuthorization> permissions;
User.java
#OneToMany(mappedBy = "user",cascade =CascadeType.ALL)
private List<ProjectAuthorization> permissions;
ProjectAuthorization.java
#ManyToOne()
private Project project;
#ManyToOne()
private User user;
I want to get all projects that current user without any permissions, how to write it by hibernate query?
Update
hql like this:
select p from Project as p left join p.permissions as pa where pa.user.id!=:userid and p.id not in (select pa2.project.id from ProjectAuthorization as pa2 where pa2.user.id=:userid)");
For example following query (also valid JPQL, no Hibernate specific constructs in use) can be used:
SELECT p
FROM Project p
WHERE p.id NOT IN
(SELECT authorization.project.id
FROM ProjectAuthorization authorization
WHERE authorization.user = :currentUser)

Database joins with three tables, one is a category / tag table

I have three database tables - one containing units, one containing categories of those units, and one containing user permissions.
If a user can access a category, they are allowed to access any units within the category.
I am trying to create a query that will retrieve a list of units they are allowed to access. So far I have a very awkward query that returns duplicates, and am a bit stuck, and it's Friday afternoon, and I wanna go home!
Here are my tables:
UnitCategory
categoryid unitid
Unit
id name
UnitCategoryUser
unitid userid categoryid
- can be null - can be null
Thanks
I suspect that UnitCategoryUser is being used as a catch-all table for the relationships between users, categories and units - so that units are recorded as belonging to categories on records where the userid is null, and users can access units even if they don't have access to their categories, where the categoryid is null.
It would also be possible for units to belong to multiple categories, and for users to be recorded as being able to access the same units mutliple times - both where a user can access multiple categories (each of which can access the same unit) and where a user has been granted access to a unit without access to the category, then subsequently granted access to the category.
The following query should return all units for a specified user:
select distinct u.name
from unit u
join unitcategory uc on u.id = uc.unitid
join unitcategoryuser ucu on uc.category_id = ucu.categoryid
where ucu.userid = YourUserID
union
select distinct u2.name
from unit u2
join unitcategoryuser ucu2 on u2.id = ucu2.unitid
where ucu2.userid = YourUserID
SELECT U.Name
FROM UnitCategoryUser UCU
JOIN UnitCategory UC ON UC.categoryid = UCU.categoryid
JOIN Unit U ON U.id = UC.UnitId
WHERE UCU.UserId = YourUserId
Does that not return your correct data? I may be missing something :x
select unit.name from unit, unitcategory, unitcategoryuser
where unit.id = unitcategory.unitid and unitcategory.categoryid = unitcategoryuser.categoryid and unitcategoryuser.userid = "Me"
Something like that?

Django: Order a model by a many-to-many field

I am writing a Django application that has a model for People, and I have hit a snag. I am assigning Role objects to people using a Many-To-Many relationship - where Roles have a name and a weight. I wish to order my list of people by their heaviest role's weight. If I do People.objects.order_by('-roles__weight'), then I get duplicates when people have multiple roles assigned to them.
My initial idea was to add a denormalized field called heaviest-role-weight - and sort by that. This could then be updated every time a new role was added or removed from a user. However, it turns out that there is no way to perform a custom action every time a ManyToManyField is updated in Django (yet, anyway).
So, I thought I could then go completely overboard and write a custom field, descriptor and manager to handle this - but that seems extremely difficult when the ManyRelatedManager is created dynamically for a ManyToManyField.
I have been trying to come up with some clever SQL that could do this for me - I'm sure it's possible with a subquery (or a few), but I'd be worried about it not being compatible will all the database backends Django supports.
Has anyone done this before - or have any ideas how it could be achieved?
Django 1.1 (currently beta) adds aggregation support. Your query can be done with something like:
from django.db.models import Max
People.objects.annotate(max_weight=Max('roles__weight')).order_by('-max_weight')
This sorts people by their heaviest roles, without returning duplicates.
The generated query is:
SELECT people.id, people.name, MAX(role.weight) AS max_weight
FROM people LEFT OUTER JOIN people_roles ON (people.id = people_roles.people_id)
LEFT OUTER JOIN role ON (people_roles.role_id = role.id)
GROUP BY people.id, people.name
ORDER BY max_weight DESC
Here's a way to do it without an annotation:
class Role(models.Model):
pass
class PersonRole(models.Model):
weight = models.IntegerField()
person = models.ForeignKey('Person')
role = models.ForeignKey(Role)
class Meta:
# if you have an inline configured in the admin, this will
# make the roles order properly
ordering = ['weight']
class Person(models.Model):
roles = models.ManyToManyField('Role', through='PersonRole')
def ordered_roles(self):
"Return a properly ordered set of roles"
return self.roles.all().order_by('personrole__weight')
This lets you say something like:
>>> person = Person.objects.get(id=1)
>>> roles = person.ordered_roles()
Something like this in SQL:
select p.*, max (r.Weight) as HeaviestWeight
from persons p
inner join RolePersons rp on p.id = rp.PersonID
innerjoin Roles r on rp.RoleID = r.id
group by p.*
order by HeaviestWeight desc
Note: group by p.* may be disallowed by your dialect of SQL. If so, just list all the columns in table p that you intend to use in the select clause.
Note: if you just group by p.ID, you won't be able to call for the other columns in p in your select clause.
I don't know how this interacts with Django.