How to access data in bidirectional many to many mapping - java

UserDetail.java (model class)
#Entity
public class UserDetail {
#Id
private String email;
private String name;
private String password;
#ManyToMany(cascade = CascadeType.ALL)
private List<Role> role;
#ManyToMany(cascade = CascadeType.ALL,mappedBy = "user")
private List<GroupDetail> group;
}
GroupDetail.java ( model class)
#Entity
public class GroupDetail {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column(unique = true)
private String groupName;
#ManyToMany(cascade = CascadeType.ALL)
private List<UserDetail> user;
#ManyToMany(cascade = CascadeType.ALL)
private List<Role> role;
}
Here as you can see GroupDetail is the owner of the association and Hibernate will only check that side when maintaining the association.
So, how do i get List of GroupDetail using the user email?

Following should work for you:
select userDtl.group from UserDetail userDtl where userDtl.email = 'Your Email'
If this does not work, then you could try to use join explicitly like this:
select g from UserDetail u join u.group g where u.email = 'Your Email'

Do this, add getter and setter in both entities.
UserDetails user = em.find(UserDetails.class, emailId);
List<GroupDetail> group = user.getGroup();

Related

Hibernate One to One, not generating Foreign key

I have 4 tables here: Register, User, User roles and User Profile Image
Register and User are mapped my a One to One relationship and a reference of Register is generated in Users table...... This is fine..
Now talking about One to Many Relation between User and the Roles table, it also works perfectly by generating a User table reference in the roles table..
But problem is when working with One to One between User and the Profile Image. Profile Image is not generating reference of User....Why the user reference is not generated in Profile Image table
Register
#Entity
#Getter
#Setter
public class Register {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#OneToOne(cascade = CascadeType.ALL,orphanRemoval = true,mappedBy = "register")
private User user;
}
User
#Entity
#Getter
#Setter
public class User {
#Id
#Column(name = "User_Id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#OneToOne(cascade = CascadeType.ALL,fetch=FetchType.EAGER)
#JoinColumn(name = "user_id")
private UserProfileImage userProfileImage;
#OneToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn(name = "user_id")
private List<UserRoles> userRoles;
}
User Profile Image
#Entity
#Getter
#Setter
public class UserProfileImage {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "name")
private String name;
#Column(name = "type")
private String type;
#Column(name = "picByte", length = 100000)
private byte[] picByte;
public UserProfileImage() {
super();
}
public UserProfileImage(String name, String type, byte[] picByte) {
this.name = name;
this.type = type;
this.picByte = picByte;
}
}
Profile mapping in User class is not correct and in your profile class there is no user field and hence it's not generating the user reference in the profile class.
Also, User to Roles mapping is also not correct, your user class will not populate roles with your mappings.
Try this:
public class User {
...
#OneToOne(cascade = CascadeType.ALL,fetch=FetchType.EAGER)
#JoinColumn(name = "PROFILE_IMAGE_ID") // foreign key column in User table
private UserProfileImage userProfileImage;
#OneToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER,mappedBy="user")
private List<UserRoles> userRoles;
}
public class UserProfileImage {
...
#OneToOne(mappedBy="userProfileImage")
private User user;
...
}
public class UserRole {
...
#ManyToOne
#JoinColumn(name="USER_ID") // foreign key column in User Role table
private User user;
...
}

How to map a nested Collection in Hibernate projection

There are two entities:
#Data
#Entity(name = "user")
public class User {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY) private Long id;
#Column(name = "username")
private String username;
#ToString.Exclude
#EqualsAndHashCode.Exclude
#OneToMany(mappedBy = "user")
private Collection<Address> addresses;
...
}
#Getter
#Setter
#Entity(name = "address")
public class Address {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY) private Long id;
#Column(name = "address")
private String address;
#ManyToOne
#JoinColumn(name = "user_id")
private WebTutorUser user;
...
}
it is needed to create a projection like this to return a list of objects with each one has users username and a list of addresses:
public interface UserProjection {
String getUsername();
List<Address> getAddresses();
}
and call it in a repository
List<UserProjection> findBy();
but it doesn't return addresses list
Just need to create a nested projection:
public interface UserProjection {
String getUsername();
List<AddressProjection> getAddresses();
interface AddressProjection {
String getAddress();
}
}
and it is possible to call it in a repository like that:
List<UserProjection> findBy();
or
#Query("select u.username from user u left join fetch u.addresses")
List<UserProjection> findBy();

JPQL query to fetch all roles of user based on user id

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)

Copy new user with associated roles

What I want to do is get data user and copy to a new user (create a new user). This is what I'm doing:
#Entity
#Table(name = "role")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "role_id")
private int roleId;
#Column(name = "role")
private String role;
public Role() {
}
}
#Entity
#Table(name = "usuario")
public class Users {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "user_id")
private int id;
#Column(name = "email")
private String email;
#Column(name = "password", nullable=false, length=60)
private String password;
#Column(name = "name", unique=true, nullable=false, length=6)
private String name;
#Column(name = "last_name")
private String lastName;
#Column(name = "active", nullable=false)
private int active;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "user_role", joinColumns = #JoinColumn(name = "user_id"), inverseJoinColumns = #JoinColumn(name = "role_id"))
private Set<Role> roles;
public Users() {
}
}
I get data from one existing user:
Optional<Users> user = usersRepository.findByName(name);
//create a new User to persist
Users newUser = new Users();
newUser.setName("new name");
newUser.setActive(1);
newUser.setEmail(user.get().getEmail());
newUser.setLastName(user.get().getLastName());
newUser.setPassword(user.get().getPassword());
Set<Role> roles = user.get().getRoles();
newUser.setRoles(roles);
usersRepository.save(newUser);
I get this message:
Found shared references to a collection: model.authentication.Users.roles;
nested exception is org.hibernate.HibernateException: Found shared references to a collection: model.authentication.Users.roles
UPDATE 1 (SOLVED)
Optional<Users> user = usersRepository.findByName(codalojamiento);
Users newUser = new Users();
newUser.setName("new name");
newUser.setActive(1);
newUser.setEmail(user.get().getEmail());
newUser.setLastName(user.get().getLastName());
newUser.setPassword(user.get().getPassword());
Set<Role> newRoles = new HashSet();
Set<Role> roles = user.get().getRoles();
for (Role r : roles) {
newRoles.add(r);
}
newUser.setRoles(newRoles);
usersRepository.save(newUser);
Any suggestion?
Thanks
There are multiple things that could go wrong:
Since you are linking a user to multiple roles, but at the same time, multiple users can have the same role, the relation should be #ManyToMany, not #OneToMany.
Secondly, if you insist on having a #OneToMany relationship, you are linking the new user to the roles of the existing user, so a solution might be to create new roles for that user that are identical to the roles of the first user
Personally, i would suggest using a #ManyToMany, and that should fix the problem
In JPA Find method also flushes the data. Here when you have retreived the user using find method it's moved to managed state.
Then you have taken the roles collection from user object and assigned it to newUser. Now basically you have two entities have same collection references which is not allowed.
Either you can detach user from persistence context before saving newUser or clone the roles collection before adding it to newUser.

JPA join in spring boot application

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"
]
]

Categories

Resources