I have 2 entites User and Role with many to many relationship. I want to get role_id for specific user_id.
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#ManyToMany
private Set<Role> roles;
}
#Entity
#Table(name = "role")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#ManyToMany(mappedBy = "roles")
private Set<User> users;
}
#Override
public List<Long> getUserRole(User user) {
return entityManager.createNativeQuery("??????").getResultList();
}
How should I write query in JPA?
First if you use createNativeQuery then you'll supply a native SQL query. If you want to use JPQL use the createQuery
To select the role_id by user id do
select role.id from Role role join role.users user where user.id=:p1
After that, you can do
entityManager.createQuery("query").setParameter("p1", user.getId()).getResultList();
Related
I know only basics of DB and JPA/Hibernate. I have to manage a User table, where a user can have many roles. The roles are contained in a catalog table which in my User formulary i do not pretend to manage/modify, i just need the catalog values as a reference to add or delete to my user.
I think the best approach would be to create a relationship table between User and Role to hold the users and their roles 'User_Roles' (unless there is a more efficient approach).
I am not allowed to modify the Role entity since it is used for different purposes in a lot of other areas of my app that are independent of the User table.
I've seen a lot of examples but I still do not know which one exactly aplies to my specific needs. How can I map my User and its roles in a sigle Entity with JPA and Hibernate?
Maybe the next image describes better what I want:
Thank you very much in advance for your answers.
In your case you have to use #ManyToMany to associate both tables.
That should look at this:
#Entity
#Table(name = "User")
public class User {
...
#ManyToMany
#JoinTable(name = "User_Roles", joinColumn = "id_person")
private Set<Role> roles = new HashSet<>;
}
#Entity
#Table(name = "Role")
public class Role {
...
#ManyToMany(mappedBy = "roles")
private Set<User> users = new HashSet<>;
}
What you're describing is a one-to-many relationship but it's between User and the joining table - User_Roles. Since there is not much you can do to avoid the joining table, the best thing would be to use #ManyToMany with #JoinTable annotations to map the relationship. Remember to use Set instead of List. You don't need an entity for the joinint table then.
You can find a discussion about this topic in this blog post.
As per your above screen, what I understood user can be assigned more than 1 role.
i.e. 1 user can be mapped to multiple role and 1 role can be mapped to multiple users.
Hence relationship between user and role is many to many.
many to many relationship can be achieved using third table which is called mapping table.
so , we have following tables in your example :-
user
user_roles
role
#Entity
#Table(name = "user")
#JsonInclude(JsonInclude.Include.NON_NULL)
public class User {
#Id
#SequenceGenerator(name = "USER_ID_GENERATOR", sequenceName = "USER_SEQ",
allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "USER_ID_GENERATOR")
#Column(name = "user_id")
private Long userId;
#OneToOne
#JoinColumn(name = "persion_id")
private person person;`
enter code
here`
#Basic
#Column(name = "date")
private Date date;
#Basic
#Column(name = "observations")
private String observations;
#Basic
#Column(name = "text")
private String text;
#JsonIgnore
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<UserRoles> users = new ArrayList<>();
}
#Entity
#Table(name = "role")
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Role {
#Id
#SequenceGenerator(name = "ROLE_ID_GENERATOR", sequenceName = "ROLE_SEQ",
allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ROLE_ID_GENERATOR")
#Column(name = "role_id")
private Long roleId;
#Basic
#Column(name = "id1")
private Long idOne;
#Basic
#Column(name = "id1")
private Long idTwo;
#Basic
#Column(name = "id1")
private Long idThree;
#Basic
#Column(name = "text")
private String text;
#JsonIgnore
#OneToMany(mappedBy = "role", cascade = CascadeType.ALL)
private List<UserRoles> users = new ArrayList<>();
}
#Entity
#Getter
#Setter
#Table(name = "user_roles")
#JsonInclude(JsonInclude.Include.NON_NULL)
#Audited
public class UserRoles {
private static final long serialVersionUID = 1L;
#EmbeddedId
UserRolesKey userRoleId;
#JsonIgnore
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("role_id")
#JoinColumn(name = "role_id")
Roles role;
#JsonIgnore
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("user_id")
#JoinColumn(user_id)
User user;
#PrePersist
private void prePersist() {
super.onPrePersist();
if (this.getId() == null) {
UserRolesKey mapKey = new UserRolesKey();
mapKey.setRoleId(this.getRole().getRoleId());
mapKey.setUserRoleId(this.getUser().getUserId());
this.setId(mapKey);
}
}
}
While saving you just need to populate user entity with all the uaerRoles mapping entity and persist it. jpa will save all the details.
while updating role assign to user you need to fetch the user entity and update the mapping by adding new userRoles entity and nullifying the while is going to be removed.
I have three entities. those are:
#Entity
public class Organization {
#Id
private long id;
#Column
private String name;
}
#Entity
public class Book {
#Id
private Long id;
#Column
private String name;
#ManyToOne
private Organization organization;
}
#Entity
public class Account {
#Id
private Long id;
#Column
private String name;
#ManyToOne
private Book book;
}
In these three entities I would like to perform following sql:
SELECT acc.name, acc.id
FROM account acc
JOIN book b on acc.book_id = b.id
JOIN organization org on b.organization_id = org.id
WHERE org.name = 'XYZ'
In this case Account entity has no relation with the Organization entity directly. Account entity has the relation via Book. How can I achieve this using hibernate criteria dynamic query?
Another way
public List<Account> getAccountListByOrgName(String name){
return sessionFactory.getCurrentSession().createCriteria(Account.class)
.createAlias("book", "book")
.createAlias("book.organization", "organization")
.add(Restrictions.eq("organization.name", name))
.list();
}
you can do like this :
Criteria accountCriteria = getCurrentSession().createCriteria(Account.class,"acc");
Criteria bookCriteria = accountCriteria .createCriteria("book","b");
Criteria orgCriteria = bookCriteria.createCriteria("organization","org");
orgCriteria.add(Restrictions.eq("name", "XYZ"));
ProjectionList properties = Projections.projectionList();
properties.add(Projections.property("name"));
properties.add(Projections.property("id"));
accountCriteria.setProjection(properties);
accountCriteria.list();
I have a class User, that contains List of Contacts
User class:
#Entity
#Table(name = "profile")
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(unique = true)
private String login;
#OneToMany(mappedBy = "owner", cascade = CascadeType.ALL)
private List<Contact> contacts;
...
...
}
Contact class:
#Entity
#Table
public class Contact implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne
#JoinColumn(name = "user_profile_id")
private User owner;
...
...
}
how may i get List of Contacts by User.id field?
Not sure I got what you want...
Simply look for the User by Id, and you will get his/her contacts. However, the collection will be lazy loaded, and the list will be really loaded when you will call getContacts() or Hibernate.initialize(user.getContacts()). Change FetchMode as EAGER in the #OneToMany in case you don't want lazy loading...
For e.g., using HQL Query :
String hql = "from User where id = :userid";
Query query = session.createQuery(hql);
query.setInteger("userid", myUserIdIWantContacts);
User user = (User) query.uniqueResult();
or Criteria :
Criteria cr = session.createCriteria(User.class);
cr.add(Restrictions.eq("id", myUserIdIWantContacts));
User user = (User) cr.uniqueResult();
Contact has a foreign key column associated to User so you don't need to use join
from Contact where owner.id = :userId
Please, don't use primitive types like int id, use Integer id instead.
I've read examples but have my personal question to you.
I have 2 tables:
Role:
id, name
User:
id, login, name, role_id
Role entity
#Entity
#Table(name = "role")
public class Role {
#Id
#Column(name = "id")
private long id;
#Column(name = "name", length = 45)
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "role")
private Set<User> user = new HashSet<>();
//getters and setters
User entity
#Entity
#Table(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id",insertable = false, updatable = false)
private long id;
#Column(name = "login")
private String login;
#Column(name = "user_name")
private String userName;
#ManyToOne(fetch = FetchType.LAZY)
private Role role;
//getters and setters
And repository:
public interface UserRepository extends JpaRepository<User, Long> {
String Q_GET_ALL_USERS = "from User u left join Role r on u.role_id=r.id";
#Query(Q_GET_ALL_USERS)
Collection<User> getAllUsers();
This code is showing: Caused by: java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: Path expected for join! [from com.example.jpa.model.User u left join Role r on u.role_id=r.id]
How I understand entity can't contains 'id' (in my case in Role) for references and I should remove this field. But entity should have '#Id'.
In this case I should create new column in 'Role'? or I can use more beautiful decision?
I put all project to bb
To use join in HQL (JPQL) you don't need on clause
String Q_GET_ALL_USERS = "select u from User u left join u.role";
This query doesn't have any sence because of you don't use role in the where clause.
If you want to get users with a fetched role you can use join fetch
String Q_GET_ALL_USERS = "select u from User u left join fetch u.role";
Update
Your schema for User and Role is not commonly used. I advice to you make #ManyToMany association from user to roles and remove any user association from the Role
#Entity
#Table(name = "user")
public class User {
#ManyToMany(fetch = FetchType.LAZY)
private Set<Role> roles;
}
#Entity
#Table(name = "role")
public class Role {
#Id
#Column(name = "id")
private long id;
#Column(name = "name", length = 45)
private String name;
}
No, you should create a new column in User.
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "role_id")
private Role role;
Thank you all for answers. Right entities and query below (plus tables schema).
Tables (queries)
CREATE TABLE role (
id INT NOT NULL PRIMARY KEY,
name VARCHAR(45) NOT NULL
);
CREATE TABLE user (
id INT NOT NULL PRIMARY KEY IDENTITY,
login VARCHAR(45) NOT NULL,
user_name VARCHAR(45) NOT NULL,
role_id INT NOT NULL,
FOREIGN KEY (role_id) REFERENCES role (id)
);
Entities:
#Entity
#Table(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id",insertable = false, updatable = false)
private long id;
#Column(name = "login")
private String login;
#Column(name = "user_name")
private String userName;
#ManyToOne(fetch = FetchType.LAZY)
private Role role;
//getters and setters
}
and
#Entity
#Table(name = "role")
public class Role {
#Id
#Column(name = "id")
private long id;
#Column(name = "name", length = 45)
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "role")
private Set<User> user = new HashSet<>();
//getters and setters
}
Repository
public interface UserRepository extends JpaRepository<User, Long> {
String Q_GET_ALL_USERS = "select u from User u left join u.role";
#Query(Q_GET_ALL_USERS)
Collection<User> getAllUsers();
}
#v-ladynev proposed alternative decision(use only #ManyToMany in User). More details you can find in comments under this answer.
When I check this decision I will update this answer (I hope I don't forget it :-))
Models
#Entity
#Table(name = "sys_std_user")
public class StdUser {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "class_id")
public int classId;
#Column(name = "user_name")
public String userName;
}
#Entity
#Table(name = "sys_std_profile")
public class StdProfile {
#Id
#Column(name = "pro_id")
public int proId;
#Column(name = "full_name")
public String fullName;
}
Controllers
#PersistenceUnit
private EntityManagerFactory emf;
#GetMapping("/join")
public List actionJoinTable() {
EntityManager em = emf.createEntityManager();
List arr_cust = em
.createQuery("SELECT u.classId, u.userName, p.fullName FROM StdUser u, StdProfile p WHERE u.classId=p.proId")
.getResultList();
return arr_cust;
}
Result:
[
[
1,
"Ram",
"Ram Pukar Chaudhary"
],
[
2,
"Raja",
"Raja Kishor Shah"
]
]
I am trying to use an #JoinColumn as an #Id using JPA and I am getting SerializationExceptions, "Could not serialize."
UserRole.java:
#Entity
#Table(name = "authorities")
public class UserRole implements Serializable {
#Column(name = "authority")
private String role;
#Id
#ManyToOne
#JoinColumn(name = "username")
private User owner;
...
}
User.java:
#Entity
#Table(name = "users")
public class User implements Serializable {
#Id
#GeneratedValue
protected Long id;
#Column(name = "username")
protected String email;
#OneToMany(mappedBy = "owner", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
protected Set<UserRole> roles = new HashSet<UserRole>();
....
}
"username" is set up as a unique index in my Users table but not as the primary key.
Is there any way to make "username" act as the ID for UserRole? I don't want to introduce a numeric key in UserRole. Have I totally lost the plot here?
I am using MySQL and Hibernate under the hood.
That mapping doesn't really make sense. ID has to be unique, but ManyToOne says 'lots of these have the same User.'