Playframework: Handling Form Submission - java

I am working on a Play 2.2 Form application. I create a form using this function
public static Result editIndicator() {
Indicators addIndObj = new Indicators();
addIndObj.aDt = (new Date()).toString();
addIndObj.aUid = Long.parseLong(play.mvc.Controller.session("userid"));
addIndObj.dTag = "N";
// List of all Countries
ALLCommon table_listobj = Indicators.ddl1();
Form<Indicators> newIndicatorForm = form(Indicators.class).fill(
addIndObj);
return (ok(addindicator.render(newIndicatorForm, table_listobj)));
}
the Indicator Model has parameters with constraints #Required for the parameters as follows
#Constraints.Required
#Column(name = "A_DT")
public String aDt;
#Constraints.Required
#Column(name = "A_UID")
public Long aUid;
#Constraints.Required
#Column(name = "D_TAG")
public String dTag;
#Column(name = "TIME")
#Required
#Formats.DateTime(pattern = "HH:mm:ss")
public Date time;
#Constraints.Required
#Column(name = "INDFREQUENCY")
public String indFrequency;
So, I set the values before and then bind it to form. I don't use all of these #Required values (just the Frequency part) in my form and when I try to get the filledform I get form errors
Form(of=class models.Indicators,
data={}, value=None,
errors={time=[ValidationError(time,error.required,[])],
aDt=[ValidationError(aDt,error.required,[])],
dTag=[ValidationError(dTag,error.required,[])],
aUid=[ValidationError(aUid,error.required,[])],
indFrequency=[ValidationError(indFrequency,error.required,[])]})
Do I need to set these values in form even if I don't use it? Or I am missing something..
Any help is appriciated.. thanks in advance.. :)

Found the answer..
1) You have to use all parameters that are required for your model i.e #Required and if you don't have to use it in your form. Simply put it in a <div display:none> tag.
2) On Submit button we call the #forms(routes.Application.index) but, the brackets should encapsulate your complete code and not just around the submit button. Hence,
the best practice would be #forms(routes.Application.index){ your complete code here}.

You can use groups if you don't need to validate all fields all the time.
For example:
// interfaces to create groups
public interface Step1 {}
public interface Step2 {}
So you need to add thats groups to your fields:
#Required(groups = {Step1.class})
#Column(name = "A_DT")
public String aDt;
#Required(groups = {Step1.class})
#Column(name = "A_UID")
public Long aUid;
#Required(groups = {Step1.class})
#Column(name = "D_TAG")
public String dTag;
#Column(name = "TIME")
#Required(groups = {Step2.class})
#Formats.DateTime(pattern = "HH:mm:ss")
public Date time;
#Required(groups = {Step2.class})
#Column(name = "INDFREQUENCY")
public String indFrequency;
Then:
// this only validates the fields that have Step1 group (aDt, aUid, dTag in this example)
Form<Indicators> newIndicatorForm = form(Indicators.class, Step1.class)
.fill(addIndObj);
// this only validates the fields that have Step2 group (time, indFrequency in this example)
Form<Indicators> newIndicatorForm = form(Indicators.class, Step2.class)
.fill(addIndObj);
// this validates the fields of both groups (all the fields in this example)
Form<Indicators> newIndicatorForm = form(Indicators.class, Step1.class, Step2.class)
.fill(addIndObj);

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.

ModelMapper - convert a Date inside a Collection<Object> to String (java)

I've searched a lot in this forum and other websites, but I'm still stuck with my problem.
I'm actually using modelmapper to convert an entity to a DTO.
Here is the Entity :
#Entity
public class Candidate implements Serializable {
#Id
#GeneratedValue (strategy=GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column (name = "name")
private String lastname;
#Column (name = "firstname")
private String firstname;
#Column (name = "phone")
private String phoneNumber;
#Column (name = "mail")
private String email;
#Column (name = "title")
private int title;
#OneToMany (mappedBy = "candidateId")
private Collection<Candidature> Interviews;
Here is Candidature Entity (that you find in the first Entity's collection):
public class Candidature implements Serializable {
#Id
#NotBlank
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private Long id;
#ManyToOne (fetch = FetchType.LAZY)
#JoinColumn (name = "candidat_id")
private Candidate candidateId;
#Column(name = "interview")
#Temporal (TemporalType.DATE)
private Date dateInterview;
#Column(name ="status")
private String status;
And here is the DTO :
public class CandidateDTO {
private Long id;
private String lastname;
private String firstname;
private String phoneNumber;
private String email;
private String title;
private String dateLastInterview;
As you can see, there are some differences.
The problem I face is that the last attribute of DTO (dateLastInterview) comes from the Collection<Candidature> and more precisely it must be the last dateInterview converted into String.
Convert a Date into String is not a problem. Getting the last item of a Collection neither.
But I can't make it work with modelMapper.
Here is a sample code I tried :
modelMapper = new ModelMapper();
Converter<Candidate, CandidateDTO> converter = new Converter<Candidate, CandidateDTO>()
{
#Override
public CandidateDTO convert(MappingContext<Candidate, CandidateDTO> mappingContext) {
Candidate candidate = mappingContext.getSource();
CandidateDTO cdto = new CandidateDTO();
List<Candidature> list = (List) candidate.getInterviews();
Date date = list.get(list.size()-1).getDateInterview();
DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
String dateInterviewConverted = df.format(date);
mappingContext.getDestination().setTitle(mappingContext.getSource().getTitle());
mappingContext.getDestination().setDateLastInterview(dateInterviewConverted);
return cdto;
}
};
modelMapper.createTypeMap(Candidate.class, CandidateDTO.class).setConverter(converter);
(and I tried, instead of the last line above : modelMapper.addConverter(converter); but same result)
But it doesn't work, I get all attributes at null.
I previously succeded using
map().setTitle(source.getTitle());
map().setDateLastInterview(dateInterviewConverted);
And then converting the Date to String in my DTO "set" method, but it seems that it shouldn't be here, but into the ModelMapper class or the class that is using it.
Do you have an idea ? I'm new with modelMapper, and I keep browsing google and I can't find (or maybe understand ?) any response that might help me.
Thanks
Ok I think I succeded.
Using the converter was the right thing, but I wasn't using it correctly. For the converter, the two objets that you put inside <> are the ones of the attributes concerned by the converter.
For example, for the first converter, I wanted to parameter the conversion of the Collection (coming from an object Candidate) to become a String (to match the attribute of the DTO).
So then you only have to create a PropertyMap with the Class and ClassDTO, and in the configure() method you only mention the attributes that will use special parameters (the other ones are correct since they respect the standard mapping).
Converter<Collection<Candidature>, String> convertLastDateToString = new Converter<Collection<Candidature>, String>() {
public String convert(MappingContext<Collection<Candidature>, String> context) {
List<Candidature> candidatureList = (List)context.getSource();
String dateInterviewConverted = "";
if (candidatureList.size() > 0) {
Date lastInterview = candidatureList.get(0).getDateInterview();
for (int i = 0; i < candidatureList.size(); i++) {
if (candidatureList.get(i).getDateInterview().after(lastInterview)) {
lastInterview = candidatureList.get(i).getDateInterview();
}
}
// converts the Date to String
DateFormat df = new SimpleDateFormat(DATE_FORMAT);
dateInterviewConverted = df.format(lastInterview);
}
return dateInterviewConverted;
}
};
// allows custom conversion for Title attribute
// the source (Candidate) has a title attribute in int type
// the destination (CandidateDTO) has a title attributes in String type
Converter<Integer, String> convertTitleToString = new Converter<Integer, String>(){
public String convert(MappingContext<Integer, String> context){
return Title.values()[context.getSource()].toString();
}
};
// define explicit mappings between source and destination properties
// does only concernes the attributes that will need custom mapping
PropertyMap<Candidate, CandidateDTO> candidateMapping = new PropertyMap<Candidate, CandidateDTO>()
{
protected void configure()
{
// to map these two attributes, they will use the corresponding converters
using(convertTitleToString).map(source.getTitle()).setTitle(null);
using(convertLastDateToString).map(source.getCandidatures()).setDateLastInterview(null);
}
};
// add the mapping settings to the ModelMapper
modelMapper.addMappings(candidateMapping);

Single variable update of an Entity using a PUT request

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.

How to parse a list of String obtained via select box in a REST

I have this method that creates an object Event on my database by getting data from an HTML form.
#PostMapping("/events")
#Timed
public ResponseEntity<Event> createEvent(#RequestBody Event event) throws URISyntaxException {
//some code
}
My Event entity has these attributes:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
private String name;
#Column(name = "jhi_date")
private LocalDate date;
#Column(name = "attendees")
private String attendees;
#OneToOne
#JoinColumn(unique = true)
private Desk refTable;
#OneToMany(mappedBy = "event")
#JsonIgnore
private Set<CustomUser> participants = new HashSet<>();
The one that interests me is attendees. Due to my business logic I can not change its type from String to a List.
My answer is: how can I get a List of strings using a multiple select in HTML if my model doesn't have a field of type List?
I would like to receive a List from HTML and then convert it into one string.
Just to know, I am using Jhipster. Thanks in advice for your time.
This where using JHipster DTO option makes a lot of sense as the DTO will adapt Event entity data to a format easier to process by the client code.
You can enable it by editing.jhipster/Event.json to set "dto": "mapstruct", and re-generate your entity with yo jhipster:entity.

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