I have a login page where the data will be retrieved from database
User
#Setter
#Getter
#Entity
#Table(name = "USER_DETAILS")
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "USER_ID")
private Long id;
#Column(name = "USER_NAME")
private String userName;
#Column(name = "USER_PASSWORD")
private String userPassword;
#Transient
private Set<Role> roles;
#ManyToMany
#JoinTable(name = "USER_ROLE", joinColumns = #JoinColumn(name = "USER_ID"), inverseJoinColumns = #JoinColumn(name = "ROLE_ID"))
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
}
Role
#Setter
#Getter
#Entity
#Table(name = "USER_ROLE")
public class Role implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ROLE_ID")
private Long id;
#Column(name = "USER_NAME")
private String userName;
#Column(name = "ROLE_NAME")
private String roles;
#Transient
private Set<User>users;
#ManyToMany(mappedBy = "roles")
public Set<User> getUsers() {
return users;
}
public void setUsers(Set<User> users) {
this.users = users;
}
}
UserDetailServiceImpl
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
#Transactional(readOnly = true)
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
System.out.println("Name "+user.getUserName());
System.out.println("role is "+user.getRoles());
if(null == user) {
throw new UsernameNotFoundException("No user present with username: " + username);
}
Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
for (Role role : user.getRoles()){
grantedAuthorities.add(new SimpleGrantedAuthority(role.getUserName()));
}
return new org.springframework.security.core.userdetails.User(user.getUserName(), user.getUserPassword(), grantedAuthorities);
}
}
Table
Role
ROLE_ID | USER_NAME | ROLE_NAME
1 John Admin
User
USER_ID | USER_NAME | USER_PASSWORD | USER_ROLE
1 John pass Admin
Output
Name John role is null 2018-03-12 00:52:06.362 ERROR 12563 ---
[nio-8088-exec-8] w.a.UsernamePasswordAuthenticationFilter : An
internal error occurred while trying to authenticate the user.
org.springframework.security.authentication.InternalAuthenticationServiceException:
null
I can't get the role value. What's wrong with the database structure ?
For #ManyToMany to work, you need 3 tables not 2. The two parent tables are typically linked through a third table containing two foreign keys. So here, there should be a third table which would have role_id and user_id.
Also, instead of adding #ManyToMany annotation to the getters, try adding it at the time of declaration itself. Something like this in User class,
#ManyToMany(cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
#JoinTable(name = "user_role",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "role_id")
)
private Set<Role> roles = new HashSet<>();
And the same modification in Role class:
#ManyToMany(mappedBy = "roles")
private Set<User> users = new HashSet<>();
Here, user_role is the third table.
Related
I am trying to have a many to many relationship between user and group, so when I save group the user and group information should get saved in user_group table, but I am getting this error while trying to save Group information,In request I am just passing groupName.
Error
2022-11-07 14:45:47.565 ERROR 29066 --- [nio-8080-exec-4] o.h.engine.jdbc.spi.SqlExceptionHelper : Cannot add or update a child row: a foreign key constraint fails (`testdb`.`user_group`, CONSTRAINT `FK7k9ade3lqbo483u9vuryxmm34` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`))
User.java
#Entity
#Table(name = "users",
uniqueConstraints = {
#UniqueConstraint(columnNames = "username"),
#UniqueConstraint(columnNames = "email")
})
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable( name = "user_roles",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "role_id"))
private Set<Role> roles = new HashSet<>();
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
},
mappedBy = "users")
#JsonIgnore
private Set<Group> groups = new HashSet<>();
public Set<Group> getGroups() {
return groups;
}
}
Group.java
#Entity
#Table(name = "grp")
public class Group {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
private String groupName;
#Column
private String createdBy;
public Group(String groupName, String createdBy ) {
this.groupName = groupName;
this.createdBy = createdBy;
}
public Group() {
}
}
GroupController.java
#RestController
#RequestMapping("/api")
public class GroupController {
#Autowired
GroupRepository groupRepository;
#Autowired
UserRepository userRepository;
#PostMapping("/group")
public ResponseEntity<Group> createTutorial(#RequestBody Group group, Principal principal) {
try {
Set<User> userSet = new HashSet<>();
Optional<User> user = userRepository.findByUsername(principal.getName());
userSet.add(user.get());
Group group2 = new Group(group.getGroupName(), principal.getName());
group2.setUsers(userSet);
Group group1 = groupRepository.save(group2);
return new ResponseEntity<>(group1, HttpStatus.CREATED);
} catch (Exception e) {
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
The issue was my database schema which got created by an earlier incorrect JPA model. after dropping and recreating the database it got resolved.
i have Three entities User, Institution and Role.
1)one to many between user and institution
2)and many to many between User and Role
-------user-------
#Entity
#Table(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="user_Id")
private int userId;
#Column(name="name")
private String name;
#Column(name="lastname")
private String lastname;
#Column(name="email")
private String email;
#Column(name="password")
private String password;
#Column(name="isActive")
private boolean isActive;
#Column(name="lastActive")
private String lastActive;
#Column(name="createdDate")
private String createdDate;
#Column(name="isBlocked")
private boolean isBlocked;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "institution_id", nullable = false)
#JsonIgnoreProperties(value = {"user"})
private Institution institution;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "user_has_role",
joinColumns = {
#JoinColumn(name = "user_id", referencedColumnName = "user_id",
nullable = false, updatable = true)},
inverseJoinColumns = {
#JoinColumn(name = "role_id", referencedColumnName = "role_id",
nullable = false, updatable = true)})
#JsonIgnoreProperties(value = {"users"})
private Set<Role> roles = new HashSet<>();
}
-------institution-------
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#ToString
#Entity
#Table(name = "institution")
public class Institution {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="institution_Id")
private int institutionId;
#Column(name="name")
private String name;
#Column(name="type")
private String type;
#Column(name="location")
private String location;
#OneToMany(mappedBy = "institution", fetch = FetchType.LAZY)
#JsonIgnoreProperties(value = {"institution" , "user"})
private Set<User> user;
}
-------role-------
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#ToString
#Entity
#Table(name = "role")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="role_Id")
private int roleId;
#Column(name="name")
private String name;
#Column(name="description")
private String description;
#ManyToMany(mappedBy = "roles", fetch = FetchType.LAZY)
#JsonIgnoreProperties(value = {"roles"})
private Set<User> users = new HashSet<>();
}
Those are my 3 entities and tables in MySql
i have 7 roles
• Super-User
• Bank-Admin
• Bank-Support
• Bank-Service
• Merchant-Admin
• Merchant-Support
• Merchant-service
The super-User can create a user of any role
#PostMapping("/addUser")
public String addUser(#RequestBody User user) {
String rawpassword = user.getPassword();
String encodedpasswrod = passwordencoder.encode(rawpassword);
user.setPassword(encodedpasswrod);
userrepository.save(user);
return "user saved with name: " + user.getName();
}
this api works and i can set the role to anything in my api json body
But want that if the User is Bank-Admin he can only create Bank-Support and Bank-Service
im trying to create a new API which can only create a user with those 2 specific roles.
and then restrict the bank admin to access the other API that can create users of any kind.
is there any other way to do it and if no how can i do that...
You have to implement your custom implementation of User Entitlement.
Like according to login person, you will get that login person role, and according to your criteria just put validation like check that entity he is trying to add is he eligible to create it.
Map<String, List<String>> roleUserAccessMap = new HashMap<>();
roleUserAccessMap.put("Bank-Admin", Arrays.asList("Bank-Support", "Bank-Service"));
Just check like below
String loginPersonRole="Bank-Admin"; //This value should get from logged-in person context
if(roleUserAccessMap.containsKey(loginPersonRole) && roleUserAccessMap.get(loginPersonRole).contains(newuserrole) ){
//proceed ahead with Add api
}else{
System.out.println("You do not have enough privileage to create Use");
}
This will help you.
I'm setting up client side shopping cart in my web application. All was ok before adding Shopping Cart class and his service. Now when I try to start the Spring application this error is shown:
Caused by: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.myBookstoreProject.domain.security.UserRole.role -> com.myBookstoreProject.domain.security.Role
I searched for a solution but what I found is a problem with entities of my application. A solution was to add (cascade=CascadeType.ALL) to the entities that are causing the error. But my classes already have it and all was good before Shopping Cart class.
User class:
#Entity
public class User implements UserDetails {
#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;
#Column(name = "email", nullable = false, updatable = false)
private String email;
private String phone;
private boolean enabled = true;
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JsonIgnore
private Set<UserRole> userRoles = new HashSet<>();
#OneToMany(cascade = CascadeType.ALL, mappedBy = "user")
private List<UserShipping> userShippingList;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "user")
private List<UserPayment> userPaymentList;
#OneToOne(cascade = CascadeType.ALL, mappedBy = "user")
private ShoppingCart shoppingCart;
// getters and setters..
}
Role
#Entity
public class Role {
#Id
private int roleId;
private String name;
#OneToMany(mappedBy = "role", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<UserRole> userRoles = new HashSet<UserRole>();
// getters and setters..
}
UserRole class:
#Entity
#Table(name = "user_role")
public class UserRole {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long userRoleId;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "user_id")
private User user;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "role_id")
private Role role;
// getters and setters..
}
Shopping Cart:
#Entity
public class ShoppingCart {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private BigDecimal GrandTotal;
#OneToMany(mappedBy="shoppingCart", cascade=CascadeType.ALL, fetch=FetchType.LAZY)
#JsonIgnore
private List<CartItem> cartItemList;
#OneToOne(cascade=CascadeType.ALL)
private User user;
// getters and setters...
}
Shopping Cart Service implementation:
#Service
public class ShoppingCartServiceImpl implements ShoppingCartService {
#Autowired
private CartItemService cartItemService;
#Autowired
private ShoppingCartRepository shoppingCartRepository;
#Override
public ShoppingCart updateShoppingCart(ShoppingCart shoppingCart) {
BigDecimal cartTotal = new BigDecimal(0);
List<CartItem> cartItemList = cartItemService.findByShoppingCart(shoppingCart);
for (CartItem cartItem : cartItemList) {
if (cartItem.getBook().getInStockNumber() > 0) {
cartItemService.updateCartItem(cartItem);
cartTotal = cartTotal.add(cartItem.getSubtotal());
}
}
shoppingCart.setGrandTotal(cartTotal);
shoppingCartRepository.save(shoppingCart);
return shoppingCart;
}
}
User Service implementation:
In this class method I added "#Transactional" and 5 lines of Shopping Cart and then the error
#Override
#Transactional
public User createUser(User user, Set<UserRole> userRoles) throws Exception {
User localUser = userRepository.findByUsername(user.getUsername());
if (localUser != null) {
LOG.info("user {} already exists. Nothing will be done.", user.getUsername());
} else {
for (UserRole ur : userRoles) {
roleRepository.save(ur.getRole());
}
user.getUserRoles().addAll(userRoles);
ShoppingCart shoppingCart = new ShoppingCart(); // 1
shoppingCart.setUser(user); // 2
user.setShoppingCart(shoppingCart); // 3
user.setUserShippingList(new ArrayList<UserShipping>()); //4
user.setUserPaymentList(new ArrayList<UserPayment>()); // 5
localUser = userRepository.save(user);
}
return localUser;
}
This error terminates Spring application and only creates tables in MySql without adding rows.
Edit 1:
The problem occurs when I try to add a new user to my application. This is my boot main class:
#SpringBootApplication
public class BookstoreProjectApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(BookstoreProjectApplication.class, args);
}
#Autowired
private UserService userService;
#Override
public void run(String... args) throws Exception {
User user1 = new User();
user1.setFirstName("New");
user1.setLastName("User");
user1.setUsername("j");
user1.setPassword(SecurityUtility.passwordEncoder().encode("p"));
user1.setEmail("newUser#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);
}
}
If I comment method body(run), server runs very well until a new user should be created, then the error appears.
You are persisting the roles from your userRole and then assigning them to the user, but you don't assign the persisted entities to the roles after saving them, thus the roles in userRole are not the same as the persisted ones anymore and also do not have the generated id. When you save an Entity and then add it or a parent to another Entity as a value and not have full cascading, you are adding a different Object. This means, use the return Object from save and reassign it to the object you saved and then it should be fine, or use cascading everywhere and only save 1 object.
TLDR; userRoles' role is not the same as the Role Entities in your Database.
EDIT 1:
Change Set<UserRole> userRoles to List<UserRole> userRoles (otherwise you have to convert it like 100 times since you cannot replace the value of a Set while traversing it afaik) and then replace
for (UserRole ur : userRoles) {
roleRepository.save(ur.getRole());
}
with
for (int i = 0; i < userRoles.size(); i++) {
userRoles.get(i).setRole(roleRepository.save(userRoles.get(i).getRole())
}
I have three tables: users(id, name, login, password), roles(id, name), user_roles(id, user_id, role_id)
This is my code
#Entity
#Table(name = "users")
public class User extends Model {
#Id
public Long id;
public String name;
public String login;
public String password;
#ManyToMany
#JoinTable(
name = "user_roles",
joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "role_id", referencedColumnName = "id")
)
public Set<Role> roles;
public static Finder<Integer, User> find = new Finder<>(User.class);
}
#Entity
#Table(name = "roles")
public class Role extends Model {
#Id
public Long id;
public String name;
#ManyToMany(mappedBy = "roles")
public List<User> users;
public static Finder<Integer, Role> find = new Finder<>(Role.class);
}
I want to display all users with roles, example: {"id":1, "name":"My Name", "login":"My Login", "password":"My Password", roles: [{"name":"ADMIN"}, {"name":"USER"}]}
How can I do this? I'm new in Ebean and ORM. Thanks for any help.
Update
public Result all() {
List<User> users = User.find.all();
return ok(toJson(users));
}
But now I getting stackoverflow error infinite recursion.
Make users.role = null and then return Json
I have two tables(entities):
#Entity
#Table(name = "users")
#NamedQuery(name = "User.getAll", query = "SELECT c from User c")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#NotNull
#Column(name = "LOGIN")
private String login;
#Column(name = "PASSWORD", length = 64)
private String password;
#Column(name = "SALT", length = 80)
private String salt;
#ManyToMany(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
private Set<Role> roles;
#OneToMany(mappedBy = "user", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private Set<Permission> permissions;
And
#Entity
#Table(name = "roles")
#NamedQuery(name = "Role.getAll", query = "SELECT c from Role c")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "ROLE_NAME", length = 100)
private String roleName;
#ManyToMany(mappedBy = "roles")
private Set<User> users;
And method for select Users:
#Override
public List<User> getUsersList() {
Criteria criteria = getSession().createCriteria(User.class);
return (List<User>)criteria.list();
}
I have 2 users. First user has 2 roles and second user has 1 role.
But this method return me 3 users. User who has 2 role is dublicate.
Itried criteria.createCriteria("", JoinType.NONE);
but it not helped.
You need to use criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY), check:
criteria-distinct-root-entity-vs-projections-distinct