tushar
tushar

Reputation: 489

Child table is not mapping in OneToMany relationship in JPA

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

Answers (2)

junbetterway
junbetterway

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

Davide D&#39;Alto
Davide D&#39;Alto

Reputation: 8206

You need to flush the changes to the database when using save(), try this instead:

userRepo.saveAndFlush(user);

Upvotes: 0

Related Questions