Unidirectional #OneToOne resulting in null foreign key - spring

I have two entities:
User.java
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
public class User implements UserDetails {
#Id
#GeneratedValue
Long id;
#NotBlank
String username;
#NotBlank
String password;
#ElementCollection
#Builder.Default
List<String> roles = new ArrayList<>();
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return this.roles.stream().map(SimpleGrantedAuthority::new).collect(toList());
}
#Override
public String getUsername() {
return username;
}
#Override
public boolean isAccountNonExpired() {
return false;
}
#Override
public boolean isAccountNonLocked() {
return false;
}
#Override
public boolean isCredentialsNonExpired() {
return false;
}
#Override
public boolean isEnabled() {
return false;
}
}
Student.java
#Entity
#Data
public class Student {
#Id
String id;
String name;
String firstName;
#OneToOne
#JoinColumn(name = "user_id")
User user;
public void addUser(User user) {
this.user = user;
}
}
The StudentService should add a new Student, create a new User and add it to the Student
#Override
public boolean addStudent(StudentDTO student) {
if (!studentRepository.existsById(student.getId())) {
Student s = modelMapper.map(student, Student.class);
studentRepository.save(s);
String username = String.format("s%s", student.getId());
List<String> roles = Arrays.asList("ROLE_STUDENT");
User user = userService.createUser(username, roles);
userService.addUser(user);
s.addUser(user);
return true;
}
return false;
}
The User is being created by UserService
#Override
public User createUser(String username, List<String> roles) {
String password = RandomStringUtils.randomAlphanumeric(16);
User u = User.builder()
.username(username)
.password(encoder.encode(password))
.roles(roles)
.build();
if (addUser(u)) {
return u;
} else {
throw new UserAlreadyExistingException(username);
}
}
#Override
public boolean addUser(User user) {
boolean alreadyExists = userRepository.findByUsername(user.getUsername()).isPresent();
if (true) {
userRepository.save(user);
return true;
}
return false;
}
Despite the User and the Student are being added to my database, the user_id column in the student table is always set to null. I also checked if the User being added to the Student is properly initialized and filled with all the information but the transaction is committed.

user_id is set to null because you are not saving mapping to database.
Changed code with comments:
#Override
public boolean addStudent(StudentDTO student) {
if (!studentRepository.existsById(student.getId())) {
Student s = modelMapper.map(student, Student.class);
// don't save student here
String username = String.format("s%s", student.getId());
List<String> roles = Arrays.asList("ROLE_STUDENT");
User user = userService.createUser(username, roles);
userService.addUser(user);
s.addUser(user);
//save student here after adding user.
studentRepository.save(s)
return true;
}
return false;
}

Related

Spring Not Mapping Composite Key Entity To Database

I have two entities User and Focus for which I must store a ranking such that a User can have a ranking for many Focus', and a Focus may have many users. To handle this I've followed https://www.baeldung.com/jpa-many-to-many part 3 whereby I have the 2 following classes:
#Entity(name = "UserBaseRatings")
public class Rating {
#EmbeddedId
RatingKey id;
#ManyToOne
#MapsId("userID")
#JoinColumn(name="userID")
private User user;
#ManyToOne
#MapsId("focusID")
#JoinColumn(name="focusID")
private Focus focus;
private BigDecimal baseRating;
protected Rating(){}
public Rating(User user, Focus focus, BigDecimal baseRating) {
this.user = user;
this.focus = focus;
this.baseRating = baseRating;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Focus getFocus() {
return focus;
}
public void setFocus(Focus focus) {
this.focus = focus;
}
public BigDecimal getBaseRating() {
return baseRating;
}
public void setBaseRating(BigDecimal baseRating) {
this.baseRating = baseRating;
}
}
and
#Embeddable
public class RatingKey implements Serializable {
#Column(name="userID")
private Long userID;
#Column(name="focusID")
private Long focusID;
protected RatingKey(){}
public RatingKey(Long userID, Long focusID) {
this.userID = userID;
this.focusID = focusID;
}
public Long getUserID() {
return userID;
}
public void setUserID(Long userID) {
this.userID = userID;
}
public Long getFocusID() {
return focusID;
}
public void setFocusID(Long focusID) {
this.focusID = focusID;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
RatingKey ratingKey = (RatingKey) o;
return Objects.equals(userID, ratingKey.userID) &&
Objects.equals(focusID, ratingKey.focusID);
}
#Override
public int hashCode() {
return Objects.hash(userID, focusID);
}
}
The table for UserBaseRatings has a default value of 1 for baseRating such that if a user does not have a rating on the current focus their rating should be 1. I'm running into a problem where if the user hasn't made a rating then the set of Rating's is empty instead of having a rating of 1 for each focus that exists.
User:
public class User {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long userID;
#Column(name = "userHashedPassword")
private String password;
#Column(name = "userName")
private String userName;
#Column(name = "userEmail")
private String email;
//probably being reset
#Transient
private List<String> groups = new LinkedList<>();
#ManyToMany
#JoinTable(name = "UserRoles",
joinColumns = #JoinColumn(
name = "userID"),
inverseJoinColumns = #JoinColumn(
name = "roleID"))
private Set<Role> roles = new HashSet<>();
#OneToMany(mappedBy = "user")
private Set<Rating> ratings;
protected User(){}
public User(String userHashedPassword, String userName, String email, Set<Role> roles){
this.password = userHashedPassword;
this.userName = userName;
this.email = email;
this.roles = roles;
}
public Long getUserId() {
return userID;
}
public void setId(Long userID) {
this.userID = userID;
}
public String getPassword(){
return password;
}
public void setPassword(String password){
this.password = password;
}
public String getUsername() {
return userName;
}
public void setUsername(String name) {
this.userName = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
public Set<Rating> getRatings() {
return ratings;
}
public void setRatings(Set<Rating> ratings) {
this.ratings = ratings;
}
}
Focus:
#Entity
public class Focus {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long focusID;
private String focusCategory;
private String focusName;
private String focusExplanation;
#OneToMany(mappedBy = "focus")
Set<Rating> ratings;
//image
protected Focus(){}
public Focus(String focusCategory, String focusName, String focusExplanation, Set<Rating> ratings){
this.focusCategory = focusCategory;
this.focusName = focusName;
this.focusExplanation = focusExplanation;
this.ratings = ratings;
}
public Long getFocusId() {
return focusID;
}
public void setFocusId(Long focusID) {
this.focusID = focusID;
}
public String getFocusCategory() {
return focusCategory;
}
public void setFocusCategory(String focusCategory) {
this.focusCategory = focusCategory;
}
public String getFocusName() {
return focusName;
}
public void setFocusName(String focusName) {
this.focusName = focusName;
}
public String getFocusExplanation() {
return focusExplanation;
}
public void setFocusExplanation(String focusExplanation) {
this.focusExplanation = focusExplanation;
}
public Set<Rating> getRatings() {
return ratings;
}
public void setRatings(Set<Rating> ratings) {
this.ratings = ratings;
}
}
What am I missing that will allow a User's Ratings to be populated with the default value for each Focus that exists in the database?
Edit: Added #PrePersist and #PreUpdate to the Rating entity:
#Entity(name = "UserBaseRatings")
public class Rating {
#EmbeddedId
RatingKey id;
#ManyToOne
#MapsId("userID")
#JoinColumn(name="userID")
private User user;
#ManyToOne
#MapsId("focusID")
#JoinColumn(name="focusID")
private Focus focus;
private BigDecimal baseRating;
protected Rating(){}
public Rating(User user, Focus focus, BigDecimal baseRating) {
this.user = user;
this.focus = focus;
this.baseRating = baseRating;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Focus getFocus() {
return focus;
}
public void setFocus(Focus focus) {
this.focus = focus;
}
public BigDecimal getBaseRating() {
return baseRating;
}
public void setBaseRating(BigDecimal baseRating) {
this.baseRating = baseRating;
}
#PrePersist
public void prePersist() {
if(baseRating == null) {
baseRating = BigDecimal.ONE;
}
}
#PreUpdate
public void preUpdate() {
if(baseRating == null) {
baseRating = BigDecimal.ONE;
}
}
}
Changing spring.jpa.hibernate.ddl-auto= from none to update also made no difference.
If you use ORM all default values have to be explicitly declared. It can be done this way
#Entity(name = "UserBaseRatings")
public class Rating {
// ...
private BigDecimal baseRating = BigDecimal.ONE;
// ...
}
or use #PrePersist and #PreUpdate
#Entity(name = "UserBaseRatings")
public class Rating {
// ...
private BigDecimal baseRating;
// ...
#PrePersist
public void prePersist() {
if(baseRating == null) {
baseRating = BigDecimal.ONE;
}
}
#PreUpdate
public void preUpdate() {
if(baseRating == null) {
baseRating = BigDecimal.ONE;
}
}
}

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: Could not determine type for: java.util.List, at table: user, for columns: [org.hibernate.mapping.Column(roles)]

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

Creating a node in neo4j with one unique property other than ID

My Project is based on Spring boot + Neo4j .
I am trying to create a new Privilege node , but don't want to duplicate Privilege.
Now I have a UserRole node which is holds List<Privilege>. Now
I want that when I create a Privilege , it check first is another Privilege exists with same privilegeName property.
Below are my domain classes.
UserRole Class
#NodeEntity
public class UserRole {
public UserRole(User user, Role role) {
this.user = user;
this.role = role;
}
/**
For Jackson Parsing
**/
public UserRole() {
}
#GraphId
private Long id;
public UserRole(User user, Role role, Unit unit) {
this.user = user;
this.role = role;
this.unit = unit;
}
public long getId() {
return id;
}
#Relationship(type = HAS_USERROLE,direction = "OUTGOING")
User user;
public User getUser() {
return user;
}
#Relationship (type = HAS_ROLE_OF,direction = "OUTGOING")
Role role;
public Role getRole() {
return role;
}
#Relationship(type = "WORKS_IN",direction = "OUTGOING")
Unit unit;
public Unit getUnit() {
return unit;
}
public void setUnit(Unit unit) {
this.unit = unit;
}
#Relationship(type = "HAS_PRIVILEDGE", direction = "OUTGOING")
List<Priviledge> priviledgeList;
public List<Priviledge> getPriviledgeList() {
return priviledgeList;
}
public void setPriviledgeList(List<Priviledge> priviledgeList) {
this.priviledgeList = priviledgeList;
}
}
Privilege Class
#GraphId
Long id;
private String priviledge;
private String priviledgeOn;
private Long priviledgeOnId;
public Priviledge() {
}
public Priviledge(String priviledge, String priviledgeOn) {
this.priviledge = priviledge;
this.priviledgeOn = priviledgeOn;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getPriviledge() {
return priviledge;
}
public void setPriviledge(String priviledge) {
this.priviledge = priviledge;
}
public String getPriviledgeOn() {
return priviledgeOn;
}
public void setPriviledgeOn(String priviledgeOn) {
this.priviledgeOn = priviledgeOn;
}
public Long getPriviledgeOnId() {
return priviledgeOnId;
}
public void setPriviledgeOnId(Long priviledgeOnId) {
this.priviledgeOnId = priviledgeOnId;
}
}
I am Using GraphRepository to save Entities.
The only way to do this currently is to query for the Privilege existing first and then create it if not, or use it if it does. Also add a unique constraint to be safe.
In a future release, this use case will be supported.

[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