Spring Data: Could not determine type for: java.util.List, at table: user, for columns: [org.hibernate.mapping.Column(roles)] - spring-boot

I am new to Spring, and I was following this tutorial Spring Rest Services
to understand how oAuth works with REST APIs.
Prior to this, my app was running smoothly.
While working with the tutorial, it required me to have my User entity implement UserDetails. And I had to add an extra List<String> roles because it is used in my UserDetailsImpl service which implements UserDetailsService.
And now when I run mvn spring-boot:run I get the error that's mentioned in the title.
I looked up online, but most of the issues were related to table associations via a particular column, but in my code, there is no type of association mapped to/from roles column.
Here is my User entity:
#Entity
public class User implements UserDetails {
#Id
private UUID id;
private String name;
private String email;
private String password;
private List<String> roles;
public User (){
}
public User (String email, String name, String password, UUID id, List<String> roles){
this.email = email;
this.name = name;
this.password = password;
this.id = id;
this.roles = roles;
}
public String getEmail() {
return email;
}
public String getName() {
return name;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
#Override
public String getPassword() {
return password;
}
#Override
public String getUsername() {
return email;
}
#Override
public boolean isAccountNonExpired() {
return false;
}
#Override
public boolean isAccountNonLocked() {
return false;
}
#Override
public boolean isCredentialsNonExpired() {
return false;
}
#Override
public boolean isEnabled() {
return false;
}
public UUID getId() {
return id;
}
List<String> getRoles() {
return roles;
}
public void setPassword(String password) {
this.password = password;
}
public void setEmail(String email) {
this.email = email;
}
public void setName(String name) {
this.name = name;
}
public void setId(UUID id) {
this.id = id;
}
}
And this my UserDetailsImp service:
#Service
public class UserDetailsImp implements UserDetailsService {
#Autowired
UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
User user = userRepository.getOneByEmail(email);
UserBuilder builder = null;
if( user != null){
builder.username(user.getEmail()).password(user.getPassword()).roles(String.join("",user.getRoles()));
} else {
throw new UsernameNotFoundException("User not found");
}
return builder.build();
}
}
Any help would be hugely appreciated.

The field private List<String> roles has unknown type in the DB.
Try using #ElementCollection annotation.
https://docs.oracle.com/javaee/6/api/javax/persistence/ElementCollection.html

Related

Spring Boot; passing user's First Name to welcome.jsp after logging in

A lot of the articles online for Spring Boot deals with Spring Security and it does not help me in the slightest. I am trying to implement a registration and login page and once the user successfully logins, it will take them to a welcome page where it should display their first name, something like "Welcome first name or Welcome username". I have tried passing the first name through a
model.addAttribute("firstName", accountInstance.getFirstName());
but that doesn't seem to work. Any hints to achieve this would be much appreciated
Login Controller
#Controller
public class LoginController {
#Autowired
private AccountRepository accountRepo;
#RequestMapping(value = "/login", method = RequestMethod.GET)
public String showLoginPage(ModelMap model) {
model.addAttribute("login", new AccountEntity());
return "login";
}
#RequestMapping(value = "/login", method = RequestMethod.POST)
public Object submitLoginIn(#ModelAttribute("login") AccountEntity accountForm, Model model) {
AccountEntity accountInstance = accountRepo.findByEmail(accountForm.getEmail().toLowerCase());
// Password Verifier using Argon2
Argon2PasswordEncoder argon2PasswordEncoder = new Argon2PasswordEncoder();
boolean passwordMatch = argon2PasswordEncoder.matches(accountForm.getPassword(), accountInstance.getPassword());
// issue where if i use caps email, throws null pointer exception
if (accountInstance == null || !passwordMatch) {
System.out.println("Invalid Email or Password");
// return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
return "login";
} else if (accountInstance.isEnabled() == false) {
System.out.println("Cant login cause not verified");
return "login";
} else {
System.out.println("account exist");
model.addAttribute("firstName", accountInstance.getFirstName());
return "redirect:welcome"; // Change later
}
}
}
Account Repository
public interface AccountRepository extends CrudRepository<AccountEntity, Long> {
// Optional<AccountEntity> findById(Long Id);
AccountEntity findByUserName(String userName);
AccountEntity findByPassword(String password);
AccountEntity findByEmail(String email);
AccountEntity findByVerificationCode(String verificationCode);
}
Account Entity
#Entity(name = "user")
public class AccountEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
private String userName;
private String email;
private String password;
// private String gender;
private Integer age;
private Date createdDate;
private boolean enabled;
#Column(updatable = false)
private String verificationCode;
// Getters and Setters
public void setId(Long id) {
this.id = id;
}
public Long getId() {
return id;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
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 String getGender() { return gender; }
*
* public void setGender(String gender) { this.gender = gender; }
*/
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getVerificationCode() {
return verificationCode;
}
public void setVerificationCode(String verificationCode) {
this.verificationCode = verificationCode;
}
public Date getCreatedDate() {
return createdDate;
}
public void setCreatedDate(Date createdDate) {
this.createdDate = createdDate;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
Welcome.jsp
<p> Welcome, ${firstName.firstName} </p>
<!-- <p> Welcome, ${firstName} </p> -->
SO #Bollywood was correct with the redirecting:welcome. Doing so didn't pass the value I wanted to the jsp. Changing it to return "welcome" instead of return "redirect:welcome" worked!

Why all properties of a Model passed from AOP to controller with other arguments are null

AOP
#Around(
"execution(* net.inter.warp.bridge.controller.*.*(.., net.inter.warp.bridge.model.User)) && " +
"args(.., authenticatedUser)"
)
public Object withAuthenticatedUser(ProceedingJoinPoint joinPoint, User authenticatedUser) throws Throwable {
System.out.println(joinPoint + " -> " + authenticatedUser);
User user = null;
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null)
user = (User) userService.loadUserByUsername(authentication.getName());
else
throw new UnauthorizedException("err 1");
if (user == null)
throw new UnauthorizedException("err 2");
return joinPoint.proceed(new Object[]{user});
}
Controller (all properties of authenticatedUser are null)
package net.inter.warp.bridge.controller;
#GetMapping("/boxes/{id}")
public ResponseEntity<Box> getBoxById(#PathVariable(value = "id") Long boxId, User authenticatedUser)
throws NoDynamicTableFoundException, ResourceNotFoundException {}
Controller (This works as there is no more parameters except for authenticatedUser)
package net.inter.warp.bridge.controller;
#GetMapping("/boxes/{id}")
public ResponseEntity<Box> getBoxById(User authenticatedUser)
throws NoDynamicTableFoundException, ResourceNotFoundException {}
AOP seems to hate other paramethers... authenticatedUser is not null, every property of authenticatedUser is null.
Model (I am not sure this issue is related to this)
#Entity
#Table(name="users")
#ToString
public class User extends AuthEntity implements UserDetails
{
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
String[] userRoles = this.roles.stream().map((role) -> role.getName()).toArray(String[]::new);
Collection<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(userRoles);
return authorities;
}
#Override
public String getUsername() {
return this.email;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
#Column(nullable=false)
#NotNull(message = "")
private String name;
#Column(nullable=false, unique=true)
#Email
//#NotBlank(message = "")
private String email;
#Column
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
#JsonIgnore
private String password;
#Column(length = 20, columnDefinition ="bigint")
//#NotNull(message = "")
private Long organization_id;
#ManyToOne(optional=false)
#JoinColumn(name = "organization_id",referencedColumnName="id", insertable=false, updatable=false)
//#JsonIgnore
private Organization organization;
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.MERGE)
//#Fetch(org.hibernate.annotations.FetchMode.SELECT)
#JoinTable(
name="user_role",
joinColumns={#JoinColumn(name="user_id")},
inverseJoinColumns={#JoinColumn(name="role_id")})
private List<Role> roles;
/*
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.MERGE)
//#Fetch(org.hibernate.annotations.FetchMode.SELECT)
#Fetch(value = FetchMode.SUBSELECT)
#JoinTable(
name="hyperbridge_resource.user_workspace",
joinColumns={#JoinColumn(name="user_id")},
inverseJoinColumns={#JoinColumn(name="workspace_id")})
private List<Workspace> workspaces;
*/
#Column(length = 1, columnDefinition ="char")
private String active;
#Column(name = "reset_token")
#JsonIgnore
private String resetToken;
#Column(name = "reset_token_time")
#DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "Asia/Seoul")
private LocalDateTime resetTokenTime;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
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 Long getOrganization_id() {
return organization_id;
}
public void setOrganization_id(Long organization_id) {
this.organization_id = organization_id;
}
public Organization getOrganization() {
return organization;
}
public void setOrganization(Organization organization) {
this.organization = organization;
}
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
/* public List<Workspace> getWorkspaces() {
return workspaces;
}
public void setWorkspaces(List<Workspace> workspaces) {
this.workspaces = workspaces;
}*/
public String getActive() {
return active;
}
public void setActive(String active) {
this.active = active;
}
public String getResetToken() {
return resetToken;
}
public void setResetToken(String resetToken) {
this.resetToken = resetToken;
}
public LocalDateTime getResetTokenTime() {
return resetTokenTime;
}
public void setResetTokenTime(LocalDateTime resetTokenTime) {
this.resetTokenTime = resetTokenTime;
}
}
Try this, doc:
#Around(
"execution(* net.inter.warp.bridge.controller.*.*(..) && " +
"args(authenticatedUser,..)"

Spring Data JPA - findByAlias vs. findUserByAlias - error with both

I am using spring boot 2 with JPA and Spring Security.
I have a handler method that, depending upon a dropdown, will find all links either similar to a specific title or posted by a specific user. I know my database is set up properly.
I am getting a null pointer exception at this line:
Optional<User> user = userRepository.findUserByAlias("searchTerm");
I have tried changing the method to findByAlias(...) with the same result.
This is the code for my UserRepository:
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
Optional<User> findUserByAlias(String alias);
Optional<User> findByAlias(String alias);
}
and this is my handler method wherein the error occurs at this line:
Optional<User> user = userRepository.findUserByAlias("searchTerm");
#GetMapping("/search")
public String showSearchResults(#RequestParam("searchTerm") String searchTerm, #RequestParam("searchBy") String searchBy, Model model) {
System.out.println("INSIDE showSearchResults + searchTerm =" + searchTerm);
List<Link> searchResults;
if(searchBy.equals("user")) {
System.out.println("INSIDE IF EQUALS 'user'");
// get the user by alias
Optional<User> user = userRepository.findUserByAlias("searchTerm");
// if the user is present the find all links by the user id
if (user.isPresent()) {
searchResults = linkRepository.findAllByUser_Id(user.get().getId());
} else {
searchResults = null;
}
}
if(searchBy.equals("title")){
searchResults = linkRepository.findAllByTitleLike("%" + searchTerm + "%");
} else {
searchResults = null;
}
model.addAttribute("searchTerm", new SearchTerm());
model.addAttribute("searchResults", searchResults);
return "search-results";
}
and this is my User class:
#Entity
public class User implements UserDetails {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column
private Long id;
#Column
private String email;
#Column
private String password;
#Column
private boolean enabled;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(
name = "users_roles",
joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "role_id", referencedColumnName = "id")
)
private Set<Role> roles = new HashSet<>();
#Column
private String firstName;
#Column
private String lastName;
#Column
private String fullName;
#Column
private String alias;
#Transient
private String confirmPassword;
public User(){
}
public User(String email, String password, boolean enabled,
String firstName, String lastName,
String fullName, String alias) {
this.email = email;
this.password = password;
this.enabled = enabled;
this.firstName = firstName;
this.lastName = lastName;
this.fullName = fullName;
this.alias = alias;
}
public void addRole(Role role){
roles.add(role);
}
public void addRoles(Set<Role> roles) {
roles.forEach(this::addRole);
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
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 boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<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 getFullName() {
return firstName + " " + lastName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public String getAlias() {
return alias;
}
public void setAlias(String alias) {
this.alias = alias;
}
public String getConfirmPassword() {
return confirmPassword;
}
public void setConfirmPassword(String confirmPassword) {
this.confirmPassword = confirmPassword;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
for(Role role : roles){
authorities.add(new SimpleGrantedAuthority(role.getName()));
}
return authorities;
}
#Override
public String getUsername() {
return null;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
}
So I have two questions:
1.) First and most importantly - why am I getting null pointer exception? I'm at a loss as to how to go about debugging this.
2.) What is the difference between findByAlias and findUserByAlias?
Any advice would be much appreciated.
Thank you for your help,
Marc
How is your userRepository injected into your controller? It seems that is the most likely reason for the null pointer.

Retrieve data from a specific entity in an inheritance relationship in Spring Data Mongo

I have implemented an inheritance relationship using Spring Data MongoDB
I have an abstract entity that contains all the attributes common to all the others.
#Document(collection = PersonEntity.COLLECTION_NAME)
public abstract class PersonEntity {
public final static String COLLECTION_NAME = "persons";
#Id
private ObjectId id;
#Field("first_name")
private String firstName;
#Field("last_name")
private String lastName;
private Integer age;
public PersonEntity(){}
#PersistenceConstructor
public PersonEntity(String firstName, String lastName, Integer age) {
super();
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
public ObjectId getId() {
return id;
}
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 Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getFullName(){
return this.firstName + " - " + this.lastName;
}
}
This entity inherits the entities UserSystemEntity and SonEntity. These have specific #Field attributes and do not define the #Document annotation. So all documents are stored in the collection of "PERSONS".
To work with these entities I have created the corresponding repositories. Here I put the repository for the entity SonEntity.
#Repository
public interface SonRepository extends MongoRepository<SonEntity, ObjectId> {
Iterable<SonEntity> findByParentId(ObjectId id);
Long countByParentId(ObjectId id);
Long countByParentIdAndId(ObjectId parentId, ObjectId id);
}
The problem I have, is that when I use this repository to obtain a list of entities "SonEntity" by the following method:
#Override
public Page<SonDTO> findPaginated(Pageable pageable) {
Page<SonEntity> childrenPage = sonRepository.findAll(pageable);
return childrenPage.map(new Converter<SonEntity, SonDTO>(){
#Override
public SonDTO convert(SonEntity sonEntity) {
return sonEntityMapper.sonEntityToSonDTO(sonEntity);
}
});
It returns me documents of all entity types (UserSystemEntity, ParentEntity, SonEntity).
How can I configure this correctly to retrieve only the documents of the SonEntity entity?
Thanks in advance.
"SonEntity" Entity code:
public final class SonEntity extends PersonEntity {
#DBRef
private SchoolEntity school;
#DBRef
private ParentEntity parent;
public SonEntity() {
}
#PersistenceConstructor
public SonEntity(String firstName, String lastName, Integer age, SchoolEntity school, ParentEntity parent) {
super(firstName, lastName, age);
this.school = school;
this.parent = parent;
}
public SchoolEntity getSchool() {
return school;
}
public void setSchool(SchoolEntity school) {
this.school = school;
}
public ParentEntity getParent() {
return parent;
}
public void setParent(ParentEntity parent) {
this.parent = parent;
}
}
"UserSystemEntity" code:
public class UserSystemEntity extends PersonEntity {
#Field("email")
protected String email;
#Field("password")
protected String password;
#Field("is_locked")
protected Boolean locked = Boolean.FALSE;
#Field("last_login_access")
protected Date lastLoginAccess;
#DBRef
protected AuthorityEntity authority;
public UserSystemEntity() {
}
#PersistenceConstructor
public UserSystemEntity(String firstName, String lastName, Integer age, String email, String password, AuthorityEntity authority) {
super(firstName, lastName, age);
this.email = email;
this.password = password;
this.authority = authority;
}
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 Boolean isLocked() {
return locked;
}
public void setLocked(Boolean locked) {
this.locked = locked;
}
public Date getLastLoginAccess() {
return lastLoginAccess;
}
public void setLastLoginAccess(Date lastLoginAccess) {
this.lastLoginAccess = lastLoginAccess;
}
public AuthorityEntity getAuthority() {
return authority;
}
public void setAuthority(AuthorityEntity authority) {
this.authority = authority;
}
#Override
public String toString() {
return "UserSystemEntity [email=" + email + ", password=" + password + ", locked=" + locked + ", authority="
+ authority + "]";
}
}
"ParentEntity" code:
public final class ParentEntity extends UserSystemEntity {
public ParentEntity() {
}
#PersistenceConstructor
public ParentEntity(String firstName, String lastName, Integer age, String email, String password,
AuthorityEntity authority) {
super(firstName, lastName, age, email, password, authority);
}
}
Here you can verify that when trying to list all entities of type "SonEntity" appear data of other entities. The user "admin" is stored in the DB as a document of type UserSystemEntity:
The information is stored in MongoDB as follows:

[org.hibernate.mapping.Column(authorities)]

I have follow Hibernate error:
"Could not determine type for: java.util.List, at table: user, for columns: [org.hibernate.mapping.Column(authorities)]"
What can cause a problem?
Also, is it possible use it without model role, and set return getAuthority role "USER" by default?
User model:
#Entity
#Table(name = "user")
public class User implements UserDetails {
private int id;
private String username;
private String password;
private List<Role> roles;
#Id
#GeneratedValue
#Column(name = "id")
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#Basic
#Column(name = "username")
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
#Basic
#Column(name = "password")
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
#Override
public String getUsername() {
return this.username;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
#Override
public List<Role> getAuthorities() {
return this.roles;
}
#OneToMany(mappedBy = "user")
public List<Role> getRoles() {
return this.roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
}
Role model:
#Entity
#Table(name = "role")
public class Role implements GrantedAuthority {
private int id;
private User user;
private String role;
public Role() {
}
public Role(String role){
this.role = role;
}
#Id
#GeneratedValue
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#ManyToOne
#JoinColumn(name="user_id")
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
#Override
public String getAuthority() {
return this.role;
}
#Basic
#Column(name="role")
public String getRole(){
return this.role;
}
public void setRole(String role){
this.role = role;
}
}
I found solution myself. I set #Transient annotation to all method, which i don't want that it shouldn't be column in table.
You must replace all annotations for the attributes not to the getter methods.

Resources