For some strange reason a field of my entity has been changed to NULL in my resource.
User Entity
The User entity contains: userName, firstName, lastName, email. The JerseyTest instantiates a new User object:
newUser = new User();
newUser.setEmail(getEmail());
newUser.setFirstName("Erwin");
newUser.setLastName("Eberhard");
newUser.setUserName("abc");
Send entity in test
Before sending the data, the log in the test tells me:
[main] INFO feature.AccountStepDefinition - Erwin Eberhard erwin
erwineberhard#gmail.com
Everthing seems to be OK, so the test sends the data to the resource:
logger.info(newUser.toString());
responseUser = target("auth/register").request().post(Entity.json(newUser), User.class);
AccountResource
The AccountResource retrieves the user, and the log tells us:
[qtp1533672820-20] INFO nl.bolt.ffinschrijven.controllers.AccountsController
- Erwin Eberhard null erwineberhard#gmail.com
For some reason the username has been changed in NULL.
Method in AccountResource
#POST
#Path("/register")
#ApiOperation(value = "Register user.", notes = "After registration a JWT token has been added as header value.")
#ApiResponse(code = 200, message = "", response = User.class, responseHeaders = #ResponseHeader(name = "X-FFI-AUTH", description = "Token generated after authentication", response = String.class) )
#Consumes("application/json")
public Response callback(User user) {
logger.info(user.toString());
ServiceResult serviceResult = accountService.register(user);
if (serviceResult.serviceState == ServiceState.success) {
String jwt = "Bearer " + JwtHelper.createJWT(UUID.randomUUID().toString(), ApiConfiguration.issuer,
user.getEmail(), Long.valueOf(ApiConfiguration.token_ttl));
return Response.status(201).entity(serviceResult.content).header("Authorization", jwt).build();
}
if (serviceResult.serviceState == ServiceState.invalid) {
return Response.status(400).entity(serviceResult.responseMessages.toString()).build();
}
return Response.status(500).build();
}
Postman
When I send the data with POSTMAN, there is no problem:
Headers
Content-Type application/json
Raw content
{
"firstName": "Erwin",
"lastName" : "Eberhard",
"email" : "erwin#gmail.com",
"userName": "erwineberhard"
}
How to get the beloved 201 with my JerseyTest?
UPDATE 1
User.class
The User class extends from a generic User class:
#Entity
#Table
public class User extends GenericUser {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#OneToMany(mappedBy="user", cascade=CascadeType.ALL)
private List<Eventcomponent> eventcomponents =
new ArrayList<Eventcomponent>();
public Long getId() {
return id;
}
#Override
public String toString(){
return this.getFirstName() + " " + this.getLastName() + " " + this.getUserName()
+ " " + this.getEmail();
}
}
GenericUser.class
#MappedSuperclass
public abstract class GenericUser implements IEmail{
#NotNull(message="Gebruikersnaam niet meegegeven.")
#Column(name="userName", nullable = false)
protected String userName;
#NotNull(message="Email niet meegegeven.")
#Column(name="email", nullable = false)
protected String email;
#NotNull(message="Voornaam niet meegegeven.")
#Column(name="firstName", nullable = false)
protected String firstName;
#NotNull(message="Achternaam niet meegegeven.")
#Column(name="lastName", nullable = false)
protected String lastName;
#Column(name="locked", nullable = false)
protected Boolean locked;
protected 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 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 Boolean getLocked() {
return locked;
}
public void setLocked(Boolean locked) {
this.locked = locked;
}
}
Looks like the protected getUserName() is the problem. The serializer can't find the userName property when serializing, so there is no field in the JSON of that property name, when it goes out. When the server gets the JSON there is no "userName" field, so it stays null in the server side object. To fix it, just make the method public.
Related
Doing a project with parcel service. I created OrderItem API and Dispatcher API. Now, I want to connect then by relations. The idea is: dispatcher can have many orderItems. OrderItem can only have one dispatcher. If you delete dispatcher, his order items also has to go out.
I have already created a little bit, but I'm so messed up here and can't finish this thing logically. Would someone give me some ideas on how I should attack this problem.
Do I need to put relations both sides or only to one of them?
When do I need to create constructors with arguments? Because in entity class you have to have no arg constructors...?
OrderItem class:
#Entity
public class OrderItem {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#NotBlank(message = "Order weight is required")
private String weight;
#NotBlank(message = "Order dimensions are required")
private String dimensions;
#NotBlank(message = "Order origin is required")
private String origin;
#NotBlank(message = "Order destination is required")
private String destination;
#NotNull(message = "Order comment cannot be null")
private String comment;
#ManyToOne
private Dispatcher dispatcher;
public OrderItem() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getWeight() {
return weight;
}
public void setWeight(String weight) {
this.weight = weight;
}
public String getDimensions() {
return dimensions;
}
public void setDimensions(String dimensions) {
this.dimensions = dimensions;
}
public String getOrigin() {
return origin;
}
public void setOrigin(String origin) {
this.origin = origin;
}
public String getDestination() {
return destination;
}
public void setDestination(String destination) {
this.destination = destination;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public Dispatcher getDispatcher() {
return dispatcher;
}
public void setDispatcher(Dispatcher dispatcher) {
this.dispatcher = dispatcher;
}
}
OrderController class:
#RestController
#RequestMapping("/order")
public class OrderController {
#Autowired
OrderService service;
#Autowired
private MapValidationErrorService mapValidationErrorService;
#GetMapping("/{dispatcherId}/orders")
public List<OrderItem> getAllOrderItems(#PathVariable int dispatcherId) {
return service.getAllOrderItems(dispatcherId);
}
#PostMapping("/{dispatcherId}/orders")
public ResponseEntity<?> saveOrder(#Valid #RequestBody OrderItem orderItem, #PathVariable int dispatcherId, BindingResult result) {
ResponseEntity<?> errorMap = mapValidationErrorService.MapValidationService(result);
if (errorMap != null) {
return errorMap;
}
orderItem.setDispatcher(new Dispatcher(dispatcherId, "", "", ""));
service.insertOrUpdate(orderItem);
return new ResponseEntity<String>("Order was created successfully", HttpStatus.CREATED);
}
#PutMapping("/update")
public ResponseEntity<?> updateOrder(#Valid #RequestBody OrderItem orderItem, BindingResult result) {
ResponseEntity<?> errorMap = mapValidationErrorService.MapValidationService(result);
if (errorMap != null) {
return errorMap;
}
service.insertOrUpdate(orderItem);
return new ResponseEntity<String>("Order was updated successfully", HttpStatus.OK);
}
#GetMapping("/all")
public Iterable<OrderItem> getAllOrders() {
return service.findAllOrders();
}
#DeleteMapping("/{orderId}")
public ResponseEntity<String> deleteOrder(#PathVariable int orderId) {
if (service.findById(orderId) == null) {
throw new CustomErrorException("Order doesn't exist, check order id");
}
service.deleteOrder(orderId);
return new ResponseEntity<String>("Order with ID " + orderId + " was deleted", HttpStatus.OK);
}
#GetMapping("/{orderId}")
public ResponseEntity<OrderItem> getOrderById(#PathVariable int orderId) {
OrderItem item = service.findById(orderId);
if (service.findById(orderId) == null) {
throw new CustomErrorException("Order id not found - " + orderId);
}
return new ResponseEntity<OrderItem>(item, HttpStatus.OK);
}
}
Dispatcher class:
#Entity
public class Dispatcher {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#NotBlank(message = "Dispatcher first name is required")
private String firstName;
#NotBlank(message = "Dispatcher last name is required")
private String lastName;
#NotBlank(message = "Dispatcher email name is required")
private String email;
#NotBlank(message = "Dispatcher email is required")
private String password;
#NotBlank(message = "Dispatcher phone number is required")
private String phoneNumber;
public Dispatcher() {
}
public Dispatcher(int id, String firstName, String lastName, String email) {
super();
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
public int getId() {
return id;
}
public void setId(int 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 String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
}
DispatcherController class:
#RestController
#RequestMapping("/dispatcher")
public class DispatcherController {
#Autowired
DispatcherService service;
#Autowired
private MapValidationErrorService mapValidationErrorService;
#PostMapping("/save")
public ResponseEntity<?> saveDispatcher(#Valid #RequestBody Dispatcher dispatcher, BindingResult result) {
ResponseEntity<?> errorMap = mapValidationErrorService.MapValidationService(result);
if (errorMap != null) {
return errorMap;
}
service.insertOrUpdate(dispatcher);
return new ResponseEntity<String>("Dispatcher was created successfully", HttpStatus.CREATED);
}
#GetMapping("/all")
public Iterable<Dispatcher> getAllDispatchers() {
return service.findAllDispatchers();
}
#GetMapping("/{dispatcherId}")
public ResponseEntity<?> getDispatcherById(#PathVariable int dispatcherId) {
Dispatcher dispatcher = service.findById(dispatcherId);
if (service.findById(dispatcherId) == null) {
throw new CustomErrorException("Dispatcher id not found - " + dispatcherId);
}
return new ResponseEntity<Dispatcher>(dispatcher, HttpStatus.OK);
}
#DeleteMapping("/{dispatcherId}")
public ResponseEntity<?> deleteDispatcher(#PathVariable int dispatcherId) {
if (service.findById(dispatcherId) == null) {
throw new CustomErrorException("Dispatcher doesn't exist, check dispatcher id");
}
service.deleteDispatcher(dispatcherId);
return new ResponseEntity<String>("Order with ID " + dispatcherId + " was deleted", HttpStatus.OK);
}
#PutMapping("/update")
public ResponseEntity<?> updateDispatcher(#Valid #RequestBody Dispatcher dispatcher, BindingResult result) {
ResponseEntity<?> errorMap = mapValidationErrorService.MapValidationService(result);
if (errorMap != null) {
return errorMap;
}
service.insertOrUpdate(dispatcher);
return new ResponseEntity<String>("Dispatcher was updated successfully", HttpStatus.OK);
}
}
I think you have defined the relationship incorrectly. And yes you need to have no-args constructor. This helps hibernate to map the values from database to java objects when retrieving data from the database
Assuming you are going for a uni-directional mapping,
#Entity
public class OrderItem {
#ManyToOne( cascade = CascadeType.ALL )
#JoinColumn(name = <foriegn_key_column in orderItem table i.e. id>)
private Dispatcher dispatcher;
}
#Entity
public class Dispatcher {
private List<OrderItem > orders;
}
The main difference is that bidirectional relationship gives you access in both directions. so that you can access the other side without any queries. It works for cascade actions too.
The bidirectional #OneToMany generates better DML because the #ManyToOne owns the relationship.
Unidirectional #ManyToOneor bidirectional #OneToMany are more efficient than unidirectional #OneToMany.
Before JPA 2.0 this unidirection #OneToMany used a join table to manage the association between parent and child rows. So higher cost in read (3 tables join) and write (3 tables insertion).
Since JPA 2.0 for unidirectional #OneToMany you should use it in correlation with #JoinColumn
With the #JoinColumn the #OneToMany association controls the child table FK.. and so no need for extra junction table.
But performance wise there is no better than bidirectional associations.
Pros of unidirectional #OneToMany -> simplicity.
For your second question : NoArg is required only by the persistence framework (Hibernate for e.g). But you can (and should) use your own constructors to create consistent objects.
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
I have faced with a problem that I can't get my object on server side.
I am getting this error:
128870 [http-apr-8080-exec-1] WARN org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver - Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing: public com.webserverconfig.user.entity.User com.webserverconfig.user.controller.UserController.login(com.webserverconfig.user.entity.User)
I am trying to send object on server side using GET request. I want to use this object just to verify fields on it. (I am doing simple user login method and i want to check userName and userPassword).
Here is my code on server side:
Request:
#RequestMapping(value = "/login", method = RequestMethod.GET)
#ResponseStatus(value = HttpStatus.ACCEPTED)
public User login(#RequestBody User user) {
userValidator.validateUserLogin(user);
securityService.autoLogin(user.getUserName(), user.getUserPassword());
return user;
}
Entity user:
#Entity
#Table(name = "Users")
public class User {
public User() {
}
#Id
#GeneratedValue(generator = "increment")
#GenericGenerator(name = "increment", strategy = "increment")
#Column(name = "Id", unique = true, nullable = false)
private long id;
#Column(name = "userName", nullable = false)
private String userName;
#Column(name = "userPassword", nullable = false)
private String userPassword;
#Transient
private String confirmPassword;
public long getId() {
return id;
}
public String getUserName() {
return userName;
}
public String getUserPassword() {
return userPassword;
}
public String getConfirmPassword() {
return confirmPassword;
}
public void setId(long id) {
this.id = id;
}
public void setUserName(String userName) {
this.userName = userName;
}
public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
}
public void setConfirmPassword(String confirmPassword) {
this.confirmPassword = confirmPassword;
}
}
Client side code:
private class LoginUserTask extends AsyncTask<Void, Void, User> {
#Override
protected User doInBackground(Void... voids) {
restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
User user = new User(userName, userPassword);
return restTemplate.getForObject(URL.getUserLogin(), User.class, user);
}
#Override
protected void onPostExecute(User user) {
responseEntity = restTemplate.getForEntity(URL.getUserLogin(), String.class);
HttpStatus responseStatus = responseEntity.getStatusCode();
if(responseStatus.equals(HttpStatus.ACCEPTED)){
view.makeToast("User login completed " + user.getUserName());
} else {
view.makeToast("User login or password is not correct");
}
}
}
Am I missing something? Can anybody help with this please ?
You have set a #RequestBody annotation with a User object as an input parameter.
In this case, you have to use POST method along with a User object in the body of the request.
I have done this is the past with mySQL but I need to use Oracle:
This is a very simple register user:
application.properties
#Oracle database setup
spring.datasource.url=jdbc:oracle:thin:#999.999.999.11:1521:d3SID
spring.datasource.username=userName
spring.datasource.password=password
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver
server.port = 4000
UserInformation model
#Entity
public class UserInformation {
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
private Long id;
#Column(name = "firstName")
#Min(2) #Max(15)
#NotNull
private String firstName;
#Column(name = "lastName")
private String lastName;
#Column(name = "userName")
private String userName;
#Column(name = "password")
private String password;
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;
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof UserInformation)) {
return false;
}
UserInformation that = (UserInformation) o;
return id.equals(that.id);
}
#Override
public int hashCode() {
return id.hashCode();
}
#Override
public String toString() {
return "Applicant{" + "firstName='" + firstName + '\'' + ", lastName='" + lastName + '\'' + ", userName='" + userName + '\'' + ", password='" +
password + '\'' + '}';
}
}
JPA repo
public interface UserLoginRepo extends JpaRepository<UserInformation, Long> {}
Controller method
#RequestMapping(value = "/register", method = RequestMethod.POST)
public UserInformation registerUser(#RequestBody UserInformation user){
return userService.save(user);
}
When I run SELECT * FROM USERINFORMATION; nothing displays.
From my understanding I should not need to set up JPA config since I am doing it in applications.properties.
Did you check if the UserInformation object is coming from request body? As I know you shouldn't use "#RequestBody", just put "UserInformation user" must be in the parameters.
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.