JPA Filter Properties of Queries - java

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)

Related

Spring JPA Multiple join conditions

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

JPA: Fetching list of entities by list of values for a given column

I am new to JPA and Hibernate.
In one use case, I need to fetch all users from DB with associated email present in given email list.
To do this, I have written a custom JPA query as follows:
#Query("SELECT u from User u where u.email in :emailIds")
List<User> findUsersByEmailIds(#Param("emailIds") List<String> emailIdList);
But, I would like to know, is there any better way to do the same?
As commented by Vishnu, here is the explanation:
Assuming in your User entity you have a field like this:
private String email;
Then in your repository you can go like this:
List<User> findByEmailIn(List<String> emailIdList);
The important key thing to notice here is the Capital E in findByEmailIn while in your entity this field was email (small e). This way you can eliminate the #Query statement completely.

JPA - If a parent entity was referenced before a JPQL query fetching child objects, the parent entity is fetched in the results ignoring lazy load

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.

Change update insert attributes programmatically

I would like to know if it's posible to change the insert and update attributes of a property defined in the mapping of a Class.
This is because in one scenario I need to update a property (or properties), but not in another, it's posible?
Thanks in advance
EDIT: Lets say that I've the Class User(with name, surname and loginDate), when the user logs into the app, I need to update only loginDate. But the administrator of the system must be able to edit the name and the surname of the User.
The only other solution that ocurrs to me is to use HQL for a Update (or in the worst case SQL), but I want if it's posible to modify that attributes.
EDIT 2: after reading Java persistence with hibernate and some forum threads I found that once the sessionFactory is created the mappings are immutables, and though You can change the properties programmatically, You need to create a new sessionFactory
// this is what the login screen calls
void updateLoginDate(Date date)
{
User user = session.get(User.class);
user.setDate(date);
session.Flush();
}
and in the mapping you could specify dynamicUpdate = true on the class so that the generated sql only updates columns which have changed

Querying many to many association where the entity doesn't have a certain value using Hibernate Criteria API

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

Categories