Single variable update of an Entity using a PUT request - java

I have a User entity as follows:
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Integer id;
#Column(name = "name")
private String name;
#Column(name = "age")
private Integer age;
#NotNull
#Size(min = 5, max = 50)
#Pattern(regexp = RegexConstants.EMAIL_REGEX, message = ValidationMessages.EMAIL_VALIDATION_MESSAGE)
#Column(name = "email", unique = true)
private String email;
#NotNull
#Size(min = 5, max = 50)
#Column(name = "password")
private String password;
#NotNull
#Size(min = 5, max = 50)
#Column(name = "country_code")
private String countryCode;
/* Getters and Setters */
}
I want to update only the country code of the user.
I want to use the PUT request as that seems to be the an appropriate choice (don't hate me for using the word appropriate here, I might be wrong) but it seems like an over-kill to send the user in the request body as I do not intend to update anything but the country code.
I actually do not even have the whole user when I am making a request. The user body (#RequestBody user) only contains name and country code.
So, based on what I've read about PUT requests, I need to send the complete user object in the request which I do not have. Now I can accomplish this by passing the country code simply in the URL but all the articles I have read on RESTful web services recommend against it.
Current Code:
#RequestMapping(value = "/user/{id}", method = RequestMethod.PUT)
public ResponseEntity<User> updateUser(#PathVariable("id") long id, #RequestBody User user) {
System.out.println("Updating User " + id);
User currentUser = userService.findById(id);
if (currentUser==null) {
System.out.println("User with id " + id + " not found");
return new ResponseEntity<User>(HttpStatus.NOT_FOUND);
}
currentUser.setCountryCode(user.getCountryCode);
userService.updateUser(currentUser);
return new ResponseEntity<User>(currentUser, HttpStatus.OK);
}
I would really appreciate if someone can guide to the right way to do this. I have read about the PATCH request but I am not sure if that is the way to go in my case.
If I am doing it wrong it would really helpful if you can also provide an example or give me a starting point as to how to take this in the correct format.

Related

Why this Spring Data JPA query by name method gives me this error and Eclipse forces me to return an Optional<User> instead a simple User object?

I am working on a Spring Boot project using Spring Data JPA and Hibernate mapping. In my repository classes I am using a query by method name approach and I have the following question. I have this User entity class:
#Entity
#Table(name = "portal_user")
#Getter
#Setter
public class User implements Serializable {
private static final long serialVersionUID = 5062673109048808267L;
#Id
#Column(name = "id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
#Column(name = "first_name")
#NotNull(message = "{NotNull.User.firstName.Validation}")
private String firstName;
#Column(name = "middle_name")
private String middleName;
#Column(name = "surname")
#NotNull(message = "{NotNull.User.surname.Validation}")
private String surname;
#Column(name = "sex")
#NotNull(message = "{NotNull.User.sex.Validation}")
private char sex;
#Column(name = "birthdate")
#NotNull(message = "{NotNull.User.birthdate.Validation}")
private Date birthdate;
#Column(name = "tax_code")
#NotNull(message = "{NotNull.User.taxCode.Validation}")
private String taxCode;
#Column(name = "e_mail")
#NotNull(message = "{NotNull.User.email.Validation}")
private String email;
#Column(name = "pswd")
#NotNull(message = "{NotNull.User.pswd.Validation}")
private String pswd;
#Column(name = "contact_number")
#NotNull(message = "{NotNull.User.contactNumber.Validation}")
private String contactNumber;
#Temporal(TemporalType.DATE)
#Column(name = "created_at")
private Date createdAt;
#Column(name = "is_active")
private boolean is_active;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "user", orphanRemoval = true)
#JsonManagedReference
private Set<Address> addressesList = new HashSet<>();
#ManyToMany(cascade = { CascadeType.MERGE })
#JoinTable(
name = "portal_user_user_type",
joinColumns = { #JoinColumn(name = "portal_user_id_fk") },
inverseJoinColumns = { #JoinColumn(name = "user_type_id_fk") }
)
Set<UserType> userTypes;
#ManyToOne(fetch = FetchType.EAGER)
#JsonProperty("subagent")
private User parent;
public User() {
super();
// TODO Auto-generated constructor stub
}
public User(String firstName, String middleName, String surname, char sex, Date birthdate, String taxCode,
String email, String pswd, String contactNumber, Date createdAt, boolean is_active) {
super();
this.firstName = firstName;
this.middleName = middleName;
this.surname = surname;
this.sex = sex;
this.birthdate = birthdate;
this.taxCode = taxCode;
this.email = email;
this.pswd = pswd;
this.contactNumber = contactNumber;
this.createdAt = createdAt;
this.is_active = is_active;
}
}
Then I have this repository interface:
public interface UsersRepository extends JpaRepository<User, Integer> {
/**
* Retrieve an user by its e-mail address:
* #param email is the e-mail of the user
* #return the user related to the specified e-mail
*/
User findByemail(String email);
/**
* Retrieve the list of users belonging to a specific user type
* #param typeName is the name of the user type
* #return the list of users belonging to a specific user type
*/
List<User> findByUserTypes_TypeName(String typeName);
/**
* Retrieve the list of users children of an user.
* Generally used to retrieve the client users of a specific sub-agent user
* #param id is the id of the parent user
* #return the list of children of a specific user (generally used to retrieve the client users of a specific sub-agent user)
*/
List<User> findByParent_Id(Integer id);
/**
* Retrieve an user starting from is id
* #param id of the user which you want to retrieve
* #return the retrieved object
*/
Optional<User> findById(Integer id);
}
Om my doubt is on the last method (intended to retrieve an User object by its own id field value, this one:
Optional<User> findById(Integer id);
Originally I tried to define it simply as:
User findById(Integer id);
But doing in this way Eclipse\STS give me the following error:
Multiple markers at this line
- implements org.springframework.data.repository.CrudRepository<com.easydefi.users.entity.User,java.lang.Integer>.
findById
- The return type is incompatible with CrudRepository<User,Integer>.findById(Integer)
- The return type is incompatible with CrudRepository<User,Integer>.findById(Integer)
Why this error? and why Eclipse force me to return Optional<User> as returned type? What is this Optional? And why in the findByemail() allow me to return a simple User type?
As you can see in the JavaDoc of CrudRepository interface which JpaRepository extends, there is already a findById method with the same signature.
Java overriding rules do not allow you to have a method with the same name and parameters and a return type that is not consistent with the parent class definition (in this case, Optional<User>).
If you somehow change the method name, Spring Data JPA will be able to use a plain reference. In this case, null will be returned if the entity hasn't been found. For you case however, you should just drop your findById method.
Your repository extends JpaRepository. JpaRepository extends PagingAndSortingRepository that extends CrudRepository. CrudRepository has the method: Optional<T> findById(ID var1);.
https://docs.spring.io/spring-data/jpa/docs/current/api/org/springframework/data/jpa/repository/JpaRepository.html
When you define a method with this signature you are overriding the CrudRepository method. But if you define a different return type for this method created in your repository, your code won't compile.
Optional was created in Java 8, in resume, is used to indicate that the return data of method may be null. Is a good practice to use Optional for these scenarios.
https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html
About another methods that you mentioned, they are just methods of your UsersRepository that you can create as you want.
Alexy and Raul already gave good explanations why this doesn't work.
But the options the offer for avoiding the problem is rather limited.
Here are the most relevant options you have:
omit your findById method and just use the one offered by the CrudRepository. Works but it seems you don't want the Optional so this isn't really satisfying.
You can modify the method name by using an alternative for find, e.g. search or query. See https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repository-query-keywords for a list of possibly words to use.
You can also add anything between find and By. findUserById would work just as well.
You can decide not to inherit from CrudRepository. This of course will remove all the other methods of the CrudRepository as well. But if you copy their declaration into your repository interface the will get their proper implementation. You may also use this variant to prune methods that you possibly don't want to have in your repository anyway.

Optionals with JPA and Spaghetti code in Springboot API

I have a few questions about the use of optionals in an API that I am working on.
First, I am using in my native queries (in my JPA repositories) as return optionals.
It's okay to use them like this?
Also I have a service that checks if one of my entity exits by its id if not it throws a custom exception. What I don't understand is the service can return the Object type, but the JPA query definition is: Optional findById(ID id) ???
public BasicTvShowInfo getBasicTvShowInfoById (Integer idTvShow) throws ResourceNotFoundException {
return basicTvShowInfoRepository.findById(idTvShow).orElseThrow(() -> new ResourceNotFoundException(
"The tv show with the id : " + idTvShow + " was not found."));
}
Finally, I have a validation when I want to post an entity call TvShowReminder. This entity is declared as:
#Entity
#Table(name = "tv_show_reminder")
public class TvShowReminder {
// Attributes
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column (name = "id_tv_show_reminder")
private Integer idTvShowReminder;
#ManyToOne(optional = false)
#JoinColumn(name="id_user")
#NotNull(message = "Provide user {idUser}")
private User user;
#ManyToOne()
#JoinColumn(name= "id_basic_tv_show_info")
private BasicTvShowInfo basicTvShowInfo;
#ManyToOne()
#JoinColumn(name= "id_tv_show_created_by_user")
#OnDelete(action = OnDeleteAction.CASCADE)
private UserTvShow userTvShow;
private Boolean completed;
#Column(name = "current_season")
private Integer currentSeason;
#Column(name = "current_episode")
private Integer currentEpisode;
#Column(name = "personal_rating")
private Integer personalRating;
It has two nested entities, that can be nullable. The BasicTvShowInfo and UserTvShow.
#Entity
#Table(name = "basic_tv_show_info")
public class BasicTvShowInfo {
#Id
#Column(name = "id_basic_tv_show_info")
#NotNull(message = "Provide id (Integer)")
private Integer id;
#Column(name = "original_name")
#JsonProperty("original_name")
#NotNull(message = "Provide original_name (String)")
private String originalName;
}
#Entity
#Table(name = "tv_show_created_by_user")
public class UserTvShow {
// Attributes
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_tv_show_created_by_user")
private Integer idTvShowCreatedByUser;
#ManyToOne()
#JoinColumn(name= "id_user")
#NotNull(message = "Provide user {idUser}")
private User user;
#Column(name = "name_tv_show")
#NotNull(message = "Provide nameTvShow (String)")
private String nameTvShow;
private String genre;
#Column(name = "production_company")
private String productionCompany;
}
I have a validation method in the service that checks:
if both the BasicTvShowInfo and UserTvShow are null.
if both objects (BasicTvShowInfo and UserTvShow) have an id.
if the logged user already has a reminder with the BasicTvShowInfo id provided.
if the logged user already has a reminder with the UserTvShow id provided.
In all these cases I throw exceptions.
How can I refactor this service method which is working perfectly but I want to write it more elegantly? I really don't like to use so many if-elseif-else and nested ifs. It gets really hard to follow the code.
This is the method:
private void validateExistenceOfTvShowReminder(TvShowReminder tvShowReminder) throws ResourceAlreadyExistsException, BusinessLogicValidationFailure, ResourceNotFoundException {
Optional<TvShowReminder> tvShowReminderOptional = Optional.empty();
String messageError = null;
// If both the basic tv show info object and user tv show object ARE PRESENT -> exception
// If the basic tv show info object id already exists in the reminders table -> exception
// If the user tv show info object id already exists in the reminders table -> exception
// If both are null -> exception.
if(tvShowReminder.getBasicTvShowInfo() != null && tvShowReminder.getUserTvShow() != null){
if(tvShowReminder.getBasicTvShowInfo().getId() != null && tvShowReminder.getUserTvShow().getIdTvShowCreatedByUser() != null)
throw new BusinessLogicValidationFailure("You cant have a reminder that point to a tv show from the system and to a tv show created by the user");
} else if(tvShowReminder.getBasicTvShowInfo() != null){
if(tvShowReminder.getBasicTvShowInfo().getId() != null) {
tvShowReminderOptional = tvShowReminderRepository.findByUserIdAndTvShowId(tvShowReminder.getUser().getIdUser(), tvShowReminder.getBasicTvShowInfo().getId());
messageError = "User already created a tv show reminder with the basicTvShowInfo id : " + tvShowReminder.getBasicTvShowInfo().getId();
}
} else if (tvShowReminder.getUserTvShow() != null){
if(tvShowReminder.getUserTvShow().getIdTvShowCreatedByUser() != null) {
// Validate if the user tv show of the reminder actually belongs to the logged user.
if(Optional.ofNullable(userTvShowService.getUserTvShow(tvShowReminder.getUser().getIdUser(), tvShowReminder.getUserTvShow().getIdTvShowCreatedByUser())).isPresent()) {
tvShowReminderOptional = tvShowReminderRepository.findByUserIdAndTvShowCreatedByUserId(tvShowReminder.getUser().getIdUser(), tvShowReminder.getUserTvShow().getIdTvShowCreatedByUser());
messageError = "User already created a tv show reminder with a userTvShow id : " + tvShowReminder.getUserTvShow().getIdTvShowCreatedByUser();
}
}
} else {
messageError = "To create a tv show reminder you have to provided a basicTvShowInfo id OR a userTvShow id";
throw new BusinessLogicValidationFailure(messageError);
}
// Each query findByUserIdAndTvShowId and findByUserIdAndTvShowCreatedByUserId return an optional with the tv show or an empty optional.
// This method will return true if there is a tv show present in the optional OR returns false if is an empty optional (with null value).
if(tvShowReminderOptional.isPresent()){
throw new ResourceAlreadyExistsException(messageError);
}
}
// Repository if it is of any help is:
#Repository
public interface TvShowReminderRepository extends JpaRepository<TvShowReminder, Integer> {
Page<TvShowReminder> findByUser_IdUser(Pageable pageable, Integer idUser);
List<TvShowReminder> findByUser_IdUser(Integer idUser);
#Query(value = "SELECT * FROM tv_show_reminder WHERE id_user = ?1 and id_basic_tv_show_info = ?2", nativeQuery = true)
Optional<TvShowReminder> findByUserIdAndTvShowId(Integer idUser, Integer idBasicTvShowInfo);
#Query(value = "SELECT * FROM tv_show_reminder WHERE id_user = ?1 and id_tv_show_created_by_user = ?2", nativeQuery = true)
Optional<TvShowReminder> findByUserIdAndTvShowCreatedByUserId(Integer idUser, Integer idTvShowCreatedByUser);
#Query(value = "SELECT * FROM tv_show_reminder WHERE id_user = ?1 and id_tv_show_reminder = ?2", nativeQuery = true)
Optional<TvShowReminder> findByIdTvShowReminderAndUserId(Integer idUser, Integer idTvShowReminder);
}
Sorry for the long post, thank you in advance, if anyone can help me or guide me to the right approach I will appreciate it.
First, I am using in my native queries (in my JPA repositories) as return optionals. It's okay to use them like this?
I don't see why it wouldn't be ok.
Also I have a service that checks if one of my entity exits by its id if not it throws a custom exception. What I don't understand is the service can return the Object type, but the JPA query definition is: Optional findById(ID id) ???
This is fine as well. Spring Data just didn't want to impose the cost of throwing an exception every time an object wasn't found which is why they use the optional approach. You can do whatever you want with that.
Finally, I have a validation when I want to post an entity call TvShowReminder. This entity is declared as:
To refactor validation into more readable form, I would recommend you look into the Jakarta/Java EE Validation API, also called Bean Validation API: https://www.baeldung.com/javax-validation

How to update Child entity along with Parent entity in Spring Boot?

I am trying to update the parent entity and child entity (comes with parent) using JPA. I tried many ways but I couldn't do it. It gives me the following error.
javax.persistence.EntityExistsException: A different object with the
same identifier value was already associated with the session Contact(6)
Following is the entity,
Parent -> User.java
#Entity
public class User extends Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_generator")
#SequenceGenerator(name = "seq_generator", sequenceName = "seq_users", initialValue = 1, allocationSize = 1)
private Long id;
private String firstName;
private String lastName;
private String email;
private String password;
private String street;
private String city;
#OneToOne(cascade = CascadeType.ALL, mappedBy = "userContact")
private Contact contact;
}
Child -> Contact.java
#Entity
public class Contact extends Serializable {
private static final long serialVersionUID = 1L;
#Getter(value = AccessLevel.NONE)
#Setter(value = AccessLevel.NONE)
#Id
#Column(insertable = false, updatable = false)
private Long id;
#MapsId
#OneToOne
#JoinColumn(name = "id", nullable = false)
private User userContact;
private String phone;
}
In my Service.java I am updating like this,
#Override
#Transactional
public User update(Long id, User user) {
Optional<User> curUser = userRepo.findById(id);
user.setId(curUser.get().getId());
user.setStreet(curUser.get().getStreet());
user.setCity(curUser.get().getCity());
Contact contact = user.getContact();
contact.setUserContact(user);
user.setContact(contact);
return userRepo.save(user);
}
And the update JSON payload is like this,
{
"firstName": "FirstName",
"lastName": "LastName",
"email": "Email",
"password": "Password",
"contact": {
"phone": "0123456789"
}
}
So why I can't update this at once? The insert is working fine but this update is not working. What I am doing wrong here? I really appreciate it if anybody can help me. Thanks in advance.
Here is the new way I tried,
#Override
#Transactional
public User update(Long id, User user) {
Optional<User> curUser = userRepo.findById(id);
curUser.setFirstName(user.getFirstName());
curUser.setlastName(user.getLastName());
curUser.setEmail(user.getEmail());
curUser.setPassword(user.getPassword());
Contact contact = user.getContact();
contact.setUserContact(curUser);
curUser.setContact(contact);
return userRepo.save(curUser);
}
Because you have the same user (i.e. same id) represented twice in the persistence context.
This line loads the user curUser in the persistence context.
Optional<User> curUser = userRepo.findById(id);
This line makes user the same user by id, but it is a different instant.
user.setId(curUser.get().getId());
And finally this line tries to add user to the persistence context.
userRepo.save(user);
This would result with the two instances being attached to the persistence context while representing the same user with the same id which would result in an inconsistent state since JPA wouldn't know which version to persist. Therefore this is forbidden and throws the exception you are seeing.
In your case changing
Optional<User> curUser = userRepo.findById(id);
user.setId(curUser.get().getId());
to
user.setId(id);
should do the trick.
Regarding your comments:
The same problem exists for referenced objects. You'll have to make sure that you: either load an entity and change it's attribute, or do NOT load the entity, not directly nor indirectly via a reference and save a detached entity, constructed from your request.
If you load an entity and then try to save a detached instance of it, you will get this kind of error.

How to pass session attribute to a ThymeLeaf form field for saving into database

I have a Thymeleaf form that sends data to a database table when the user is logged in. The ThymeLeaf template is able to capture the session of the logged in user from it's assigned controller. When saving data to the database.
I need to accompany the form data alongside the captured session username, such that the said session username is saved in the database table as well. However I get an error that says
org.springframework.beans.NotReadablePropertyException: Invalid
property 'userName' of bean class
[com.example.jobonics.Persistence.model.NewJobs]: Bean property
'userName' is not readable or has an invalid getter method: Does the
return type of the getter match the parameter type of the setter?
I have a controller that picks the user session and sends it to the thymeleaf template as well as a hidden input user field in my form that holds the session object from the controller. My code is pasted below for some clarity:
#RequestMapping(value = "/new_job", method = RequestMethod.GET)
public String newRegistration(ModelMap model) {
NewJobs newJobs = new NewJobs();
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
ServletRequestAttributes attr = (ServletRequestAttributes)
RequestContextHolder.currentRequestAttributes();
//extract user session from HttpSession
HttpSession session = attr.getRequest().getSession(true);
//initialize user service to get authenticated user
User user = userService.findUserByEmail(auth.getName());
model.addAttribute("newjobs", newJobs);
//add session captured name as attribute to view
model.addAttribute("userName", "Welcome " + user.getFullName() + "");
return "new_job";
#RequestMapping(value = "/save", method = RequestMethod.POST)
public String saveRegistration(#Valid NewJobs newJobs, BindingResult result,
ModelMap model,
RedirectAttributes redirectAttributes) {
if (result.hasErrors()) {
System.out.println("has errors");
return "new_job";
}
NJS.save(newJobs);
return "redirect:/new_job";
}
}
My controller
<form class="form-horizontal" th:action="#{/save}" th:object="${newjobs}" method="post">
<div class="form-group">
<div class="col-sm-7">
<input type="text" class="form-control" th:value="*{userName}" style="display:none">
</div>
</div>
NewJobs
#Entity
#Table(name = "newjobs")
public class NewJobs {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private int job_id;
#Column(name = "Employer")
private String Employer;
#Column(name = "JobTitle")
#NotEmpty(message = "Please provide the job title")
private String jobTitle;
#Column(name = "job_summary", length = 1000)
private String jobSummary;
#Column(name = "job_description", length = 1000)
private String jobDescription;
#Column(name = "location")
private String Location;
#Column(name = "career_level")
#NotEmpty(message = "Please provide career level required")
private String careerLevel;
#Column(name = "industry")
private String industry;
#Column(name = "profession")
#NotEmpty(message = "Please provide the profession")
private String profession;
#Column(name = "jobtype")
#NotEmpty(message = "Please provide your job type")
private String jobType;
#Column(name = "min_experience")
#NotEmpty(message = "Please provide the Min years of experience")
private String minExperience;
#Column(name = "min_qualification")
#NotEmpty(message = "Please provide the Min Qualifications")
private String minQualification;
#Column(name = "salary")
#NotEmpty(message = "Please provide the salary")
private String salary;
#Column(name = "deadline_date")
#NotEmpty(message = "Please provide the Applications Deadline")
private String deadlineDate;
#Column(name = "created_at", columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP", insertable = false, nullable = false)
private Date created_at;
}`
My expected output is to send the logged in user session object to db as part of a submitted form. What can I do to correct the said error above?
I think the mistake is you got username from wrong location:
th:value="*{userName}" mean newjobs.username
but in controller, you do not set username to newjobs, you set to model attribute
Let change code in controller (with additional getter)
newjobs.setUserName("Welcome " + user.getFullName())
or
th:value="${userName}" ($ but not *)
Note:
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#expressions-on-selections-asterisk-syntax
There is an important difference though: the asterisk syntax evaluates
expressions on selected objects rather than on the whole context. That
is, as long as there is no selected object, the dollar and the
asterisk syntaxes do exactly the same.

Ebean and Play! not filtering columns with .select()

I'm trying to fetch just a part of the model using Ebean in Play! Framework, but I'm having some problems and I didn't found any solutions.
I have these models:
User:
#Entity
#Table(name = "users")
#JsonInclude(JsonInclude.Include.NON_NULL)
public class User extends Model{
#Id
private int id;
#NotNull
#Column(name = "first_name", nullable = false)
private String firstName;
#Column(name = "last_name")
private String lastName;
#NotNull
#Column(nullable = false)
private String username;
#NotNull
#Column(nullable = false)
private String email;
private String gender;
private String locale;
private Date birthday;
private String bio;
#NotNull
#Column(nullable = false)
private boolean active;
private String avatar;
#Column(name = "created_at",nullable = false)
private Date createdAt;
#OneToMany
private List<UserToken> userTokens;
// Getters and Setters omitted for brevity
}
UserToken:
#Entity
#Table(name = "user_tokens")
public class UserToken extends Model {
#Id
private int id;
#Column(name = "user_id")
private int userId;
private String token;
#Column(name = "created_at")
#CreatedTimestamp
private Date createdAt;
#ManyToOne
private User user;
// Getters and Setters omitted for brevity
}
And then, I have a controller UserController:
public class UserController extends Controller{
public static Result list(){
User user = Ebean.find(User.class).select("firstName").where().idEq(1).findUnique();
return Results.ok(Json.toJson(user));
}
}
I expected that, when using the .select(), it would filter the fields and load a partial object, but it loads it entirely.
In the logs, there is more problems that I don't know why its happening.
It is making 3 queries. First is the one that I want. And then it makes one to fetch the whole Model, and another one to find the UserTokens. I don't know why it is doing these last two queries and I wanted just the first one to be executed.
Solution Edit
After already accepted the fact that I would have to build the Json as suggested by #biesior , I found (out of nowhere) the solution!
public static Result list() throws JsonProcessingException {
User user = Ebean.find(User.class).select("firstName").where().idEq(1).findUnique();
JsonContext jc = Ebean.createJsonContext();
return Results.ok(jc.toJsonString(user));
}
I render only the wanted fields selected in .select() after using JsonContext.
That's simple, when you using select("...") it always gets just id field (cannot be avoided - it's required for mapping) + desired fields, but if later you are trying to access the field that wasn't available in first select("...") - Ebean repeats the query and maps whole object.
In other words, you are accessing somewhere the field that wasn't available in first query, analyze your controller and/or templates, find all fields and add it to your select (even if i.e. they're commented with common HTML comment in the view!)
In the last version of Play Framework (2.6) the proper way to do this is:
public Result list() {
JsonContext json = ebeanServer.json();
List<MyClass> orders= ebeanServer.find(MyClass.class).select("id,property1,property2").findList();
return ok(json.toJson(orders));
}

Categories

Resources