Reputation: 489
I am trying to establish One to many relationship between User and Role.
One user can have many role.
Here is the code for User class
@Entity
public class User {
@Id
private int id;
private String name;
private String password;
private String email;
private String phoneNo;
@OneToMany(
targetEntity = Role.class,
mappedBy = "user",
cascade = CascadeType.ALL,
fetch = FetchType.LAZY
)
private Set<Role> roles;
// Getters, setters and Constructor
Code for the Role class
@Entity
public class Role {
@Id
@GeneratedValue
private int roleId;
private String role;
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
// Getters, setters and Constructor
POST request on Postman is
{
"id":101,
"name": "rahul",
"password": "456",
"email": "[email protected]",
"phoneNo": "1234561234",
"role": [{
"role":"USER"
}]
}
Code on Configuration part
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http)throws Exception
{
http.csrf().disable();
}
@Bean
public BCryptPasswordEncoder passwordEncoder()
{
return new BCryptPasswordEncoder();
}
}
Code On Controller part
@RestController
public class AdminController {
@Autowired
UserRepository userRepo;
@Autowired
BCryptPasswordEncoder encryptPassword;
@PostMapping("/admin/add")
public String addUserByAdmin(@RequestBody User user)
{
String pass = user.getPassword();
String encrypt = encryptPassword.encode(pass);
user.setPassword(encrypt);
userRepo.save(user);
return "User added Successfully";
}
}
Role table connection to database through Jpa
public interface RoleRepository extends JpaRepository<Role, Integer> {
}
User table connection to database through Jpa
public interface UserRepository extends JpaRepository<User,Integer>{
}
Here problem is User table is mapped properly but Role table is not getting mapped.
roleId role user_id
NULL NULL NULL
Where I am wrong ? Could anyone help me ?
Upvotes: 0
Views: 1410
Reputation: 298
On your controller method below, have you tried debugging the incoming request User object?
Anyways, I have below points here:
First, looking into your request body, the field for your roles is named role
while your User object has a field roles
thus, I'm pretty sure it is null
during your processing since it will not be deserialized there due to mismatch field names. Try changing your request body to something like this:
{
"id":101,
"name": "rahul",
"password": "456",
"email": "[email protected]",
"phoneNo": "1234561234",
"roles": [{
"role":"USER"
}]
}
Second, if you check your database, the roles will be persisted however your foreign key user_id
is null. This is expected. The cascade you did on the User object will only means that (since you use CascadeType.ALL) once you save the User object the save operation will also be cascaded to the Role object however, JPA still needs to know the relationship thus you have to set the user for each role object. Hence, you can update your controller method to something below:
@PostMapping("/admin/add")
public String addUserByAdmin(@RequestBody User user)
{
String pass = user.getPassword();
String encrypt = encryptPassword.encode(pass);
user.setPassword(encrypt);
// JPA needs to know this relationship...
user.getRoles().forEach(role -> role.setUser(user));
userRepo.save(user);
return "User added Successfully";
}
Now you can try and see that your expected behavior should now be happening.
Additional recommendations:
Why are we passing ID field on the user request? You can just remove that from your request body and use below to auto-generate your IDs to avoid Unique index or primary key violation exceptions on all of your entities:
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
You can also remove the targetEntity = Role.class
on the mapping as it is only used for generics and for your case clearly you are not using generics for Set. Update your User object for roles mapping:
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Role> roles;
Lastly, it is better if you can wrap your incoming payload to a DTO since you would not want to expose your entity/model to your API but I am thinking this is just for your test environment.
Upvotes: 1
Reputation: 8206
You need to flush the changes to the database when using save()
, try this instead:
userRepo.saveAndFlush(user);
Upvotes: 0