No serializer found when serializing one Object - java

I'm trying to return an Object as JSON.
Using the /user/id endpoint, I want to display a User based on his Id.
When calling this controllerMethod I get the following Exception:
InvalidDefinitionException: No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: com.sample.scrumboard.models.User_$$_jvsta02_1["handler"])
My contollerClass looks like this:
#RestController
#RequestMapping(path="/user")
#JsonIgnoreProperties(ignoreUnknown = true)
public class UserRestController {
private UserRepository repository;
#Autowired
public UserRestController(UserRepository repository){
this.repository = repository;
}
#GetMapping(value = "/list")
public List<User> getUsers(){
return repository.findAll();
}
#GetMapping(value = "/{id}")
public #ResponseBody User getUserById(#PathVariable Long id, User user){
user = repository.getOne(id);
return user;
}
}
I checked if al fields have a public getter and tried various options with #JSONIgnoreProperties, but I can't find it.
Displaying all users as a JSONlist does work JSONlist with /user/list. So the problem is only there when trying to display one Object, not a list of Objects.
From the repository it does find the User, but it's unable to serialize that Object and put in on the screen.
The User class itself looks like this:
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "userId", nullable = false, updatable = false)
private Long id;
#NotNull
#Size(min=2, max=20)
private String firstName;
#NotNull
#Size(min=2, max=30)
private String lastName;
#NotNull
#Size(min=2, max=20)
private String userName;
#NotNull
#Size(min=2, max=30)
private String passWord;
#NotNull
#Email
private String email;
//the mappedBy element must be used to specify the relationship field or property of the entity that is the owner of the relationship
#OneToMany(mappedBy = "owner", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JsonIgnore
private List<UserStory> userStoryList;
public User() {
}
public User(String firstName, String lastName, String userName, String passWord, String email) {
this.firstName = firstName;
this.lastName = lastName;
this.userName = userName;
this.passWord = passWord;
this.email = email;
}
#Override
public String toString() {
return "User{" +
"id=" + id +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", userName='" + userName + '\'' +
", passWord='" + passWord + '\'' +
", email='" + email + '\'' +
'}';
}
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 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 List<UserStory> getUserStoryList() {
return userStoryList;
}
public void setUserStoryList(List<UserStory> userStoryList) {
this.userStoryList = userStoryList;
}
}
How can I display my User returned from /user/id?
A Solution?
As suggested below, I made it work using a Dto and ModelMapper.
I added
#Bean
public ModelMapper modelMapper(){
return new ModelMapper();
}
ControllerMethod
#GetMapping(value = "/{id}")
public UserDTO getUserById(#PathVariable Long id, User user, ModelMapper modelMapper){
user = repository.getOne(id);
return modelMapper.map(user, UserDTO.class);
}
And UserDto
public class UserDTO {
private Long id;
private String firstName;
private String lastName;
private String userName;
private String passWord;
private String email;
private List<UserStory> userStoryList;
//getters and setters
Now I'm able to show a User on the screen.
Still I'm wondering if there is no solution using Jackson and without modelmapper and dto?

Try adding the following line to your application.properties file :
spring.jackson.serialization.fail-on-empty-beans=false

Maybe it's not a good idea to use your entity (User) to expose the data about user via REST? Can you create UserDTO for your user that will implement Serializable and send this DTO via REST? In this case it should be necessary to convert User object that you've retrieved from the db to UserDTO.

Don't use #JsonIgnoreProperties(ignoreUnknown = true) in the controller class.
Use the below annotation in the entity class. That solved that issue.
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})

Annotation #JsonIgnoreProperties should not be on UserRestController, what you need to serialize is User, remove annotation is just ok. Jackson will help to do all the work, if you want to ignore some fields on class User, move #JsonIgnoreProperties to User class, and add #JsonProperty on filed need to display on the page.

Serializability of your orm classes implementing the java.io.Serializable interface
You are using #RestController, there is no need of #ResponseBody
Also there is no need to create a DTO class to transform the ORM class for the response json.
OBS: For the Bidirectional reationship you will use #JsonManagedReference, #JsonBackReference

There is a different between spring repository.getOne() and repository.findById(). With getOne() you can get a reference (proxy) e.g. if the object is already read in the same transaction. With findById() you always get the User as expected.

i am apply the
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
spring.jackson.serialization.fail-on-empty-beans=false
in my cause . it was not working fine.

Related

Native Query mapstruct

I am using native query and I get a Tuple with a set of login, email, pass, regDate.
I also created a class with these attributes (This is not an Entity and I don't need it).
Question: How can I get this class using mapstruct (DTO)
#Query("SELECT * FROM users .......", nativeQuery = true)
List<Tuple> getInfo();
#Data
public class UserPro {
String login;
String email;
String pass;
Date regDate;
}
You are almost there but you have some misunderstood concept. If i understood what you are asking...
The "getInfo" query has to obtain every "user" it receives from the query. Everything it receives it's been saved into a "Tuple" List, I suppose Tuple is a model or dto. If you want to save what you receive from that query you have to create a model, not a dto and you may call it "user".
Dtos are classes made to work with data relationated to a Http request, not a query.
Models are classes made to work with data relationated to a database, including database queries.
Inside that "user" model you have to instantiate every column you get from the database (be sure to name the #Column annotation variable the same each column is named on the database and give them the correct data type as in the Database).
Keep in mind that every user has an unique identifier, it doesn't have to be called ID, but you have to know who that unique identifier is.
After instantiating every variable you have to instantiate the getters and setters and a constructor instantiating every variable.
Example:
#Entity
#Table(name = "TABLE_NAME")
public class User implements Serializable
{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "PUT_HERE_YOUR_UNIQUE_INDENTIFIER_HERE")
private "UNIQUE_INDENTIFIER_DATA_TYPE" id;
#Column(name = "LOGIN_COLUMN_NAME")
private String login;
#Column(name = "EMAIL_COLUMN_NAME")
private String email;
#Column(name = "PASSWORD_COLUMN_NAME")
private String pass;
#Column(name = "REGDATE_COLUMN_NAME", nullable = false)
private Date regDate;
//default constructor
public User() {}
//user constructor
public User()
{
this.id = id;
this.login = login;
this.email = email;
this.pass = pass;
this.regDate = regDate;
}
public Long getId()
{
return id;
}
public void setId(Long id)
{
this.id = id;
}
public String getLogin()
{
return login;
}
public void setLogin(String login)
{
this.login = login;
}
public String getEmail()
{
return email;
}
public void setEmail(String email)
{
this.email = email;
}
public String getPass()
{
return pass;
}
public void setPass(String pass)
{
this.pass = pass;
}
public Date getRegDate()
{
return regDate;
}
public void setRegDate(Date regDate)
{
this.regDate = regDate;
}
}

Is it possible to create a newUser from the dto and User class?

I'm trying to create a registration form that uses fields from my DTO and my User class. I'm not exactly sure to go about it and could use some help.
I have a User class that looks like this:
#Entity
public class User extends AbstractEntity {
#NotNull
private String username;
#NotNull
private String pwHash;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "email")
private String email;
#Column(name = "phone_number")
private String phoneNumber;
public User() {}
public User(String username, String password) {
this.username = username;
this.pwHash = encoder.encode(password);
}
public String getUsername() {
return username;
}
private static final BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
public boolean isMatchingPassword(String password) {
return encoder.matches(password, pwHash);
}
// ---> Removed Getters and setters removed for brevity.
The registration form originally was set up to ask for a username/password pair, and then ask the user to confirm the password by typing it in again. So the associated DTO extended the LoginFormDTO and added an additional field for password verification. When I only asked for a username and password I was able to complete the registration and update those columns in my database. However now I'd like to add additional user information during registration.
My data transfer object has been updated to look like this... (I've tried this with and without the #Column annotations.
public class RegisterFormDTO extends LoginFormDTO{
private String verifyPassword;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "email")
private String email;
#Column(name = "phone_number")
private String phoneNumber;
public String getVerifyPassword() {
return verifyPassword;
}
public void setVerifyPassword(String verifyPassword) {
this.verifyPassword = verifyPassword;
}
public String getFirstName() {
return firstName;
}
// additional pojo getters and setters
My LoginFormDTO looks like this...
public class LoginFormDTO {
#NotNull
#NotBlank
#Size(min = 3, max = 20, message = "Invalid username. Must be between 3 and 30 characters.")
private String username;
#NotNull
#NotBlank
#Size(min = 5, max = 20, message = "Invalid password. Must be between 5 and 30 characters.")
private String password;
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;
}
}
My AuthenticationController's registration method looks like this
#PostMapping("/register")
public String processRegistrationForm(#ModelAttribute #Valid RegisterFormDTO registerFormDTO,
Errors errors, HttpServletRequest request,
Model model) {
if (errors.hasErrors()) {
model.addAttribute("title", "Register");
return "register";
}
User existingUser = userRepository.findByUsername(registerFormDTO.getUsername());
if (existingUser != null) {
errors.rejectValue("username", "username.alreadyexists", "A user with that username already exists");
model.addAttribute("title", "Register");
return "register";
}
String password = registerFormDTO.getPassword();
String verifyPassword = registerFormDTO.getVerifyPassword();
if (!password.equals(verifyPassword)) {
errors.rejectValue("password", "passwords.mismatch", "Passwords do not match");
model.addAttribute("title", "Register");
return "register";
}
User newUser = new User(registerFormDTO.getUsername(), registerFormDTO.getPassword(), registerFormDTO.getFirstName(), registerFormDTO.getLastName(), registerFormDTO.getPhoneNumber(), registerFormDTO.getEmail());
userRepository.save(newUser);
setUserInSession(request.getSession(), newUser);
return "redirect:";
}
I'm not sure how to create a new user from the dto and the user class. I'm able to set the username and password but can't seem to add user info like firstName, lastName, phoneNumber or email. The database adds the id, pw_hash and username. What is the best way to go adding these fields so that they show up in the database? Thank you in advance. I'm learning so much from this helpful community!
How about extending RegisterFormDTO class with fields: firstName, lastName, phoneNumber, email?
Then, your User class should have another constructor that supports more than username and password. In this case to limit the number of arguments in the constructor you can use a builder design pattern.
You can read more here: Managing constructors with many parameters in Java
And one more thing about naming convention, if your name contains uppercase shortcut that is longer than two characters you should proceed with PascalCase: RegisterDTO -> RegisterDto.
See: Java Naming Convention with Acronyms

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 Boot REST - How to implement a search that requires a Java object?

I have the following JPA models:
Issue
#Entity
public class Issue {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
private String title;
private String text;
#ManyToOne
#JoinColumn(name="user_id")
private User user;
public Issue() {}
public Issue(String title, String text) {
this.title = title;
this.text = text;
}
public long getId() {
return id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
#Override
public String toString() {
return "Issue [id=" + id + ", title=" + title + ", text=" + text + ", user=" + user + "]";
}
}
User
#Entity
public class User {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
private String username;
private String firstname;
private String lastname;
public User() {}
public User(String username) {
this.username = username;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
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;
}
#Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", firstname=" + firstname + ", lastname=" + lastname
+ "]";
}
}
And an Issue repository that extends PagingAndSortingRepository and contains the method List<Issue> findByUser(User user); See below:
public interface IssueRepository extends PagingAndSortingRepository<Issue,Long> {
List<Issue> findByUser(User user);
}
I'm trying to find a way to navigate these relationships with HTTP calls, namely how do I call findByUser(User user) and get all the issues for that user?
Using the following call I can execute that particular query:
GET http://localhost:8080/issues/search/findByUser
But I'm unclear what I should be providing as the User? Do I send the id as a query param? Do I construct an object and send that as a query param? Am I just modeling this the wrong way?
I'd like to get back a JSON list containing all the Issues for this particular User.
Thanks in advance for any help or guidance.
Changing the repository to this solved the issue. The key is to do the lookup based on a field of the User, not the User itself.
public interface IssueRepository extends PagingAndSortingRepository<Issue,Long> {
List<Issue> findByUserUsername(#Param("username") String username);
}
GET http://localhost:8080/issues/search/findByUserUsername?username=jerney
This returns a list of issues.
put another read only column like
#Column(name = "user_id", insertable = false, updatable = false)
private Long userId;
in Issue entity and use findByUserId(Long userId) repo method to find it and pass userId parameter(i.e path varible) to controller to do this using http calls.
You can use a simple findOne(Long userId) if you need only one record as its probably faster than query by string field

Spring Restcontroller not returning xml

I'm getting json response from my controller even though I added the xml annotations to my model. I get the list of users in json with no problems. Can I use #Entity and #XmlRootElement in the same class?
User.java
#Entity
#Table(name = "usr")
#XmlRootElement
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID", nullable = false, unique = true, length = 11)
private int id;
#Column(nullable = false)
private String username;
#Column(nullable = false)
private String password;
#Column(nullable = false)
private String firstName;
#Column(nullable = false)
private String lastName;
#Column(nullable = false)
private String email;
#OneToMany(mappedBy = "user")
private List<Post> post;
#OneToMany(mappedBy = "user")
private List<Friend> friends;
public List<Friend> getFriends() {
return friends;
}
public void setFriends(List<Friend> friends) {
this.friends = friends;
}
public List<Post> getPost() {
return post;
}
public void setPost(List<Post> post) {
this.post = post;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#XmlElement
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;
}
#XmlElement
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
#XmlElement
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
#XmlElement
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
getAllUsers
#Override
public List<User> getAllUsers() {
List<User> users = new ArrayList<User>();
Session session = HibernateUtil.getSessionAnnotationFactory().openSession();
Transaction trns = null;
try {
trns = session.beginTransaction();
users = session.createQuery("select firstName, lastName, email, username as name from User").list();
trns.commit();
} catch (RuntimeException e) {
if (trns != null) {
trns.rollback();
}
e.printStackTrace();
} finally {
session.flush();
session.close();
}
return users;
}
and in the controller
#RequestMapping(value="/api/users", method = RequestMethod.GET)
public List<User> getAllUsers() {
//List<User> users = new ArrayList<User>();
UserDao userdao = new UserDaoImpl();
List<User> users = userdao.getAllUsers();
return users;
}
What am I missing here?
There is a nice blog entry from Spring about this. From there is this about content negotiation:
Enabling Content Negotiation in Spring MVC
Spring supports a couple of conventions for selecting the format
required: URL suffixes and/or a URL parameter. These work alongside
the use of Accept headers. As a result, the content-type can be
requested in any of three ways. By default they are checked in this
order:
Add a path extension (suffix) in the URL. So, if the incoming URL is
something like http://myserver/myapp/accounts/list.html then HTML is
required. For a spreadsheet the URL should be
http://myserver/myapp/accounts/list.xls. The suffix to media-type
mapping is automatically defined via the JavaBeans Activation
Framework or JAF (so activation.jar must be on the class path).
A URL parameter like this: http://myserver/myapp/accounts/list?format=xls.
The name of the parameter is format by default, but this may be
changed. Using a parameter is disabled by default, but when enabled,
it is checked second.
Finally the Accept HTTP header property is
checked. This is how HTTP is actually defined to work, but, as
previously mentioned, it can be problematic to use.
But if you just want to fix the return type I'd add the annotation #RequestMapping(value="/api/users", produces={"application/xml"}) to your controller method.
First, try to change
#RequestMapping(value="/api/users", method = RequestMethod.GET)
to
#ResponseBody
#RequestMapping(value="/api/users", method = RequestMethod.GET, produces = MediaType.APPLICATION_XML_VALUE)
Second, check out if client code ( I assume it is javascript ) is sending correct accept-type, because Jackson - the default serialization engine would derive format of response body from HTTP header.
Third, ensure that you have JAXB present in your classpath.

Categories