Compare two fields that use same class - validation

I have two input fields fromDate and toDate which are instances of Date class.
The Date class uses custom Date validator which validates the month, day and year fields
contained in the date field.
The custom date validator is specific for each date i.e, fromDate and toDate.
I need to compare the month, day or year fields of fromDate with toDate.
If the fromDate is greater than toDate, a validation message has to displayed.
Update:
The fromDate and toDate are two custom date components as below
<eg:dateField id="inpFromDate" value="#{mrBean.fromDate}" .... />
<eg:dateField id="inpToDate" value="#{mrBean.toDate}" .... />
fromDate and toDate are instances of Date class which is
public class Date {
private String mm;
private String dd;
#customDateValidator //Validates each date field
private String yyyy;
//constructors
//getters and setters
How would you implement the validator in this case where each date already has a validator

Yes, you can! Suppose you have the following PrimeFaces's input fields:
<p:calendar id="from" value="#{mrBean.fromDate}" binding="#{from}" >
<p:ajax process="from to" update="toDateMsg" />
</p:calendar>
<p:calendar id="to" value="#{mrBean.toDate}" >
<f:attribute name="fromDate" value="#{from.value}" />
<f:validator validatorId="validator.dateRangeValidator" />
<p:ajax process="from to" update="toDateMsg" />
</p:calendar>
<p:message for="to" id="toDateMsg" />
This should be your Validator:
#FacesValidator("validator.dateRangeValidator")
public class DateRangeValidator implements Validator {
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
if (value == null || component.getAttributes().get("fromDate") == null) return;
Date toDate = (Date) value;
Date fromDate = (Date) component.getAttributes().get("fromDate");
if (toDate.after(fromDate)) {
FacesMessage message = new FacesMessage("Invalid dates submitted.");
message.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ValidatorException(message);
}
}
}
Note that I am using PrimeFaces's <p:calendar> component to write my example because the properties binded to this component will automatically be converted to Date object before being validated. In your program, you may have your own Converter to convert String to Date.

Related

Cannot insert date into the Oracle databe in Spring Boot via Thymeleaf

I have a typo in my CRUD process. I couldn't handle with inserting date type into the my database named for Oracle 11g.
When I try to insert data, there is an error appeared on the console.
Field error in object 'employee' on field 'registerDate': rejected value [2019-09-11]; codes [typeMismatch.employee.registerDate,typeMismatch.registerDate,typeMismatch.java.util.Date,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [employee.registerDate,registerDate]; arguments []; default message [registerDate]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.Date' for property 'registerDate'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [#javax.validation.constraints.NotNull #javax.persistence.Temporal #javax.persistence.Column java.util.Date] for value '2019-09-11'; nested exception is java.lang.IllegalArgumentException]
How can I fix it?
Employee Forum
<form action="#" th:action="#{/employees/save}"
th:object="${employee}" method="POST">
...
<input type="date" name="date" th:field="*{registerDate}"
class="form-control mb-4 col-4" placeholder="Register Date">
</form>
Employee Object
#NotNull(message="is required")
#Temporal(TemporalType.DATE)
#Column(name="REGISTER_DATE")
private Date registerDate;
Controller Class
#PostMapping("/save")
public String saveEmployee(#ModelAttribute("employee") Employee theEmployee,#RequestParam("date") String date) throws IOException, ParseException {
// Date
SimpleDateFormat dateformat = new SimpleDateFormat("dd/MM/yyyy");
Date registerDate = dateformat.parse(String.valueOf(date));
System.out.println("/save | registerDate : " + registerDate);
theEmployee.setRegisterDate(registerDate);
// save the employee
employeeService.save(theEmployee);
}
Maybe you need to format the date for Oracle, for example:
#DateTimeFormat(pattern = "hh:mm aa")
#Column(name = "date_start")
private Date dateStart;

Spring MVC complex object data binding

I am still struggling with Spring MVC with what should be a fairly straightforward problem but what seems to be sparsly documented in Spring MVC documentation.
My project uses Spring MVC and Thymeleaf for the views, but the view rendering engine is not really relevant to the problem.
My application is centered around an Activity class which models an (indoor or outdoor) activity which is organized by a member and where fellow members can subscribe to. An Activity has, among others, a Category field and a Region field, which are dropdown fields which are modeled by Hibernate as many-to-one entities to DB lookup tables which contain an id and description field.
The code for the Activity entity class is as follows, the non relevant fields are omitted to shorten the code:
package nl.drsklaus.activiteitensite.model;
//imports
#Entity
#Table(name="activity")
public class Activity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name="organizer_id")
private Member organizer;
#Size(min=5, max=50)
#Column(name = "title", nullable = false)
private String title;
#Size(min=5, max=500)
#Column(name = "description", nullable = false)
private String description;
#ManyToOne
#JoinColumn(name="category_id")
private ActivityCategory category;
#ManyToOne
#JoinColumn(name="region_id")
private ActivityRegion region;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name="member_activity_subscription",
joinColumns = {#JoinColumn(name="activity_id")},
inverseJoinColumns={#JoinColumn(name="member_id")})
private List<Member> participants = new ArrayList<Member>();
//getters and setters
#Override
public int hashCode() {
...
}
#Override
public boolean equals(Object obj) {
...
}
}
In the view, the user should be able to select a Region and Category from a select box. THe options are put in the Model using a #ModelAttribute annotated method on the class level.
THe problem is with the binding of the box to the lookup property fields.
For example the Category field is of the ActivityCategory type, which is an entity class containing an id and a description property.
In the view, the select box is filled with the list of possible options (allCategories which contains ActivityCategory instances), Thymeleaf takes care of selecting the current value by matching the "value" attribute value with the list:
<label>Categorie</label>
<select th:field="*{category}">
<option th:each="cat : ${allCategories}"
th:value="${cat}"
th:text="${cat.description}">
</option>
</select>
The generated HTML looks like:
<select id="category" name="category">
<option value="nl.drsklaus.activiteitensite.model.lookup.ActivityCategory#20">Actief en sportief</option>
<option value="nl.drsklaus.activiteitensite.model.lookup.ActivityCategory#21">Uitgaan en nachtleven</option>
<option value="nl.drsklaus.activiteitensite.model.lookup.ActivityCategory#22" selected="selected">Kunst en cultuur</option>
<option value="nl.drsklaus.activiteitensite.model.lookup.ActivityCategory#23">Eten en drinken</option>
<option value="nl.drsklaus.activiteitensite.model.lookup.ActivityCategory#24" selected="selected">Ontspanning en gezelligheid</option>
</select>
As we see, the value attributes contain a string representation of the object itself which is clearly not desired, to show the id values we could use ${cat.id} instead of ${cat} but then the selection of the current value (setting the 'selected="selected"' attribute) does not work anymore. THerefore I implemented a Converter which converts an ActivityCategory object to an int (the id value). In Thymeleaf, the converter is called by using the double accolades {{}}:
th:value="${{cat}}"
THe converter is created and added to Spring:
public class LookupConverter implements Converter<LookupEntity, String> {
public String convert(LookupEntity source) {
return String.valueOf(source.getId());
}
}
//In MvcConfig class
#Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new LookupConverter());
}
Now the HTML shows the id values for the options, which is much more logical:
<select id="category" name="category">
<option value="1">Actief en sportief</option>
<option value="2">Uitgaan en nachtleven</option>
<option value="3" selected="selected">Kunst en cultuur</option>
<option value="4">Eten en drinken</option>
<option value="5">Ontspanning en gezelligheid</option>
</select>
But it still wrong after submitting, the id value cannot be bound to the Activity object which expects a ActivityCategory instead if an integer value, so a typeMismatch validation error is generated.
My handler method looks like:
#RequestMapping(value = "/{id}/submit", method = RequestMethod.POST)
public String submitForm(#ModelAttribute("activity") Activity activity, BindingResult result, ModelMap model) {
if (result.hasErrors()) {
return "activityform";
} else {
if (activity.getId() == null) {
this.service.saveActivity(activity);
} else {
this.service.mergeWithExistingAndUpdate(activity);
}
return "redirect:/activity/" + activity.getId() + "/detail";
}
}
I have looked at many posts but still found have no solution for this IMHO pretty trivial issue. How can the String value containing the id be accepted by the handler method and properly converted? Or can we not use the id value for this purpose?
Looking for some hints...
I think you can't use your entity model to submit data from your form to the MVC controller. Try to create a separate form object that matches the form data and write a service method to translate it to entities that can be persisted in the database.
With help from another forum I have found the most elegant solution! Instead of a Converter, we use a Formatter which can convert from specfiec Object type to a String and vice versa. The formatter is registered to Spring and automatically called from Thymeleaf and converts the id field to an ActivityCategory instance with only the id value set. So we do not lookup the actual instance from the database because we do not need the description here, for Hober ate the id is enough to create the query.
My formatter looks like:
public class ActivityCategoryFormatter implements Formatter<ActivityCategory> {
#Override
public String print(ActivityCategory ac, Locale locale) {
// TODO Auto-generated method stub
return Integer.toString(ac.getId());
}
#Override
public ActivityCategory parse(final String text, Locale locale) throws ParseException {
// TODO Auto-generated method stub
int id = Integer.parseInt(text);
ActivityCategory ac = new ActivityCategory(id);
return ac;
}
}
and is registered to Spring (together with the ActivityRegionFormatter for the other lookup field) by:
#Override
public void addFormatters(FormatterRegistry registry) {
//registry.addConverter(new LookupConverter());
registry.addFormatter(new ActivityCategoryFormatter());
registry.addFormatter(new ActivityRegionFormatter());
}
And now it works as expected!
The only remaining issue is that we have some code duplication because the two Formatter classes are almost the same, they only differ in the generic class that is passed in.
I tried to solve this by using a common interface LookupEntity which is implemented by the two lookup entity classes (ActivityCategory and RegionCategory) and use this common interface to define the formatter but unfortunately that did not work...

how to map front-end field names to db column names for sorting

I am working on a web app using Spring MVC 3, Spring Data Commons 1.4.1, and MyBatis 3. To support sorting, I find myself needing to map input field names used on the client side to names of corresponding columns in the database. For example, on the client side, an input field is called shortName while the name of the corresponding column in the database is SHORT_NAME. What is the best way to do it? Does Spring provide any support for this? Thanks.
Here's how I annotate field names in the domain class so that I can look them up later in the controller method below.
#Entity
public class Activity extends BaseDomainObject {
#Column(name="ID")
private Long id;
private String name;
#Column(name="SHORT_NAME")
private String shortName;
#Column(name="START_TIME")
private Date startTime;
#Column(name="END_TIME")
private Date endTime;
#Column(name="LOCATION")
private String location;
public Activity()
{
}
// getters and setters go here
}
Here's the method in the controller
public #ResponseBody Page<Activity> query(ActivityCriteria ac, Pageable p) {
// translate the fiend name used in the front-end into the one used in the back-end
List<Order> orders = new ArrayList<Order>();
java.util.Iterator<Order> iterator = p.getSort().iterator();
while (iterator.hasNext()) {
Order order = iterator.next();
if (!StringUtils.isBlank(order.getProperty()) && order.getDirection() != null) {
String columnName = mapFieldNameToColumnName(Activity.class, order.getProperty());
Order newOrder = new Order(order.getDirection(), columnName);
orders.add(newOrder);
}
}
Pageable copy = new PageRequest(p.getPageNumber(), p.getPageSize(), new Sort(orders));
return activityService.query(ac, copy);
}
Here's what I end up doing in the corresponding MyBatis mapper file.
<select id="getListOfActivitiesWithConditions" resultMap="activityMap" parameterType="map">
SELECT a.* FROM activity a
<include refid="search-conditions"/>
<if test="page.sort != null">
<foreach item="order" index="index" collection="page.sort" open="order by" separator="," close=" ">
<if test="order.property != null and order.property == 'shortName'">
SHORT_NAME ${order.direction}
</if>
<if test="order.property != null and order.property == 'startTime'">
START_TIME ${order.direction}
</if>
</foreach>
</if>
limit #{page.offset}, #{page.size}
</select>

How to insert date type when integratting between Hibernate and Spring

I 'm using Hibernate framework as way of mapping from Javabean to Database for my project which applied by Spring framework.But, I don' know how to insert Date type in Hibernate
My code :
User class:
import java.util.Date;
......................................
#Column(name = "date")
private Date date;
#SuppressWarnings("deprecation")
public void setDate(String date) {
Date date1 = new Date(date);
this.date = date1;
}
My register.jsp
<tr>
<td><form:label path="date">Date</form:label></td>
<td><form:input path="date" /></td>
</tr>
After submitting.date fileds in databse is null. It's value can not be mapped into Account table in database
Note: Account means :
#Entity
#Table(name="Account")
public class User {
Please help me.Thanks
I also has this problem.When I insert date type object into DB,it turns out null value.
My date type in DB is Datetime type.In my case,it is format problem.
Then I try to do so,then solved it,after following formatting.
Date date=new Date();
SimpleDateFormat spl=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String d=spl.format(date);
date=spl.parse(d);
You can try it.
in your User class use the following to map the column as date type
#Column(name = "date")
#Temporal(TemporalType.DATE)
private Date date;

JSF/Primefaces doesn't validate #AssertTrue

my entity class look like this:
public class TimeFrame implements Serializable {
#NotNull
private Date startTime;
#NotNull
#Future
private Date stopTime;
To have the right order I added:
#AssertTrue(message="Wrong order")
public boolean isTimeFrameValid(){
return this.startTime.before(stopTime);
}
Now, JSF/Primefaces should validate this directly after input.
<p:calendar id="aucstop" required="true" pages="2" value="#{transportbean.stopTime}" >
<p:ajax event="dateSelect" />
<f:validateBean/>
</p:calendar>
For example, when putting a past date into "StopTime" I got immediately a message. The "isTimeValid"-Mothod however is only validated when the whole object ist being created. Any ideas how to validate this directly after input?
GF 3.1.1 PrimeFaces 3.4

Resources