Reputation: 133
I am implementing a functionality to add users along with one or more existing roles in the database. The Roles are added successfully from angular front end. When I create a new User in the angular front end and add more than one role to this user, I get the Object in the spring boot back end, however it throws an exception related to the jpa save method in the userServiceImpl class.
org.hibernate.PersistentObjectException: detached entity passed to persist: com.app.model.Role
[
{
"roleId": 1,
"name": "USER",
"description": "Role assigned to regular users who perform regular tasks within the application, such as add or update transactions and companies"
},
{
"roleId": 2,
"name": "ADMIN",
"description": "Role assigned to users who can create other users, roles and perform regular users tasks within the Investment application"
}
]
In the UI (angular front end) I need to create new users and assign them one or more existing roles. I can see from the console that the roles I added are there, however I am not receiving the roles in spring boot backend, only the regular user info such as name, last name, email, password but not the roles.
Here's my User component where I create the users.
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { Role } from 'src/app/model/Role';
import { User } from 'src/app/model/User';
import { RoleServiceService } from 'src/app/services/role-service.service';
import { UserServiceService } from 'src/app/services/user-service.service';
@Component({
selector: 'app-user',
templateUrl: './user.component.html',
styleUrls: ['./user.component.css']
})
export class UserComponent implements OnInit {
user!: User;
createForm!:FormGroup;
errorMessage!:string;
role: Role[]= [];
constructor(private userService: UserServiceService,
private router: Router,
private roleService: RoleServiceService) { }
ngOnInit(): void {
this.getAllRoles();
this.createForm = new FormGroup({
'userId': new FormControl(),
'firstName': new FormControl(null, Validators.required),
'lastName': new FormControl(null, Validators.required),
'email': new FormControl(null, Validators.required),
'password': new FormControl(null, Validators.required),
'roles': new FormControl()
});
}
onSubmit(){
this.user = new User(
this.createForm.get('userId')?.value,
this.createForm.get('firstName')?.value,
this.createForm.get('lastName')?.value,
this.createForm.get('email')?.value,
this.createForm.get('password')?.value,
this.createForm.get('roles')?.value
)
console.log(this.user);
this.userService.addUser(this.user).subscribe(
data=>{
this.router.navigate([""]);
}, err=>{
this.errorMessage = "Unable to add user!";
console.log(err);
}
)
}
getAllRoles(){
this.roleService.getAllRoles().subscribe(
data=>{
this.role = data;
console.log(data);
}
)
}
}
In the back end, here's my User service where I add the users. This is working partially as I am not getting the roles from angular.
@Override
public User addUser(User newUser) throws Exception {
List<User> users = userRepo.findAll();
for(User user: users) {
if(user.getEmail().equalsIgnoreCase(newUser.getEmail())) {
throw new Exception("User already exists");
}
}
Set<Role> roles = newUser.getRoles();
for (Role role : roles) {
newUser.addRole(role);
}
return userRepo.save(newUser);
}
My mapping method in the controller is this one:
@PostMapping("/register")
public ResponseEntity<?> addUser(@RequestBody User user) throws Exception{
System.out.println(user);
return new ResponseEntity<>(userService.addUser(user), HttpStatus.CREATED);
}
The User entity in java:
import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import javax.persistence.Transient;
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false, updatable = false)
private long userId;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
@Column(name = "email")
private String email;
@Column(name = "password")
private String password;
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinTable(name="user_roles", joinColumns = @JoinColumn(name="user_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name="role_id", referencedColumnName = "id") )
private Set<Role> roles = new HashSet<>();
public void addRole(Role role) {
this.roles.add(role);
}
public User() {
}
public User(String firstName, String lastName, String email, String password, Set<Role> roles) {
super();
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.password = password;
this.roles = roles;
}
public long getUserId() {
return userId;
}
public void setUserId(long userId) {
this.userId = userId;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
@Override
public String toString() {
return "User [userId=" + userId + ", firstName=" + firstName + ", lastName=" + lastName + ", email=" + email
+ ", password=" + password + ", roles=" + roles + "]";
}
}
The model in Angular Front end:
import { Role } from "./Role";
export class User{
constructor(public userId: number,
public firstName: string,
public lastName: string,
public email: string,
public password:string,
public roles: Role){
}
}
The Role entity is the following in Java:
@Entity
@Table(name = "role")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false, updatable = false)
private Integer roleId;
@Column(name = "role_name")
private String name;
@Column(name = "description")
private String description;
public Role() {
}
public Role(String name, String description) {
super();
this.name = name;
this.description = description;
}
public int getRoleId() {
return roleId;
}
public void setRoleId(int roleId) {
this.roleId = roleId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
The Role model in Angular front end is:
export class Role{
constructor(public roleId: number,
public name: string,
public description:string){
}
}
I am certain it has be related to how I add the roles in the reactive form as it is an array of roles but I am not sure how to approach that.
Upvotes: 0
Views: 640
Reputation: 133
Thanks for the help on some key issues in my code. I was able to fix it once I understood the following:
Issues:
User Object keys name for Java and Angular. One of the instance variables name was "roles" with s in my User entity in Java, and in angular role entity that same field was called role without the s.
In the spring boot back end (Java). In the user entity I added an addRole method and I was using it inside my userServiceImpl service which was causing the detached entity passed to persist error/exception. To fix this, all I did was simply remove the addRole method from my service and now I am able to create users and add any role I want or multiple roles and all is saved correctly in the database.
The service now looks as follows:
@Override
public User addUser(User newUser) throws Exception {
List<User> users = userRepo.findAll();
for(User user: users) {
if(user.getEmail().equalsIgnoreCase(newUser.getEmail())) {
throw new Exception("User already exists");
}
}
return userRepo.save(newUser);
}
Upvotes: 1