Spring JPA-I get error that object is not persisted even though i have persisted it - java

I am using Spring boot-I have 3 classes User,Role and UserRole.I have pesisted both role object and user object but i get error that role object is not persisted.The mappings- between User and UserRole is OneToMany ,between Role and UserRole OneToMany.In the UserServiceImpl class i have persisted Role object roleRepository.save(userRole.getRole());
Error is-
Caused by: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.bookstore.domain.security.UserRole.role -> com.bookstore.domain.security.Role
#Entity
public class User implements UserDetails,Serializable {
private static final long serialVersionUID=157954L;
#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;
#OneToMany(mappedBy = "user",cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JsonIgnore
private Set<UserRole> userRoles=new HashSet<UserRole>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
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 getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public Set<UserRole> getUserRoles() {
return userRoles;
}
public void setUserRoles(Set<UserRole> userRoles) {
this.userRoles = userRoles;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Set<GrantedAuthority> authorities=new HashSet<GrantedAuthority>();
userRoles.forEach(userRole->{
authorities.add(new Authority(userRole.getRole().getRoleName()));
});
return authorities;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return enabled;
}
#Override
public String getUsername() {
return userName;
}
}
#Entity
public class Role implements Serializable{
private static final long serialVersionUID=68678L;
#Id
private Long roleId;
private String roleName;
#OneToMany(mappedBy = "role",cascade = CascadeType.ALL,fetch = FetchType.LAZY)
#JsonIgnore
private Set<UserRole> userRoles=new HashSet<UserRole>();
public Role() {
}
public Long getRoleId() {
return roleId;
}
public void setRoleId(Long roleId) {
this.roleId = roleId;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public Set<UserRole> getUserRoles() {
return userRoles;
}
public void setUserRoles(Set<UserRole> userRoles) {
this.userRoles = userRoles;
}
}
#Entity
public class UserRole implements Serializable {
private static final long serialVersionUID=456874L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long userRoleId;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "roleId")
private Role role;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "userId")
private User user;
public UserRole(User user,Role role) {
this.role = role;
this.user = user;
}
public UserRole() {
super();
}
public Long getUserRoleId() {
return userRoleId;
}
public void setUserRoleId(Long userRoleId) {
this.userRoleId = userRoleId;
}
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
#Service
public class UserServiceImpl implements UserService {
private static final Logger LOG=LoggerFactory.getLogger(UserServiceImpl.class);
#Autowired
UserRepository userRepository;
#Autowired
RoleRepository roleRepository;
#Transactional
#Override
public User CreateUser(User user, Set<UserRole> userRoles) {
User localUser=userRepository.findByUserName(user.getUserName());
if(localUser!=null) {
LOG.warn("Username {} already exists",user.getUserName());
}
else {
for(UserRole userRole:userRoles) {
roleRepository.save(userRole.getRole());
LOG.error("inside for {}",userRole.getRole().getRoleName());
}
user.getUserRoles().addAll(userRoles);
localUser=userRepository.save(user);
}
return localUser;
}
}
#SpringBootApplication
public class BookStoreApplication implements CommandLineRunner {
#Autowired
UserService userService;
public static void main(String[] args) {
SpringApplication.run(BookStoreApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
User user1=new User();
user1.setUserName("test");
user1.setPassword(SecurityUtility.passwordEncoder().encode("test"));
user1.setEmail("test#test.com");
user1.setEnabled(true);
user1.setFirstName("testFirstName");
user1.setLastName("testLastName");
user1.setPhone("123456789");
Role role1=new Role();
role1.setRoleId((long)1);
role1.setRoleName("ROLE_USER");
UserRole userRole1=new
UserRole(user1,role1);
Set<UserRole> userRoles1=new HashSet<UserRole>();
userRoles1.add(userRole1);
userService.CreateUser(user1, userRoles1);
User user2=new User();
user2.setUserName("admin");
user2.setPassword(SecurityUtility.passwordEncoder().encode("admin"));
user2.setEmail("admin#admin.com");
user2.setEnabled(true);
user2.setFirstName("adminFirstName");
user2.setLastName("adminLastName");
user2.setPhone("223456789");
Role role2=new Role();
role2.setRoleId((long) 2);
role2.setRoleName("ROLE_ADMIN");
UserRole userRole2=new UserRole(user2,role2);
Set<UserRole> userRoles2=new HashSet<UserRole>();
userRoles2.add(userRole2);
userService.CreateUser(user2, userRoles2);
}
}

Couple of issues here.
The first (and the question) issue and the reason why you are getting a "Transient state error" is because you are trying to save an entity with entities attached to it that are NOT yet managed by hibernate.
Have a read of: Entity Lifecycle Model in Hibernate
Caused by: org.hibernate.TransientPropertyValueException:
object references an unsaved transient instance -
save the transient instance before flushing :
com.bookstore.domain.security.UserRole.role
->
com.bookstore.domain.security.Role
So you are somewhere trying to save a UserRole with a Role that is not yet managed.
When you call new on an entity, it is in a Transient state. Hibernate doesn't know how to handle it. It doesn't have a database ID and is not part of the context for hibernate to manage (make the relevent queries etc).
To make an entity managed you need to save it via the repo.
I.e. roleRepo.save(role)
You will then notice it then has an Id and is now managed by hibernate.
#Service
public class UserServiceImpl implements UserService {
#Transactional
#Override
public User CreateUser(User user, Set<UserRole> userRoles) {
User localUser = userRepository.findByUserName(user.getUserName());
if (localUser != null) {
LOG.warn("Username {} already exists", user.getUserName());
} else {
// For each transient UserRole passed in, save the Role.
// Role is now managed.
for (UserRole userRole : userRoles) {
roleRepository.save(userRole.getRole());
LOG.error("inside for {}", userRole.getRole().getRoleName());
}
user.getUserRoles().addAll(userRoles);
localUser = userRepository.save(user);
}
return localUser;
}
}
This service above doesn't maybe do what you expect.
You are getting the Roles and saving them.
You then don't replace the Role in UserRole with the managed one back from the repo.
Maybe this would work?
for(UserRole userRole:userRoles) {
//The role is now managed.
Role managedRole = roleRepository.save(userRole.getRole());
//Replace the transient role in the UserRole with a managed Role.
userRole.setRole(managedRole);
}
So when it goes on to save the User:
user.getUserRoles().addAll(userRoles);
localUser = userRepository.save(user);
The UserRoles (which are still transient) have a managed Role at least.
The Cascade.ALL should do what you expect (I am unsure mind!) and save the transient UserRoles because Cascade.ALL will save the children UserRoles.
https://www.baeldung.com/jpa-cascade-types
=============================
The second issue, not causing the problem in question, but you may want to go have a think about:
At the moment you have:
User 1 : M UserRole
UserRole M : 1 Role
1 User has many UserRoles.
1 Role has many UserRoles.
The modelling here just smells off.
Usually you'd have some Role entities/database entries that can be related to a user via ManyToMany relationship.
User signs up, is given the "USER" role in their Set<Role> userRoles rather than creating a new Role for each user with "USER" as a field.
So a user has a relationship to a role via a "join table" UserRole.
Spring can already create a join table for you. You do not really need the UserRole entity in your code as it stands as it just holds a FK_User and FK_Role.
Basically, you want:
User M:M Role
I.e. a user can have many roles.
Simply use the #ManyToMany annotation for a Many:Many relationship between user and roles.
To add a role you search the database for
Role findByRoleName
And add that managed entity to the user's roles and then persist the user.
ManyToMany Baeldung

Related

How to auto insert foreign key when insert entity/object by Spring Boot JPA to MySQL

I tried to insert an entity that contains foreign key from Spring boot JPA to MYSQL. But get error:
java.sql.SQLIntegrityConstraintViolationException: Column 'role_role_id' cannot be null.
I know it is an error when I insert null into a column that does not allow null. I know the fix(please see myMainController below) is get object Role (foreignkey) to set into my User object then insert User Object to DB (new row in DB) (the necessary data of Role is already in the DB). But I want when I insert User, it will auto insert Object Role because I have set the realtion clearly(more info please see my pictures in User Entity and Role Entity). Please see my Code.
This is ERR:
java.sql.SQLIntegrityConstraintViolationException: Column 'role_role_id' cannot be null.
This is myMainRestController
#RestController
public class MyMainController {
#Autowired
private UserRepository service;
#RequestMapping("/i")
#ResponseBody
public String Welcome() {
return "Welcome!";
}
#JsonIgnore
#RequestMapping(value = "/", //
method = RequestMethod.GET, //
produces = { MediaType.APPLICATION_JSON_VALUE })
#ResponseBody
public User initData() {
User user = new User();
user.setUserId(4);
user.setPassword("1234");
user.setFullname("Nguyễn Văn D");
user.setPhonenumber("012345678");
user.setAddress("2 đường Số 7 Bình Trị Dông");
user.setFontIdCardImg("p2fontid.jpg");
user.setBackIdCardImg("p2backid.jpg");
user.setRoleId(2);
user.setAreaId(2);
// Role role = new Role();
// role.setRoleId(user.getRoleId()); / My fix here
// user.setRole(role);
service.save(user);
return user;
}
}
This is my User Entity:
#Entity
#Table(name="user")
#NamedQuery(name="User.findAll", query="SELECT u FROM User u")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="user_id", unique=true, nullable=false)
private Integer userId;
#Column(length=1000)
private String address;
#Column(name="area_id")
private Integer areaId;
#Column(name="back_id_card_img", length=200)
private String backIdCardImg;
#Column(name="font_id_card_img", length=200)
private String fontIdCardImg;
#Column(length=45)
private String fullname;
#Column(length=45)
private String password;
#Column(length=45)
private String phonenumber;
#Column(name="role_id")
private Integer roleId;
//bi-directional many-to-one association to Accident
#OneToMany(mappedBy="user")
private List<Accident> accidents;
//bi-directional many-to-one association to Area
#ManyToOne
#JoinColumn(name="area_area_id")
private Area area;
//bi-directional many-to-one association to Policeworktime
#ManyToOne
#JoinColumn(name="policeworktime_police_user_id")
private Policeworktime policeworktime;
//bi-directional many-to-one association to Role
#ManyToOne(cascade={CascadeType.ALL})
#JoinColumn(name="role_role_id", nullable=false)
private Role role;
public User() {
}
public Integer getUserId() {
return this.userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getAddress() {
return this.address;
}
public void setAddress(String address) {
this.address = address;
}
public Integer getAreaId() {
return this.areaId;
}
public void setAreaId(Integer areaId) {
this.areaId = areaId;
}
public String getBackIdCardImg() {
return this.backIdCardImg;
}
public void setBackIdCardImg(String backIdCardImg) {
this.backIdCardImg = backIdCardImg;
}
public String getFontIdCardImg() {
return this.fontIdCardImg;
}
public void setFontIdCardImg(String fontIdCardImg) {
this.fontIdCardImg = fontIdCardImg;
}
public String getFullname() {
return this.fullname;
}
public void setFullname(String fullname) {
this.fullname = fullname;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPhonenumber() {
return this.phonenumber;
}
public void setPhonenumber(String phonenumber) {
this.phonenumber = phonenumber;
}
public Integer getRoleId() {
return this.roleId;
}
public void setRoleId(Integer roleId) {
this.roleId = roleId;
}
public List<Accident> getAccidents() {
return this.accidents;
}
public void setAccidents(List<Accident> accidents) {
this.accidents = accidents;
}
public Accident addAccident(Accident accident) {
getAccidents().add(accident);
accident.setUser(this);
return accident;
}
public Accident removeAccident(Accident accident) {
getAccidents().remove(accident);
accident.setUser(null);
return accident;
}
public Area getArea() {
return this.area;
}
public void setArea(Area area) {
this.area = area;
}
public Policeworktime getPoliceworktime() {
return this.policeworktime;
}
public void setPoliceworktime(Policeworktime policeworktime) {
this.policeworktime = policeworktime;
}
public Role getRole() {
return this.role;
}
public void setRole(Role role) {
this.role = role;
}
}
This is my Role Entity:
#Entity
#Table(name="role")
#NamedQuery(name="Role.findAll", query="SELECT r FROM Role r")
public class Role implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="role_id", unique=true, nullable=false)
private Integer roleId;
#Column(name="role_name", length=45)
private String roleName;
//bi-directional many-to-one association to User
#OneToMany(mappedBy="role", cascade={CascadeType.ALL})
private List<User> users;
public Role() {
}
public Integer getRoleId() {
return this.roleId;
}
public void setRoleId(Integer roleId) {
this.roleId = roleId;
}
public String getRoleName() {
return this.roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public List<User> getUsers() {
return this.users;
}
public void setUsers(List<User> users) {
this.users = users;
}
public User addUser(User user) {
getUsers().add(user);
user.setRole(this);
return user;
}
public User removeUser(User user) {
getUsers().remove(user);
user.setRole(null);
return user;
}
}
You don't need to create Colunm roleId. You just put relational with tables. For example in my situation, I have OneToMany User with Role. I only established relational in my MySql DB. And then, you can create your own entities or use the JPA tool at your disposal. User entity has a object Role, Role entity has a List.
#Entity
#Table(name="role")
#NamedQuery(name="Role.findAll", query="SELECT r FROM Role r")
public class Role implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="role_id", unique=true, nullable=false)
private Integer roleId;
#Column(name="role_name", length=45)
private String roleName;
//bi-directional many-to-one association to User
#OneToMany(mappedBy="role")
private List<User> users;
public Role() {
}
public Integer getRoleId() {
return this.roleId;
}
public void setRoleId(Integer roleId) {
this.roleId = roleId;
}
public String getRoleName() {
return this.roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public List<User> getUsers() {
return this.users;
}
public void setUsers(List<User> users) {
this.users = users;
}
public User addUser(User user) {
getUsers().add(user);
user.setRole(this);
return user;
}
public User removeUser(User user) {
getUsers().remove(user);
user.setRole(null);
return user;
}
}
#Entity
#Table(name = "user")
#NamedQuery(name = "User.findAll", query = "SELECT u FROM User u")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
// bi-directional many-to-one association to Role
#ManyToOne
#JoinColumn(name = "role_role_id", nullable = false)
private Role role;
public Role getRole() {
return this.role;
}
public void setRole(Role role) {
this.role = role;
}
}

Hibernate adds unnecessary row in role table after user registration

I'm trying to add a functionality to my webapp with user registration. Webapp is based on spring boot, hibernate and mysql database, frontend is in angular. Generally, the user creation procedure is working correctly, user data is correctly send from frontend to backend via json and saved to the database in shop_user table (with all the user data, such as name, surname, address etc.), but it DOESN'T have role column.
I also have table 'role', which should be:
id name
1 USER
2 ADMIN
and joined table user_role, which consists of user_id from table shop_user and role id from table role, so it should look like this:
id_user id_role
1 2
2 1
3 1
When user is being created on the website, it is hard-coded to set the role by default to USER. This seems to work quite well as it adds a new row in shop_user, and it adds a row to user_role, but... it also creates a new row in 'role' table.
so in the end 'role' table looks like this:
id name
1 ADMIN
2 USER
3 USER
4 USER
5 USER
99 USER
`
while this is not a blocking bug that stops application from working, it is not 'as it should work' unfortunately... as the table should only consist of two role rows (and possibly additional ones, in the future), but not multiplicated for each user!
here's the flawed code of user:
User
#Entity
#Table(name = "shop_user")
public class User extends AbstractEntity {
#Column
private String firstName;
#Column
private String lastName;
#Column
private String addressLine;
#Column
private String city;
#Column
private String country;
#Column
private String zipCode;
#Column
private String phoneNumber;
#Column
private String email;
#Column
private String password;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "user_role",
joinColumns = #JoinColumn(name = "id_user", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "id_role", referencedColumnName = "id"),
uniqueConstraints = {#UniqueConstraint(columnNames = {"id_user", "id_role"})})
private List<Role> roles;
public User() {
}
public User(User user) {
setId(user.getId());
this.firstName = user.getFirstName();
this.lastName = user.getLastName();
this.addressLine = user.getAddressLine();
this.city = user.getCity();
this.country = user.getCountry();
this.zipCode = user.getZipCode();
this.phoneNumber = user.getPhoneNumber();
this.email = user.getEmail();
this.password = user.getPassword();
this.roles= user.getRoles();
}
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
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 getAddressLine() {
return addressLine;
}
public void setAddressLine(String addressLine) {
this.addressLine = addressLine;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getZipCode() {
return zipCode;
}
public void setZipCode(String zipCode) {
this.zipCode = zipCode;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
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;
}
}
Role implementation:
Role
#Entity
#Table(name = "role")
public class Role extends AbstractEntity {
#Column
private String name;
#ManyToMany(mappedBy = "roles", cascade = CascadeType.PERSIST)
private List<User> users;
public Role(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
}
Abstract entity:
AbstractEntity
#MappedSuperclass
public abstract class AbstractEntity implements Persistable<Long> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
public void setId(Long id) {
this.id = id;
}
#Override
public Long getId() {
return id;
}
#Override
public boolean isNew() {
return id == null;
}
}
User service:
UserServiceImpl
#Service
public class UserServiceImpl extends AbstractServiceImpl<User, UserDTO> implements UserService {
private final UserRepository userRepository;
private final UserConverter userConverter;
public UserServiceImpl(UserRepository userRepository, UserConverter
userConverter) {
this.userRepository = userRepository;
this.userConverter = userConverter;
}
#Override
protected JpaRepository<User, Long> getRepository() {
return userRepository;
}
#Override
protected Converter<User, UserDTO> getConverter() {
return userConverter;
}
#Override
#Transactional
public User registerUser(User user) {
List<Role> roles = new LinkedList<>();
roles.add(new Role("USER"));
user.setRoles(roles);
return userRepository.save(user);
}}
I am nearly sure that this comes to the relations mapping in Hibernate and object creation, but can't quite figure it out...
Any help will be appreciated, thank you!
The issue is here:
#Override
#Transactional
public User registerUser(User user) {
List<Role> roles = new LinkedList<>();
roles.add(new Role("USER"));
user.setRoles(roles);
return userRepository.save(user);
}}
Since the relationship User -> Role is cascade persist, the (new) role new Role("USER") is also persisted and you ended up with a new Role for each user instead of reusing the existing one.
The solution is to check the existence of a Role with name = USER. If doesn't exist, insert it. Otherwise add the existent one to the roles collection.

Spring end-point always return 403 while checking role

I have integrated the role and want to manage the access of the specific service on the basis of the role.
An admin can create an AGENT and that agent came under the group of ADMIN user.
What I basically did have create a 1 to Many relation ship because my user could have only 1 role.
#Entity
#Table(name = "role")
public class Role {
private Long id;
private String name;
private Collection<User> users;
public Role() {
}
public Role(String name) {
this.name = name;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#OneToMany(mappedBy = "role", cascade = CascadeType.ALL)
public Collection<User> getUsers() {
return users;
}
public void setUsers(Collection<User> users) {
this.users = users;
}
}
And here I have the user which the group relation as well the group is also 1 TO MANY because a user_admin can have multiple agents but agent could not have multiple admins.
#Entity
#Table(name = "user")
public class User {
private long id;
private String username;
private String password;
private boolean enabled = false;
private Role role;
private UserGroup userGroup;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
public User(String username, String password, Role role) {
this.username = username;
this.password = password;
this.role = role;
}
public User(String username, String password, boolean enabled, Role role, UserGroup userGroup) {
this.username = username;
this.password = password;
this.enabled = enabled;
this.role = role;
this.userGroup = userGroup;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
#NotBlank
#Column(nullable = false, updatable = false, unique = true)
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
#NotBlank
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
#Column(nullable = false, updatable = true)
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
#ManyToOne
#JoinColumn(name = "role_id", referencedColumnName = "id")
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
#ManyToOne
#JoinColumn(name = "user_group_id", referencedColumnName = "id")
public UserGroup getUserGroup() {
return userGroup;
}
public void setUserGroup(UserGroup userGroup) {
this.userGroup = userGroup;
}
}
At the time of creation of the user I specify the role and the group as well.
And in the SecurityConfig I have configure like this.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers(Constants.BASE_URL_FILE_UPLOADER + Constants.URL_UPLOAD_FILE).hasRole("ADMIN") .anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
// this disables session creation on Spring Security
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.exceptionHandling().authenticationEntryPoint(new ContentSearcherAuthenticationEntryPoint());
}
But If I am accessing this end-point with the admin user I throws forbidden and also in the function when I access authentication.getAuthorities()it return emptyList
ResponseEntity<JsonNode> uploadFile(#RequestParam("file") MultipartFile file, Authentication authentication) {
logger.info("Authentication is [{}] and user is [{}]", authentication.getAuthorities(), authentication.getName()); // []
}
I am confused in UserDetailsService I have also added the GrantedAuthority as well like this.
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
private UserRepository userRepository;
private RoleRepository roleRepository;
public UserDetailsServiceImpl(UserRepository userRepository, RoleRepository roleRepository) {
this.userRepository = userRepository;
this.roleRepository = roleRepository;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findUserByUsername(username);
if (user == null) {
throw new UsernameNotFoundException(username + " Not Exists");
}
user.setEnabled(true);
userRepository.save(user);
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), user.isEnabled(),
true, true, true, getAuthorities(user.getRole()));
}
private Collection<? extends GrantedAuthority> getAuthorities(
Role role) {
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority(Constants.ROLE_PREFIX + role.getName()));
return authorities;
}
}
What I am missing is there any more configuration I have to add ?
I am using JWT for the authentication purpose some thing should not to be added in this as well ?
Because when I get the Authentication in the JWT successfulAuthentication it shows the Authorities.
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
logger.info("Auth in the successful is [{}]", authResult.getAuthorities()); // [ROLE_ADMIN]
}
Using Spring-Security core 5.0.9.
I did it after getting the hint from #Reza Nasiri question basically I was not adding the Authorities when authenticating the JWT token now what I did is,
return new UsernamePasswordAuthenticationToken(user, null, getAuthorities("ADMIN");
In my getAuthentication function in JWTAuthorizationFilter class.

Spring boot , CrudRepository findbyid or findOne is not getting One to many table details of role table

#Entity
#NamedQuery(name="User.findAll", query="SELECT u FROM User u")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private Long id;
private String email;
private String password;
private String username;
#Transient
private String passwordConfirm;
//bi-directional many-to-one association to Role
#ManyToOne(fetch=FetchType.LAZY ,cascade = {CascadeType.PERSIST})
private Role role;
public User() {
}
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public String getEmail() {
return this.email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
public Role getRole() {
return this.role;
}
public void setRole(Role role) {
this.role = role;
}
#Transient
public String getPasswordConfirm() {
return passwordConfirm;
}
public void setPasswordConfirm(String passwordConfirm) {
this.passwordConfirm = passwordConfirm;
}
}
#Entity
#NamedQuery(name="Role.findAll", query="SELECT r FROM Role r")
public class Role implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private Long id;
private String name;
//bi-directional many-to-one association to User
#OneToMany(fetch=FetchType.LAZY, mappedBy="role", cascade = {CascadeType.PERSIST})
private List<User> users;
public Role() {
}
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public List<User> getUsers() {
return this.users;
}
public void setUsers(List<User> users) {
this.users = users;
}
public User addUser(User user) {
getUsers().add(user);
user.setRole(this);
return user;
}
public User removeUser(User user) {
getUsers().remove(user);
user.setRole(null);
return user;
}
}
public interface UserRepository extends CrudRepository <User, Long> {
public UserDto getUserDetailsById(Long userId) throws commonException {
//ArrayList<UserDto> arr = new ArrayList<>();
User user = userRepository.`findOne`(userId);
UserDto userDto = new UserDto();
userDto.setId(user.getId());
userDto.setUsername(user.getUsername());
userDto.setEmail(user.getEmail());
userDto.setPassword(user.getPassword());
userDto.setRoleId(user.getRole().getId());
userDto.setRoleName(user.getRole().getName());
// arr.add(userDto);
return userDto;
}
find by Id is not getting role details by using user object.lazy initialization is not happening.When I use to get user ID from user object, I can not get role details.
userDto.setRoleId(user.getRole().getId()); is having null value
In your User entity, you have configured the Role collection to load lazily. Therefore when you first call user.getRole(), you get a proxy object. If you want to call methods on the proxy object that need to fetch data, you should initialize the proxy. There is a technique to initialize it. Try the following:
Before the following line of code
userDto.setRoleId(user.getRole().getId());
add the following;
user.getRole().size();
For situations where you know that you need a particular association, its generally recommended that you specify that the association be join-fetched.
If the association is optional, you'd use something like:
FROM User u LEFT JOIN FETCH u.role WHERE u.id = :userId
If the association is not-optional, you could improve the above by specifying an inner join like:
FROM User u JOIN FETCH u.role WHERE u.id = :userId
With Spring data, you can use the #Query annotation on methods and specify the JPQL/HQL above to suit your needs.

How to save an object to db and connect it with logged in user

I'm making my first web-app using Spring Boot, Thymeleaf, Hibernate (with in-memory H2 database). I also want to use Spring Security but I try to avoid it for now (want to finish Entities and repositories first although I know I will have to change the way they work) I want a user to register on my site (he is created and added to my db) and then he can CRUD his ToDos (which are also saved to db). I have connected both entitites with bidirectional OneToMany using annotations and #JoinTable/#mappedBy. Here are my entities:
ToDoItem.java
#Entity
#Table (name = "TO_DO_ITEM")
public class ToDoItem extends BaseEntity {
#Column(name = "TITLE", nullable = false)
private String title;
#Column(name = "COMPLETED")
private boolean completed;
// TODO: 29.01.17 Add description property
#Column(name = "DUE_DATE", nullable = false)
private LocalDate dueDate;
// two-directional OneToMany
#ManyToOne
#JoinColumn(name = "USER_ID")
private User user;
// JPA demands empty contructor
ToDoItem() {}
public ToDoItem(String title, LocalDate dueDate) {
this.title = title;
this.dueDate = dueDate;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public boolean isCompleted() {
return completed;
}
public void setCompleted(boolean completed) {
this.completed = completed;
}
public LocalDate getDueDate() {
return dueDate;
}
public void setDueDate(LocalDate dueDate) {
this.dueDate = dueDate;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
User.java
#Entity
#Table (name = "USERS")
public class User extends BaseEntity {
#Column(name = "USERNAME")
private String username;
// TODO: 28.01.17 Find a way to store hashed and salted pws in DB
#Column(name = "PASSWORD")
private String password;
#Column(name = "EMAIL")
private String email;
#OneToMany(mappedBy = "user")
private Set<ToDoItem> toDoItems = new HashSet<>();
// JPA demands empty constructor
User() {}
public User(String username, String password, String email) {
this.username = username;
this.password = password;
this.email = email;
}
public Set<ToDoItem> getToDoItems() {
return toDoItems;
}
public void setToDoItems(Set<ToDoItem> toDoItems) {
this.toDoItems = toDoItems;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
I am initialized a set in User.java so I can add created items by logged in user to his own set so they are not just sent to the db, but they are actually connected. Now I just need a method to add newly created item by him to his set (and to the db).
I have two Service beans:
ToDoItemServiceBean.java
#Service
public class ToDoItemServiceBean implements ToDoItemService {
#Autowired
private ToDoItemRepository toDoItemRepository;
#Override
public Iterable<ToDoItem> listAllToDoItems() {
return toDoItemRepository.findAll();
}
#Override
public ToDoItem getToDoItemById(Long id) {
return toDoItemRepository.findOne(id);
}
#Override
public ToDoItem addNewToDo(ToDoItem toDoItem) {
return toDoItemRepository.save(toDoItem);
}
#Override
public void deleteToDo(Long id) {
toDoItemRepository.delete(id);
}
}
UserServiceBean.java
#Service
public class UserServiceBean implements UserService {
#Autowired
private UserRepository userRepository;
#Override
public User saveUser(User user) {
return userRepository.save(user);
}
}
And I have no clue how to add those ToDos to a specific user in db. Should I just add an item to a set in addNewToDo so apart from adding new ToDo to db this method would also somehow connect ToDo with a user? But how do I do it? What's the best approach to fix it?
HERE is the rest of my code (didn't want to paste all of my files into this post).
EDIT:
I rethought my db structure and I think that I should have OneToMany in UserEntity because User can have many ToDos but OneToOne in ToDoItemEntity because a ToDo can have only one user - that's correct, right?
And from that link I made something like this, is it ok?
ToDoItemEntity
// a ToDoItem is only associated with one user
#OneToOne(cascade = CascadeType.ALL, mappedBy = "toDoItems")
private User user;
UserEntity
//user can have many ToDoItems
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "USER_ID")
private Set<ToDoItem> toDoItems;
Added updateUser to:
UserServiceBean.java
#Override
public void updateUser(ToDoItem toDoItem) {
User currentUser = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
User queriedUser = userRepository.findOne(currentUser.getId());
queriedUser.setToDoItems();
}
you can add ToDoItems to the user and save the user entity. Make use of Cascade operations to cascade the operation
http://howtodoinjava.com/hibernate/hibernate-jpa-cascade-types/
// two-directional OneToMany
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name = "USER_ID")
private User user;
update : UserServiceBean.java
public void updateUser(User user){
// update the user entity here by adding the todoitems and call saveuser again
}
Update 2 : ToDoItemController
#Override
public ToDoItem addNewToDo(ToDoItem toDoItem) {
return toDoItemRepository.updateItems(toDoItem);
}
ToDoItemServiceBean :
public void updateItems(ToDoItem toDoItem) {
User currentUser = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
User queriedUser = userRepository.findOne(currentUser.getId());
toDoItem.setUser(queriedUser);
saveToDoItem(toDoItem)// since it is bidirectional
}

Categories