Foreign Key in One to One RelationShip Hibernate - java

I have an one to one relationship between the following 2 entities:
#Entity
#Table(name = "user")
public class User {
#Id
#Column(name="id")
private String id;
#Column
private String name;
#Column
private String email;
#Column
private String password;
#OneToOne(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
#JoinColumn(name = "user_role_id", referencedColumnName = "id")
private UserRole userRole;
#Entity
#Table(name = "userRole")
public class UserRole {
#Id
#Column(name="id")
private String id;
#Column
private String description;
#OneToOne(mappedBy = "userRole")
private User user;
public UserRole() {
}
I also use EntityManagerFactory to create the tables in my local DB. I received this code and I have to follow it.
public class UserRepo {
private EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("ro.tutorial.lab.SD");
public void insertNewUser(User user) {
EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
em.persist(user);
em.getTransaction().commit();
em.close();
}
There is a similar UserRoleRepo too.
My problem is when instantiating in main, I don't know how to get just the UserRole id for the FK in User. Instead, I get the whole instance of userRole and the error "Duplicate entry 'b36fcb4c-3904-4205-888b-9792f24d8b5c' for key 'userrole.PRIMARY' ".
public static void main(String[] args) {
UserRoleRepo userRoleRepo= new UserRoleRepo();
UserRole userRole1 = new UserRole();
userRole1.setId(UUID.randomUUID().toString());
System.out.println(userRole1);
userRole1.setDescription("admin");
userRoleRepo.insertNewUser(userRole1);
UserRole userRole2 = new UserRole();
userRole2.setId(UUID.randomUUID().toString());
System.out.println(userRole2);
userRole2.setDescription("client");
userRoleRepo.insertNewUser(userRole2);
UserRepo userRepo= new UserRepo();
User user = new User();
user.setId(UUID.randomUUID().toString());
user.setName("Todoran");
user.setEmail("todoran#utcluj.ro");
user.setPassword("mona");
user.setUserRole(userRole1); //////////it breaks here :(((((
System.out.println(user);
userRepo.insertNewUser(user);
}

It seemed that I got confused and the proper relationship is one to many between UserRole and User tables. Now it works fine.

Related

How to access data in bidirectional many to many mapping

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

Hibernate user_roles table will not update when using ManyToOne relationship

I have 3 tables, User, Role, and User_role. User has a OneToMany relationship mapped by "user" with a CascadeType.Merge and user_role has 2 ManyToOne Relationships with cascadeTypes.All however the user_table never populates with data when running hibernate. Instead values are only populated in the user and role tables, but never the user_role table.
User Table Definition
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="Id", nullable=false, updatable = false)
private Long id;
private String username;
private String password;
private String firstName;
private String lastName;
private String email;
private String phone;
private boolean enabled = true;
#OneToMany(mappedBy = "user", cascade=CascadeType.MERGE, fetch =
FetchType.EAGER)
#JsonIgnore
private Set<UserRole> userRoles = new HashSet<>();
UserRole Table:
#Entity
#Table(name="user_role")
public class UserRole implements Serializable {
private static final long serialVersionUID = 890345L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long userRoleId;
#ManyToOne(fetch = FetchType.EAGER, cascade=CascadeType.ALL,
optional=false)
#JoinColumn(name = "user_id")
private User user;
#ManyToOne(fetch = FetchType.EAGER, cascade=CascadeType.ALL,
optional=false)
#JoinColumn(name = "role_id")
private Role role;
public UserRole () {}
public UserRole (User user, Role role) {
this.user = user;
this.role = role;
}
public long getUserRoleId() {
return userRoleId;
}
public void setUserRoleId(long userRoleId) {
this.userRoleId = userRoleId;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
}
Call To userRepository.save() in userServiceImpl that is called from a commandLine Runner.
#Service
public class UserServiceImpl implements UserService {
private static final Logger LOG = LoggerFactory.getLogger(UserSecurityService.class);
#Autowired
private UserRepository userRepository;
#Autowired
private RoleRepository roleRepository;
// Indicates a Database Transaction
#Transactional
public User createUser(User user, Set<UserRole> userRoles) {
User localUser = userRepository.findByUsername(user.getUsername());
if(localUser != null) {
LOG.info("User with username {} already exist. Nothing will be done. ", user.getUsername());
} else {
for (UserRole ur : userRoles) {
roleRepository.save(ur.getRole());
}
Set<UserRole> currentRoles =user.getUserRoles();
currentRoles.addAll(userRoles);
user.setUserRoles(currentRoles);
localUser = userRepository.save(user);
}
return localUser;
}
}
Main Class Run()
public void run(String... args) throws Exception {
User user1 = new User();
user1.setFirstName("John");
user1.setLastName("Adams");
user1.setUsername("j");
user1.setPassword(SecurityUtility.passwordEncoder().encode("p"));
user1.setEmail("JAdams#gmail.com");
Set<UserRole> userRoles = new HashSet<>();
Role role1 = new Role();
role1.setRoleId(1);
role1.setName("ROLE_USER");
userRoles.add(new UserRole(user1, role1));
userService.createUser(user1, userRoles);
}
CascadeType.MERGE will only cascade merge events. Persist events wont cascade so if you try to save a new user, the persist event will not cascade to User_role and no entries will be inserted into the user_role table.
Try to add the CascadeType.PERSIST or change to CascadeType.ALL in order to cascade save the record in the database.
#OneToMany(mappedBy = "user", cascade={CascadeType.MERGE, CascadeType.PERSIST}, fetch = FetchType.EAGER)
You can find out more about Cascade events in this answer: What do REFRESH and MERGE mean in terms of databases?
I was able to persist the data to the user_role associative table by implementing the JPA entity manager. A new EntityManager class was instantiated and the data was persisted through the javax.persistence .merge() method.

updating an entity in many-to-many relationship

I have a Spring project that uses JPA with Hibernate and MySQL database. Database includes three tables: Users, Roles, and many-to-many join table UserRoles.
Below are the corresponding classes for the tables.
ApplicationUser.java
#Entity
#Table(name = "APPLICATION_USER")
public class ApplicationUser extends ExtAuditInfo {
public static final Long SYSTEM_USERID = 1000L;
#Id
#Column(name = "APPLICATION_USER_ID")
private long applicationUserId;
#Column(name = "LOGIN_NAME")
private String loginName;
#Column(name = "LAST_NAME")
private String lastName;
#Column(name = "FIRST_NAME")
private String firstName;
#Column(name = "MIDDLE_NAME")
private String middleName;
#OneToMany(mappedBy = "id.applicationUser", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<UserRole> roles =new ArrayList<>();
//get and set methods
Role.java
#Entity
#Table(name = "ROLE")
#NamedQueries({
#NamedQuery(name = "Role.getRoleById", query = "select r from Role r where r.roleId =:roleId"))}
public class Role extends AuditInfo {
#Id
#Column(name="ROLE_ID")
private long roleId;
#Column(name="ACTIVE_FLAG")
private String activeFlag;
#Column(name="ROLE_NAME")
private String roleName;
#OneToMany(mappedBy = "id.role", fetch = FetchType.LAZY)
private List<UserRole> users = new ArrayList<>();
//get and set methods
UserRole.java
#Entity
#AssociationOverrides({
#AssociationOverride(name = "id.applicationUser",
joinColumns = #JoinColumn(name = "APPLICATION_USER_ID")),
#AssociationOverride(name = "id.role",
joinColumns = #JoinColumn(name = "ROLE_ID")) })
#Table(name = "USER_ROLE")
public class UserRole extends ExtAuditInfo implements Serializable{
#EmbeddedId
private UserRoleID id = new UserRoleID();
#Column(name="USER_ROLE_VER")
private long userRoleVer;
public UserRole(){
}
#Transient
public ApplicationUser getApplicationUser() {
return this.id.getApplicationUser();
}
public void setApplicationUser(ApplicationUser applicationUser) {
this.id.setApplicationUser(applicationUser);
}
public long getUserRoleVer() {
return this.userRoleVer;
}
public void setUserRoleVer(long userRoleVer) {
this.userRoleVer = userRoleVer;
}
#Transient
public Role getRole() { return this.id.getRole(); }
public void setRole(Role role) { this.id.setRole(role); }
}
UserRoleID.java
#Embeddable
public class UserRoleID implements Serializable {
#ManyToOne(cascade = CascadeType.ALL)
private ApplicationUser applicationUser;
#ManyToOne(cascade = CascadeType.ALL)
private Role role;
private static final long serialVersionUID = 1L;
public UserRoleID() {
}
public ApplicationUser getApplicationUser() {
return this.applicationUser;
}
public void setApplicationUser(ApplicationUser applicationUser) {
this.applicationUser = applicationUser;
}
public Role getRole() {
return this.role;
}
public void setRole(Role role) {
this.role = role;
}
}
Now, when I create a sample user with viewer role, the record is being inserted into the Application_user and User_Role tables, but when I try to update the role of the user it is adding a new role to the user instead of updating the existing role.
This is what I'm doing
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public void updateRole(ApplicationUser appUser, long roleId){
EntityManager em=getEntityManager();
TypedQuery<Role> query = em.createNamedQuery("Role.getRoleById", Role.class);
query.setParameter("roleId",roleId);
Role r = query.getSingleResult();
UserRole userRole= appUser.getRole().get(0);
userRole.setRole(r);
em.merge(userRole);
em.flush();
em.detach(userRole);
}
Any idea, what to do to update the existing role instead of creating a new role in user_role table?
You are assigning new role to user, so a new record is added in user_role table, and old user_role entry is deleted. That's the right behavior.
So it's not you called "update the role of user".
Update:
You should delete role manually when many-to-many relationship.
appUser.getRoles().remove(userRole);
em.remove(userRole);
UserRole newUserRole = new UserRole();
newUserRole.setRole(r);
appUser.getRoles().add(newUserRole);

many-to-many-relationship between two entities in spring boot

I have two Entities in my Spring-Boot Application:
User.java
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
Long id;
String firstname;
String lastname;
String username;
String password;
}
and
Role.java
Entity
#Table(name = "role")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
Long id;
String name;
String description;
}
for my MySql database
I have excluded the getter and setter methods for this question.
I want to realise a Many-to-Many-Relationship between both Entities. Every user should be able to assign multiple roles to himself
I already Created a mapping table for both tables in my database. It has the rows
user_id
role_id.
I also created a new Entity UserRole.java which looks like this:
#Entity
#Table(name = "user_role")
public class UserRole implements Serializable{
private User user;
private Role role;
#Id
#ManyToOne
#JoinColumn(name = "user_id")
public User getuser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
#Id
#ManyToOne
#JoinColumn(name = "role_id")
public Role getrole(){
return role;
}
public void setRole(Role role){
this.role = role;
}
}
Now my question: is this construction correct? If yes, how do i add existing roles to an existing user and get the roles of this user in spring-boot?
You can find any tutorial connected with many-to-many relationship using Hibernate/Spring Data, example:
Spring Data many-to-many
With your model it's simple to add the relationship mappings, like this:
#Entity
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String description;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable
private Set<User> users;
}
and this:
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstname;
private String lastname;
private String username;
private String password;
#ManyToMany(mappedBy = "users")
private Set<Role> roles;
}

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