I have a User entity with a many-to-many relationship with a Role entity.
#Entity
#Table(name = "auth_user")
public class OAuthUser {
// #Autowired
// #Transient
// private PasswordEncoder passwordEncoder;
//
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(name = "username")
private String userName;
#Column(name = "password")
#JsonIgnore
private String password;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "email")
private String email;
#Column(name = "is_enabled")
private boolean isEnabled;
/**
* Reference:
* https://github.com/nydiarra/springboot-jwt/blob/master/src/main/java/com/nouhoun/springboot/jwt/integration/domain/User.java
* Roles are being eagerly loaded here because they are a fairly small
* collection of items for this example.
*/
#ManyToMany(fetch = FetchType.EAGER)
#Fetch(value = FetchMode.SUBSELECT)
#JoinTable(name = "user_role", joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "id"), inverseJoinColumns = #JoinColumn(name = "role_id", referencedColumnName = "id"))
private List<Role> roles;
#ManyToMany(fetch = FetchType.EAGER)
#Fetch(value = FetchMode.SUBSELECT)
#JoinTable(name = "user_properties", joinColumns = #JoinColumn(name = "AuthID", referencedColumnName = "id"), inverseJoinColumns = #JoinColumn(name = "PropertyID", referencedColumnName = "id"))
private List<Property> properties;
I am using the Spring Data JPA repositories, and would to be able to create a custom #Query that returns a list of users based upon a particular role id.
#Query("SELECT u FROM auth_user as u WHERE u.isEnabled AND u.id IN"
+ " (SELECT r.user_id FROM user_role as r WHERE r.role_id = ?1)")
public List<OAuthUser> findByRole(int roleID);
The code above results in the error auth_user is not mapped. I do understand why I am getting the error; the framework is using the entity names (OAuthUser) rather than the table (auth_user) to perform the query. This would ordinarily not be a problem, except for there is no entity for user_role; it is simply a join table with two columns: 'user_id' and 'role_id'.
What is the appropriate way to achieve this?
Thanks.
The error says:
auth_user is not mapped
It refers to the auth_user used in the query like SELECT u FROM auth_user. It must be OAuthUser instead, in the query.
You are using the table name (auth_user) inside of you jpql #Query. You should use the class name OAuthUser instead:
#Query("SELECT u FROM OAuthUser u ...")
public List<OAuthUser> findByRole(int roleID);
If you want to use SQL instead of jpql, you need to use like this:
#Query(value = "SELECT * FROM auth_user ..." , nativeQuery=true)
public List<OAuthUser> findByRole(int roleID);
That way you could mention table name and columns.
Related
I currently have a problem with this Relationship, I have tried everything I saw on the internet. Still, I get this error: ERROR: column roles0_.user_id does not exist.
I have a boot app that has spring security, and I need to login using users from PostgreSQL database.
But I just can't get the relation between the user and the Role to work.
Here are Entity classes:
#Data
#Entity
#Table(name="user",schema = "public")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id")
private Integer id;
#Column(unique = true)
private String username;
private String password;
private boolean enabled;
#OneToMany(mappedBy = "user", fetch = FetchType.EAGER)
private List<Role> roles;
}
#Data
#Entity
#Table(name="role",schema = "public")
public class Role {
#Id
#Column(name="role_id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
#ManyToOne()
#JoinColumn(name ="user_id")
private User user;
}
The database looks fine, I looked at the column names, etc. I don't know what to do to get rid of this error. I have the user table, and another table named roles, which include id and name, 2 inputs, USER and ADMIN...
It seems that the #JoinColumn annotation requires one additional column in the roles table the one with #ManytoOne relation, because when I add the column the error disappears, but when I'm trying to get the role from each user, I get an empty List. The foreign key is set as well, from the roles column to the role_id column from role table.
worked for me this way:
#Entity
#Data
#Table(name = "users")
public class User{
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "user_roles", joinColumns = {#JoinColumn(name = "user_id", referencedColumnName = "id")},
inverseJoinColumns = {#JoinColumn(name = "role_id", referencedColumnName = "id")})
private List<Role> roles;
}
and then in roles just:
#Entity
#Table(name = "roles")
public class Role{
#ManyToMany(mappedBy = "roles", fetch = LAZY)
private List<User> users;
}
that's if you are ok with third table user_roles (user_id, role_id) which manages the many to many relation
User table :
#OneToMany(cascade = CascadeType.ALL, mappedBy = "user", fetch = FetchType.EAGER)
private List<Role> roles;
Role table :
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id")
private User user;
In my project I have many-to-many relationship between User and Role. For this reason I have also new entity UserRole which connects this two entities.
It looks like this:
User:
#Data
#Entity
#Table(NAME = "USERS")
public class User {
#Id
#Column(name = "USER_ID")
private String userId;
#Basic
#Column(name = "EMAIL")
private String email;
#OneToMany(fetch = LAZY, mappedBy = "user")
private Set<UserRole> userRoles;
}
Role:
#Data
#Entity
#Table(NAME = "ROLES")
public class Role {
#Id
#Column(name = "ROLE_ID")
private String roleId;
#Basic
#Column(name = "NAME")
private String name;
#OneToMany(fetch = LAZY, mappedBy = "role")
private Set<UserRole> userRoles;
}
UserRole:
#Data
#Entity
#IdClass(UserRolePK.class)
#Table(NAME = "USER_ROLES")
public class UserRole {
#Id
#Column(name = "USER_ID")
private String userId;
#Id
#Column(name = "ROLE_ID")
private String roleId;
#ManyToOne(fetch = LAZY)
#JoinColumn(name = "USER_ID", insertable = false, updatable = false)
private User user;
#ManyToOne(fetch = LAZY)
#JoinColumn(name = "ROLE_ID", insertable = false, updatable = false)
private Role role;
}
In this scenario User can have multiple roles.
Question: How to fetch User by his id (userId) with all assigned to him Roles using one query (JPQL)?
I know I can first fetch User by id, and than I can fetched separately Roles based on UserRole table.
But I want to do that in one query. I want to have User with List of Roles.
I would suggest you to correct your mapping in the following way:
#Data
#Entity
#Table(NAME = "USERS")
public class User {
#Id
#Column(name = "USER_ID")
private String userId;
#Column(name = "EMAIL")
private String email;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "USER_ROLES",
joinColumns = #JoinColumn(name = "USER_ID"),
inverseJoinColumns = #JoinColumn(name = "ROLE_ID"))
private Set<Role> roles;
}
#Data
#Entity
#Table(NAME = "ROLES")
public class Role {
#Id
#Column(name = "ROLE_ID")
private String roleId;
#Column(name = "NAME")
private String name;
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "roles")
private Set<User> users;
}
This correction will not affect the database schema only hibernate mapping.
Then you will be able to do as suggested in the Andronicus answer:
#Query(
"select u " +
"from User u " +
"left join fetch u.roles "+
"where u.userId = :id "
)
List<User> getUsersWithFetchedRoles(#Param("id") String id)
If you stay with your current mapping you will not be able to fetch more than one association at a time as it is explained in this article.
Additional details related to the #ManyToMany association see in the documentation.
You can use the fetch keyword:
#Query(
"select u " +
"from User u " +
"left join fetch u.userRoles "+
"where u.userId = :id "
)
List<User> getUsersByIdAndRoles(#Param("id") String id)
DB Schema - 2 tables (user and role) have many to many relationship and are bridged by an intermediate table (user_role) in postgres. I want to fetch all the roles and the name of a person who created it. Name is available in the users table but all the other details are in roles table. Roles table has a field created_by (User_id of the person who created the role).
I am trying to build a GET Request to view all the roles of a given id with the name of the person who created it
Entity class Users1.java
#Data
#Entity
#Table(name = "user")
public class Users1 {
#Id
#Column(name = "user_id")
private Long user_id;
#Column(name = "org_id")
private Long org_id;
#Column(name = "boss")
private Boolean boss;
#Column(name = "firstname")
private String firstname;
#Column(name = "created_by")
private Long created_by;
#CreationTimestamp
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "created_on")
private Date created_on;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {CascadeType.ALL})
#JoinTable(name = "user_role",
joinColumns = {#JoinColumn(name = "user_id")},
inverseJoinColumns = {#JoinColumn(name = "created_by")})
private List<Role> roles = new ArrayList<Role>();
// Getters and Setters
Entity class Role.java
#Data
#Entity
#Table(name = "role")
public class Role implements Serializable {
private static final long serialVersionUID = -2343243243242432341L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "role_id")
private Long role_id;
#Column(name = "org_id")
private Long org_id;
#Column(name = "role_name")
private String role_name;
#Column(name = "created_by")
private Long created_by;
#CreationTimestamp
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "created_on")
private Date created_on;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {CascadeType.ALL},
mappedBy = "roles")
private List<Users1> users = new ArrayList<>();
// Getters and Setters
roles.repository class
#Repository
#Transactional
public interface RolesRepository extends CrudRepository<Role,Long> {
#Query(value ="select r.*,u.firstname as firstname
from user u
join user_role ur on u.user_id = ur.user_id
join role r on ur.role_id = r.role_id
where(true = (select u.boss from user u where u.user_id = ?2) and r.org_id = ?1)
or
(false = (select u.boss from user u where u.user_id = ?2) and r.created_by =?2)",
nativeQuery=true)
public Iterable<Role> findAllById(Long org_id,Long created_by );
#Query("from Users1 u where u.user_id=?2 and u.org_id = ?1")
public Users1 findUserbyOrgId(Long org_id, Long created_by);
}
roles.Controller class :
public ResponseEntity<Iterable<Role>> getAllRoles(#RequestParam("org_id") Long org_id,
#RequestParam("created_by") Long created_by ) throws Exception {
if( null != rolesRepository.findUserbyOrg(org_id, created_by)) {
Iterable<Role> Roles = rolesRepository.findAllById(org_id, created_by); }
GET Response from postman:
[
{
"roleId": 3,
"org_id": 2,
"roleName": "manager",
"createdBy": 5,
"createdOn": 1591716178419,
}
]
I'm getting everything except the firstname. I'm not sure how to fetch that in my GET API. Any help would be really appreciated.
Not a direct answer to your question, but #ManyToMany is generally not encouraged in the real field due to the following reasons.
The joining table may need to have more information (but can't)
Since the joining table is hidden, query result is hard to anticipate.
The recommended approach is to
decompose two #ManyToMany classes to two #OneToMany classes +
one #ManyToOne class (joining table)
elevate the joining table to an entity class.
There are many practices of such cases in stackoverflow and youtube. I think it will ultimately save you more time to switch to this approach.
I'm trying to map up an existing database schema using Hibernate+JPA annotations.
One of my entities are mapped like this:
#Entity
#Table(name = "users")
public class User implements Serializable {
#Id
private int department;
#Id
private int userId;
...
And another entity, Group:
#Entity
#Table(name = "groups")
public class Group implements Serializable {
#Id
private int department;
#Id
private int groupId;
...
Group and User should have a many-to-many relationship between them, but the issue is that the join table ("user_group") only has columns "DEPARTMENT, USERID, GROUPID" - i.e. the DEPARTMENT column needs to be used in both joinColumns and inverseJoinColumns:
#ManyToMany(cascade = { CascadeType.ALL })
#JoinTable(
name = "user_groups",
joinColumns = { #JoinColumn(name = "department"), #JoinColumn(name = "groupid") },
inverseJoinColumns = {#JoinColumn(name = "department"), #JoinColumn(name = "userid") }
)
private List<User> groupUsers = new ArrayList<>();
which gives a mapping error - "Repeated column in mapping for entity".
However, it looks like this was/is possible using XML, because this exact example exists in the old Hibernate documentation. But I cannot find any evidence that this ever worked using annotations? I tried with #JoinFormula instead of #JoinColumn, but that does not compile. Is it possible?
Okay, I'm pretty sure it's not possible.
I found a promising workaround:
Create an #Embeddable for the "user_group" table:
#Embeddable
public class UserGroupMembership implements Serializable {
#ManyToOne
#JoinColumnsOrFormulas(
value = {
#JoinColumnOrFormula(column = #JoinColumn(referencedColumnName = "userid", name = "userid")),
#JoinColumnOrFormula(formula = #JoinFormula(referencedColumnName = "department", value = "department"))
})
private User user;
public UserGroupMembership(User user) {
this.user = user;
}
public UserGroupMembership() {
}
public User getUser() {
return user;
}
}
The trick is that #ManyToOne allows you to use #JoinColumnsOrFormulas, so one of the join conditions can be a formula, which I doesn't seem to work for #ManyToMany (the #JoinColumnsOrFormulas annotation is ignored as it expects the join columns to be part of the #JoinTable annotation).
The UserGroupMemberships are then mapped as a ElementCollection:
#ElementCollection
#CollectionTable(name = "user_group", joinColumns = {
#JoinColumn(name = "department", referencedColumnName = "department"),
#JoinColumn(name = "groupid", referencedColumnName = "groupid")
})
#OrderColumn(name = "seq", nullable = false)
private List<UserGroupMemberships> groupUsers = new ArrayList<>();
This only works right now for a unidirectional many-to-many relationship.
There is a unidirectional ManyToMany mapping between Role and Privilege with Role as the owning entity like so
Role
#Entity
public class Role extends BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "role_id")
private Integer roleId;
#Size(max = 45)
#Column(name = "role")
private String role;
#JoinTable(name = "role_privilege", joinColumns = {
#JoinColumn(name = "role_role_id", referencedColumnName = "role_id")}, inverseJoinColumns = {
#JoinColumn(name = "privilege_privilege_id", referencedColumnName = "privilege_id")})
#ManyToMany(
cascade = {
CascadeType.DETACH,
CascadeType.MERGE,
CascadeType.REFRESH,
CascadeType.PERSIST }, fetch = FetchType.EAGER, targetEntity = Privilege.class)
private Collection<Privilege> privilegeCollection;
#Transient
private Collection<Privilege> parentPrivilegeCollection;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "roleId")
#JsonIgnore
private Collection<User> userCollection;
public Role() {
}
//getters,setter,hashcode,equals removed for brevity
}
Privilege
#Entity
public class Privilege extends BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "privilege_id")
private Integer privilegeId;
#Size(max = 45)
#Column(name = "name")
private String name;
#Size(max = 150)
#Column(name = "description")
private String description;
#Size(max = 45)
#Column(name = "friendly_name")
private String friendlyName;
#JoinTable(name = "privilege_hierachy", joinColumns = {
#JoinColumn(name = "parent_privilege", referencedColumnName = "privilege_id")}, inverseJoinColumns = {
#JoinColumn(name = "child_privilege", referencedColumnName = "privilege_id")})
#ManyToMany
private Collection<Privilege> privilegeCollection;
public Privilege() {
}
}
The Problem
Whenever i set updated list of privileges in a role and update, the join table is successfully updated without removing either target or owning entity, and that is desired result. The problem is on update it also affect another self join table in Privilege called privilege_hierachy which is not what is expect.
Is it possible for hibernate to only update the Role-Privilege mant-to-many relationship and let other relation unchanged.
Spring Data Jpa is used for data persistence
It sounds like you are updating the privileges by (removing old privileges and) adding new ones. If you do that, clearly, the second join table (the self-referencing table) could be updated with new rows, based on what you are passing.
I see that for the self-referencing table, Privilege, you are not setting cascade type. It defaults to no operation, and that sounds like what you want. But my guess is based on what you said "Whenever i set updated list of privileges in a role", and that tells me you are creating new privileges for a role, instead of using existing privileges and associate them with the role.