I am working on my first Spring Boot application and I am having a bit of a struggle within the joining of two tables/entities.
I need to join my user_roles table to the users table so that the user will get its correct roles directly from the database (postgres).
The thing that is my the fact that the roles are depending on the current organization that the user is in. The app allows a user to work for multiple companies at once, and switch between them. The roles a user has in organization 1 may not apply to organization 2.
The user has a roles field in the database which should be mapped to the correct roles. The user also has a 'current_organization_id' field, which contains the current user organization.
I think the solution wouldn't be that difficult but my brain is not solving this case yet.
Roles could be mapped on 'user_id' (UUID) and 'cur_org_id' (UUID) with an table like 'user_role_within_org'. But I don't know how to do that within JPA.
An explanation how your solution is solving this would be nice, it is important to understand code and not just copy paste it ;)
Thanks in advance,
Jesse
Current database model
Try storing a Map of Organization->List in the User.
#Entity
class User {
#ManyToMany
Map<Organization, List<Role>> roles;
}
To get the roles in current organization try the following JPQL:
SELECT VALUE(r)
FROM User u JOIN u.roles r
WHERE KEY(r).id = u.currentOrganization.id
Related
i have an issue (maybe only in my head :-)) with some Database-Design JPA related stuff. I want to have the following:
Permission table:
ProjectId
UserId
This table should have an entry for each user and each project the user has permission to. Example:
ProjectId1 - user1
ProjectId1 - user2
ProjectId2 - user1
For sure, i also have a project table with the projectId as Primary Key. And i want to have a OneToMany dependency to the permission table.
What i do not want to do is to have a generated ID in the permissions table. As far as i know this can be solved with an EmbeddedId.
Using an EmbeddedId, i could not get a OneToMany or ManyToMany dependency to work. I tried a lot of stuff but in the end i am not sure if this is maybe the wrong way to go?
What worked was to have a generatedId in the Permissions table, but i think this should not be needed, or?
Thanks in advance
Edited
I have a simple uni directional #ManyToOne Lazy relationship:
public class UserLog {
...
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "userId", updatable=false,insertable=false)
private User user;
...
}
I use lazy loading, because there are cases when I want the user field to be populated, but sometimes not. I use spring, and my app is configured with hibernate, and I use the hibernate 5 module in my configuration so my entities are only fetched in my json results when I request it.
#Bean
public Module hibernate5Module()
{
return new Hibernate5Module();
}
So the problem is, when I want to fetch for example an user's log in this way:
User user = userRepository.findOne(userId);
...
/*Checking user object before requesting logs...*/
...
List<UserLog> userlogs = userLogRepository.getUserLogs(userId);
// (query looks like this: "select u from UserLog u where u.userId = ?1")
When I return my userlogs list, the user field is populated in the json response.
If I don't fetch the user object from the userRepository (commenting out the first line), then the user field is empty correctly.
I tested modifying my getUserLogs query to not make any reference to the userId field, but the user is still fetched.
So my question is, how can I make JPA to fetch the user field when I access it directly (e.g using JOIN FETCH u.user in my query)? Why is the user field fetched, even though I didn't access the user field in my userlog object?
I'd greatly appreciate any advice.
Edit: By using entityManager.clear(); before requesting the userlogs, the user field is not populated anymore. This solution seems a bit hacky, there must be another way other than detaching the entities? Because if I want to modify the User object after requesting the logs, I'd have to request the User object again.
What is happening is quite simple:
You are asking from db a certain User by his id and right after a UserLog by the same userId.
Now after the first request hibernate is keeping the User in it's first level cache.
In your second request you specify lazy fetching for User relationship and, as you noted, it's working:
(commenting out the first line), then the user field is empty correctly.
The point here is that hibernate already has the corresponding User in the cache so there is no need to query the database for it and just give it to you.
If instead you clear cache as you tried, you force hibernate to hit the db and in turn to give you just a proxy as per Lazy fetching
You should be able to verify from log that no additional query is being executed.
I m thinking the best way to audit and filter JPA Entities Properties and Fields
My Question is think if exists an interface or thing that filter the results
Example i have a User with Admin role and another User with Seller role, both can make queries, but not both can read and write the all properties. To writes filter i thinking make a interceptor with Thread attributes to know the role of current Thread. but in the query i can not know what is the property to filter.
Example
EntityManager em = ...
Query q = em.createQuery ("SELECT AVG(x.price) FROM Magazine x");
Number result = (Number) q.getSingleResult ();
To interceptor the result of Method is a Integer but how to a know what is the entity and what is the property to read.
for the user with seller role the price must shows
But for the user with admin role the price must be NULL (suppose that the admin can not see the prices)
Exist some with JPA or Hibernate to do this? i make a question to air.. to listen some idea.
Thanks,
If I understood you correctly for the the first problem you can use TypedQuery. Authorisation is a different problem and you should handle it somewhere else (not in DB code)
I am trying to inner join two tables on one column.
From DB side, there's no mapping as it's something I don't want to discuss.
I want to execute HQL query using INNER JOIN and retrieve ROLE objects/results.
Here's my hql so far
session.createQuery("from ROLE as role INNER JOIN INVOLVEMENT as involvement ON role.id = involvement.roleid WHERE involvement.id = X").list();
I see ON is not available on HQL. how do i explicitly tell Hibernate to JOIN on this column only.
I tried below one too
select roleSpec from ROLE as role, INVOLVEMENT as involvement WHERE role.ID = involvement.role_id and involvement.id =27251352
But I am getting ROLE not mapped in exception.
Please check that your ROLE is indeed a mapped entity.
In addition, you don't need to perform "ON" - hibernate knows what is the join column (I know how to define this at JPA ) - so no need to provide it at the statement.
It should be -
session.createQuery("from Role as role INNER JOIN Involvement as involvement WHERE involvement.id = X").list();
I assume you have Role class mapped to ROLE table, and Involvement class mapped to Involement table.
Maybe you used table names by mistake, and this is why you get the "not mapped" error.
Last time I wrote HQL (and not JPA-QL) I used the following link as reference, it provides all the info needed.
I got two classes User and Role which are mapped to each other using a many to many associations. Now I'm trying to query all users which doesn't have a certain role using the Hibernate Criteria API. But I'm a little bit stuck.
To query users with a certain role I use the following query, which works just fine.
Session session = getSessionFactory().getCurrentSession();
Criteria mainCrit = session.createCriteria(boClass);
return mainCrit.createAlias("roles", "r").add( Restrictions.eq("r.name", roleName)).list();
Now I'm a little bit confused how to reverse the query and get all user that don't have a certain role. If possible I want to explicit exclude a certain role and don't query for all roles chaining them with OR as there may be more roles added later dynamically.
UPDATE
To get a better understanding of my scenario you can see the association I'm trying to query in another question.
Furthermore I would also add that the name property of the Role class is an Enum, don't know if this is important or changes the way to query the database.
There's perhaps a more elegant way, but the only one I've found is to query for all the users for which there doesn't exist any role in the user's roles which has the given role name.
This is done like this:
Criteria c = session.createCriteria(User.class, "user");
DetachedCriteria roleOfUserWithName = DetachedCriteria.forClass(Role.class, "role");
roleOfUserWithName.createAlias("role.users", "userOfTheRole");
roleOfUserWithName.add(Restrictions.eqProperty("userOfTheRole.id", "user.id"));
roleOfUserWithName.add(Restrictions.eq("role.name", roleName);
roleOfUserWithName.setProjection(Projections.id());
c.add(Subqueries.notExists(roleOfUserWithName));
It's equivalent to the following HQL query:
select user from User user where not exists (
select role.id from Role role inner join role.users userOfTheRole
where userOfTheRole.id = user.id
and role.name = :roleName);