hoping I can get some assistance resolving a particular issue. I am basically trying to create a rest api endpoint that displays my one to one relationship between my User & UserProfile class. I am trying to do this using annotations but I'm not having any luck. I can't seem to return both the user info as well as associated profile info. Here are my classes
package com.account.service.model;
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import javax.persistence.*;
import java.util.Date;
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String email;
private Boolean active;
#JsonIgnore
private String password;
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
private Date created_at;
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
private Date updated_at;
#OneToOne(cascade = CascadeType.ALL, mappedBy = "user")
#JoinColumn(name = "user_id")
#JsonBackReference
private UserProfile userProfile;
public User(){}
public User(String email, Boolean active, String password, Date created_at, Date updated_at) {
this.email = email;
this.active = active;
this.password = password;
this.created_at = created_at;
this.updated_at = updated_at;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Boolean getActive() {
return active;
}
public void setActive(Boolean active) {
this.active = active;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Date getCreated_at() {
return created_at;
}
public void setCreated_at(Date created_at) {
this.created_at = created_at;
}
public Date getUpdated_at() {
return updated_at;
}
public void setUpdated_at(Date updated_at) {
this.updated_at = updated_at;
}
public UserProfile getUserProfile() {
return userProfile;
}
public void setUserProfile(UserProfile userProfile) {
this.userProfile = userProfile;
}
}
and here is the User Profile
package com.account.service.model;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import javax.persistence.*;
#Entity
#Table(name = "user_profiles")
public class UserProfile {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String first_name;
private String last_name;
#OneToOne(cascade = CascadeType.ALL, optional = false)
#JsonManagedReference
private User user;
public UserProfile(){}
public UserProfile(String first_name, String last_name, User user) {
this.first_name = first_name;
this.last_name = last_name;
this.user = user;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getFirst_name() {
return first_name;
}
public void setFirst_name(String first_name) {
this.first_name = first_name;
}
public String getLast_name() {
return last_name;
}
public void setLast_name(String last_name) {
this.last_name = last_name;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
The response I get is the following which is missing the profile association.
[
{
"id": 1,
"email": "ul#gmail.com",
"active": true,
"created_at": "2017-12-21",
"updated_at": "2017-12-21"
}
]
Any thoughts on what the issue could be?
The intention is to display the following:
[
{
"id": 1,
"email": "ul#gmail.com",
"active": true,
"created_at": "2017-12-21",
"updated_at": "2017-12-21",
"profile": {
"id": 1,
"first_name": "master",
"last_name": "splinter"
}
}
]
Figured it out, if anyone else is stuck on an issue like this please take a look at #JsonBackReference this should be used on your User class not on your Profile class.
Related
I have one Question entity in my Spring project, which is being sent as a response. Question entity has #ManyToOne relationship with User entity.
Question class
#Entity
public class Question {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int questionId;
#Column
private String question;
#ManyToOne
private User user;
#OneToMany
#JoinColumn(name = "question_id")
private List<Answer> answerList;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public List<Answer> getAnswerList() {
return answerList;
}
public void setAnswerList(List<Answer> answerList) {
this.answerList = answerList;
}
public int getQuestionId() {
return questionId;
}
public void setQuestionId(int questionId) {
this.questionId = questionId;
}
public String getQuestion() {
return question;
}
public void setQuestion(String question) {
this.question = question;
}
}
User class
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int userId;
private String userName;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
}
I am sending a response in a JSON format. Below is the example of my response for a single question.
{
"questionId": 1,
"question": "What is java?",
"user": {
"userId": 1,
"userName": "JeffHardy"
},
"answerList": [],
}
Just notice the user object in JSON. Whole user object is being sent as a response. I just want to return userId instead of user object. How can I achieve it?
You can ignore the fields with #JsonIgnoreProperties(value = { "fieldName" }) annotation on class level.
for example:
#Entity
#JsonIgnoreProperties(value = { "userName" })
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int userId;
private String userName;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
}
But I suggest to create customized classes for response jsons.
Here is my User entity
package org.scd.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.scd.model.security.Role;
import javax.persistence.*;
import java.io.Serializable;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
#Entity
#Table(name = "USERS")
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "FIRST_NAME", nullable = false, length = 45)
private String firstName;
#Column(name = "LAST_NAME", nullable = false, length = 45)
private String lastName;
#Column(name = "EMAIL", nullable = false, unique = true, length = 45)
private String email;
#Column(name = "PASSWORD", nullable = false, length = 256)
private String password;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "user_role", joinColumns = #JoinColumn(name = "user_id"), inverseJoinColumns = #JoinColumn(name = "role_id"))
private Set<Role> roles = new HashSet<>(0);
#OneToMany(mappedBy = "user",
fetch = FetchType.EAGER
)
private List<Position> positions;
public User() {
}
public User(String firstName, String lastName, String email, String password) {
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.password = password;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = 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 String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public void setPassword(String password) {
this.password = password;
}
public List<Position> getPositions() {
return positions;
}
public void setPositions(List<Position> positions) {
this.positions = positions;
}
#JsonIgnore
public String getPassword() {
return password;
}
#JsonProperty("password")
public String getHiddenPassword() {
return "****";
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
}
Here is my Position entity
package org.scd.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.format.annotation.DateTimeFormat;
import javax.persistence.*;
import java.time.LocalDate;
import java.util.Date;
#Entity
#Table(name = "POSITIONS")
public class Position {
#Id
#GeneratedValue(strategy =GenerationType.IDENTITY)
private Long id;
#Column(name = "START_DATE", nullable = false)
private LocalDate creationDate;
#Column(name = "LATITUDE", nullable = false, length = 45)
private String latitude;
#Column(name = "LONGITUDE", nullable = false, length = 45)
private String longitude;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "user_id", nullable = false, updatable = false)
#JsonIgnore
private User user;
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Position )) return false;
return id != null && id.equals(((Position) o).getId());
}
#Override
public int hashCode() {
return 31;
}
public Position() {
}
public Position(LocalDate creationDate, String latitude, String longitude, User user) {
this.creationDate = creationDate;
this.latitude = latitude;
this.longitude = longitude;
this.user = user;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public LocalDate getCreationDate() {
return creationDate;
}
public void setCreationDate(LocalDate creationDate) {
this.creationDate = creationDate;
}
public String getLatitude() {
return latitude;
}
public void setLatitude(String latitude) {
this.latitude = latitude;
}
public String getLongitude() {
return longitude;
}
public void setLongitude(String longitude) {
this.longitude = longitude;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
Here is the GET request that doesn't work. I can only send the startDate and endDate as a String, but I need them to be in Date format.
#GetMapping(path = "/byUserIdAndTimePeriod/{userId}/{startDate}/{endDate}")
public ResponseEntity<List<Position>> getPositionByUserAndTimePeriod(#PathVariable Long userId, #PathVariable Date startDate, #PathVariable Date endDate) {
return new ResponseEntity<>(new ArrayList<Position>(positionService.getPositionByUserAndTimePeriod(userId,startDate,endDate)),HttpStatus.OK);
}
This one is the updated GET request that works
#GetMapping(path = "/byUserIdAndTimePeriod/{userId}/{startDate}/{endDate}")
public ResponseEntity<List<Position>> getPositionByUserAndTimePeriod(#PathVariable Long userId,
#PathVariable #DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate,
#PathVariable #DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate) {
return new ResponseEntity<>(new ArrayList<Position>(positionService.getPositionByUserAndTimePeriod(userId,startDate,endDate)),HttpStatus.OK);
}
Here is the position service
#Override
public List<Position> getPositionByUserAndTimePeriod(Long userId, LocalDate startDate, LocalDate endDate) {
User user = new User();
user.setId(userId);
return positionRepository.findAllByUserAndCreationDateBetween(user,startDate,endDate);
}
Here is the position repository
List<Position> findAllByUserAndCreationDateBetween(User user, LocalDate startDate, LocalDate endDate);
Do you have any ideas as to how I should resolve this problem?
How should the GET request look? Should I modify some things in the service or other places?
Thanks for your time.
You have to add the #DateTimeFormat(pattern="yyy-MM-dd") annotation to the parameter:
#GetMapping(path = "/byUserIdAndTimePeriod/{userId}/{startDate}/{endDate}")
public ResponseEntity<List<Position>> getPositionByUserAndTimePeriod(#PathVariable Long userId,
#PathVariable Date startDate,
#PathVariable #DateTimeFormat(pattern="yyy-MM-dd") Date endDate) {
return new ResponseEntity<>(new ArrayList<Position>(positionService.getPositionByUserAndTimePeriod(userId,startDate,endDate)),HttpStatus.OK);
}
I'm creating Spring Boot Application with JPA PostgreSQL.
When I compile my spring project, got the following error.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: eveserver.core.entity.User.name in eveserver.core.entity.Role.users
Please, help me to understand what i'm doing wrong.
This is my User.java
package eveserver.core.entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Set;
#Entity
#Table(name = "users")
public class User implements Serializable {
#Id
#GeneratedValue
private Long id;
#Column(name = "name")
private String username;
#JsonIgnore
#Column(name = "password")
private String password;
#Column(name = "email")
private String email;
private boolean enabled = false;
#ManyToMany
#JoinTable(name = "user_role",
joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "role_id", referencedColumnName = "id"))
private Set<Role> roles;
public User(){ }
public User(String username, String password, String email) {
this.username = username;
this.password = password;
this.email = email;
}
public User(String username, String password, String email, Set<Role> roles) {
this.username = username;
this.password = password;
this.email = email;
this.roles = roles;
}
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 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 void enable(){
enabled = true;
}
public void disable(){
enabled = false;
}
}
Role.java
package eveserver.core.entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.security.core.GrantedAuthority;
import javax.persistence.*;
import java.util.Set;
#Entity
#Table(name = "role")
public class Role implements GrantedAuthority {
#Id
#GeneratedValue
private Long id;
private String name;
#ManyToMany(mappedBy = "role")
#JsonIgnore
private Set<User> users;
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;
}
#Override
public boolean equals(Object obj){
if (obj instanceof Role){
Role r = (Role)obj;
if (r.getId()==this.getId()){
return true;
}
}
return false;
}
public Set<User> getUsers() {
return users;
}
public void setUsers(Set<User> users) {
this.users = users;
}
#Override
public String getAuthority() {
return getName();
}
}
and this is my tables
In your User class, you have a property called roles:
public Set<Role> getRoles() {
return roles;
}
In your Role class, this:
#ManyToMany(mappedBy = "role")
#JsonIgnore
private Set<User> users;
should be:
#ManyToMany(mappedBy = "roles")
#JsonIgnore
private Set<User> users;
mappedBy = "something" is saying, effectively, "within this other entity, there's a property called something that gets a list of entities of this type (the current type that you're in when you use the #ManyToMany annotation). It is not specifying a type or class name like Role.
So If I have a User and a UserRole Table like soo..
User Class
package app.repo.User;
import javax.persistence.*;
import java.util.Set;
#Entity
#Table(name = "user")
public class User {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
#Version
private Long version;
#Column(nullable = false)
private String username;
#Column(nullable = false)
private String password;
#OneToMany(mappedBy = "user")
private Set<UserRole> roles;
protected User() {}
public User(String username, String password) {
this.username = username;
this.password = password;
}
#Override
public String toString() {
return String.format(
"User[id=%d, username='%s', password='%s']",
id, username, password);
}
public long getId() {
return id;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Set<UserRole> getRoles() {
return roles;
}
public void setRoles(Set<UserRole> roles) {
this.roles = roles;
}
}
UserRole Class
package app.repo.User;
import javax.persistence.*;
#Entity
#Table(name = "user_roles")
public class UserRole {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Version
private Long version;
#Column(name = "role_name")
private String roleName;
#ManyToOne
private User user;
public UserRole() {
}
public UserRole(String roleName, User user) {
this.roleName = roleName;
this.user = user;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public void setUser(User user) {
this.user = user;
}
}
The above example has a oneToMany relationship with UserRole and a ManyToOne Relationship with user.
My first question is... is it possible to save User and UserRole in one save like so...
userDao.save(user);
And second question is. How would I set that up in a JSON post call ? and how would this be done. This is what I am doing now
{
"userId":"1",
"userName":"RestMan",
"password":"happy",
"version":"1",
"email":"restman#gmail.com",
"enabled":"1",
"roles": {
{"user":"1","role_name":"ROLE_COOLGUY"}
}
}
Otherwise I am thinking to just create a Model that saves the two separately in one method
Change the annotation like this : #OneToMany(cascade = CascadeType.ALL)
I am using Hibernate with Spring in my project, I want to write a HQL query to get and update the role of user. How can I do that
This is my ERD:
this is my java classes:
User
#Entity
#Table(name = "users")
public class User implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "userId")
private int userId;
#Column(name = "userIdCardNo")
private String useridcardno;
#Column(name = "userFname")
private String fname;
#Column(name = "userMname")
private String mname;
#Column(name = "userLname")
private String lname;
#Column(name = "userPhone")
private int phone;
#Column(name = "userPhone2")
private String phone2;
#Column(name = "userAddress")
private String address;
#Column(name = "userAddress2")
private String address2;
#Column(name = "userName")
private String username;
#Column(name = "userPass")
private String password;
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(name = "users_roles", joinColumns = #JoinColumn(name = "userId", nullable = false) , inverseJoinColumns = #JoinColumn(name = "roleId", nullable = false) )
private List<Role> roles;
#Enumerated(EnumType.STRING)
#Column(name = "userStatus")
private UserStatus status;
//CREATE MD5 from String
public static String md5(String input) {
String md5 = null;
if (null == input)
return null;
try {
// Create MessageDigest object for MD5
MessageDigest digest = MessageDigest.getInstance("MD5");
// Update input string in message digest
digest.update(input.getBytes(), 0, input.length());
// Converts message digest value in base 16 (hex)
md5 = new BigInteger(1, digest.digest()).toString(16);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return md5;
}
//CONTRUSCTORS
public User() {
}
public User(int userId, String useridcardno, String fname, String mname, String lname, int phone, String phone2,
String address, String address2, String username, String password, List<Role> roles, UserStatus status) {
super();
this.userId = userId;
this.useridcardno = useridcardno;
this.fname = fname;
this.mname = mname;
this.lname = lname;
this.phone = phone;
this.phone2 = phone2;
this.address = address;
this.address2 = address2;
this.username = username;
this.password = BCrypt.hashpw(password, BCrypt.gensalt());
this.roles = roles;
this.status = status;
}
//GETTERS and SETTERS
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getUseridcardno() {
return useridcardno;
}
public void setUseridcardno(String useridcardno) {
this.useridcardno = useridcardno;
}
public String getFname() {
return fname;
}
public void setFname(String fname) {
this.fname = fname;
}
public String getMname() {
return mname;
}
public void setMname(String mname) {
this.mname = mname;
}
public String getLname() {
return lname;
}
public void setLname(String lname) {
this.lname = lname;
}
public int getPhone() {
return phone;
}
public void setPhone(int phone) {
this.phone = phone;
}
public String getPhone2() {
return phone2;
}
public void setPhone2(String phone2) {
this.phone2 = phone2;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getAddress2() {
return address2;
}
public void setAddress2(String address2) {
this.address2 = address2;
}
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 List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
public UserStatus getStatus() {
return status;
}
public void setStatus(UserStatus status) {
this.status = status;
}
}
Role
#Entity
#Table(name = "roles")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "roleId")
private int id;
#Column(name = "roleName")
private String roleName;
#ManyToMany(fetch = FetchType.EAGER, mappedBy = "roles")
private List<User> users;
public Role() {
}
public Role(int id, String roleName, List<User> users) {
super();
this.id = id;
this.roleName = roleName;
this.users = users;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
}
And UsersRoles
#Entity
#Table(name="users_roles")
public class UsersRoles {
#Id
#Column(name="userId")
private int userId;
#Id
#Column(name="roleId")
private int roleId;
public UsersRoles() {
}
public UsersRoles(int userId, int roleId) {
super();
this.userId = userId;
this.roleId = roleId;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public int getRoleId() {
return roleId;
}
public void setRoleId(int roleId) {
this.roleId = roleId;
}
}
I'm quite new to database queries since I have tried with for only a few times. I can get the data in one table, but for joining tables like this, I really need a hint.
Just load the user and modifiy the items of the role list. Then commit the transaction. - That's all.
EntityManager em ...
...
<begin Transaction>
...
User user = em.find(User.class, 1); //load by id 1 - just for example
User role1 = em.find(Role.class, 1); //load by id 1 - just for example
user.getRoles().add(role1);
...
<commit Transaction>
It is so simple because you use an ORM (Object Relational Mapper)(Hibernate). I almost all cases there is no need to write queries for updates, instead the state of the objects gets persisted.