Make writing a habit together! This is the sixth day of my participation in the “Gold Digging Day New Plan · April More text Challenge”. Click here for more details.

Type converters

In everyday enterprise development requirements, we input text fields that are all strings, but in the back end we can use other primitive types to accept data, or we can use entity classes to accept parameters. How does this work? SpringMVC provides a very rich built-in support for type converters, but in some cases it may be difficult to meet our needs, so we need to implement it ourselves

For example, if we build a data acquisition platform and collect data from different platforms, take the date format as YYYY-MM-DD on some platforms, YYYY /MM/ DD on some platforms, of course, it may be other formats. In order to achieve a unified implementation of the date, we need to define a type converter

1-1. Create a converter

Our current converter only supports YYYY-MM-DD YYYY /MM/ DD

package com.jony.converters; import org.springframework.core.convert.converter.Converter; import org.springframework.util.StringUtils; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class MyStringToDateConverter implements Converter<String, Date> { @Override public Date convert(String source) { if(! Stringutils.isempty (source)){DateFormat df=new: yyyY-MM-DD YYYY /MM/dd try {if(source.split("-").length==3){DateFormat df=new SimpleDateFormat("yyyy-MM-dd"); return df.parse(source); } else if(source.split("/").length==3){ DateFormat df=new SimpleDateFormat("yyyy/MM/dd"); return df.parse(source); } else {throw new RuntimeException(" date conversion error :"+source); } } catch (ParseException e) { e.printStackTrace(); } } return null; }}Copy the code

1-2. Create a User bean and set the birthday to the date format

package com.jony.entity; import org.junit.Test; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import java.util.Date; @Controller @RequestMapping("/converter") public class User { private String name; private Integer age; private Date birthday; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } @Override public String toString() { return "User{" + "name='" + name + ''' + ", age=" + age + ", birthday=" + birthday + '}'; }}Copy the code

1-3. Configure the class converter in spring. MVC

<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>

<bean class="org.springframework.context.support.ConversionServiceFactoryBean" id="conversionService">
    <property name="converters">
        <set>
            <bean class="com.jony.converters.MyStringToDateConverter"></bean>
        </set>
    </property>
</bean>
Copy the code

1-4, test

First use yyyy/MM/dd

Use YYYY-MM-DD again

1-5, summary

In a real project, we need to create a class converter based on the actual situation, so that we do not need to deal with it again in various businesses. The steps are as follows:

1. Define the source type and target of the type converter 2. Customize the implementation of the type conversion in the convert method 3. Configure the custom type converter \ in SpringMVC

<bean class="org.springframework.context.support.ConversionServiceFactoryBean" id="conversionService">
    <property name="converters">
        <set>
            <bean class="com.jony.converters.MyStringToDateConverter"></bean>
        </set>
    </property>
</bean>
Copy the code

4. Set up the SpringMVC annotation driver

<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
Copy the code

Second, data formatting

Spring provides two annotations, @NumberFormat and @DateTimeFormat, that can be used to format numbers, dates, and times. These tags can be used on Javabeans properties or method arguments. NumberFormat can be used to format any Number of primitive types (such as int, long) or instances of java.lang.Number (such as BigDecimal, Integer). @datetimeformat can be used to format the java.util.Date, java.util.Calendar and java.util.Long types.

2-1. Practical use

package com.jony.entity; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.NumberFormat; import java.util.Arrays; import java.util.Date; public class User { private Integer id; private String username; @DateTimeFormat(pattern="yyyy/MM/dd") private Date birthday; @ NumberFormat style = NumberFormat. Style. (CURRENCY) / / CURRENCY private Double balance; Private String[] hobbies; @NumberFormat(pattern = "#,###.##") private Double salary; / / salary 10000.00 @ NumberFormat style = NumberFormat. Style. (PERCENT) / * don't add % * 100 to display Plus will display precision you submit them to * / private Double taskCount; Public String[] getHobbies() {return hobbies; } public void setHobbies(String[] hobbies) { this.hobbies = hobbies; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public Double getBalance() { return balance; } public void setBalance(Double balance) { this.balance = balance; } public Double getSalary() { return salary; } public void setSalary(Double salary) { this.salary = salary; } public Double getTaskCount() { return taskCount; } public void setTaskCount(Double taskCount) { this.taskCount = taskCount; } @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + ''' + ", birthday=" + birthday + ", balance=" + balance + ", hobbies=" + Arrays.toString(hobbies) + ", salary=" + salary + ", taskCount=" + taskCount + '}'; }}Copy the code

2-2. Features and usage

Annotation type DateTimeFormat, mutually exclusive property:

Iso. A type of DateTimeFormat. ISO

  • Datetimeformat.iso. DATE: indicates the format YYYY-MM-DD.

  • Datetimeformat.iso. DATE_TIME: format YYYY-MM-DD HH: MM: ss.sssz.

  • Datetimeformat.iso. TIME: indicates the format HH:mm: ss.sssz.

  • Datetimeformat.iso. NONE: indicates the time that is not in ISO format.

The pattern. The type is String, using a custom time format String.

Style. The value consists of two characters. The first character indicates the date style, and the second character indicates the time format:

  • S: short date/time style;

  • M: Date/time style in;

  • L: long date/time style;

  • F: Full date/time style;

  • -: ignores date/time styles;

NumberFormat pattern. The type is String, using a custom numeric format String, “##,###.##”.

Style. The type is numberformat. Style, and the common values are:

  • Style.NUMBER Indicates the normal NUMBER type

  • Style.PERCENT Indicates the percentage type

  • CURRENCY Type of CURRENCY

3. Data verification

General situation, we can implement data validation in the front page, but you need to pay attention to the front-end data validation will exist insecurity, so normally we will use the front-end validation + back-end check way, so that both can satisfy the user’s experience, at the same time, it can guarantee the security of the data, Here’s how to do backend validation in SpringMVC.

3-1, JSR303

JSR303 is a Java standard for Bean data validation that has been included in JavaEE 6.0. JSR 303 (Java Specification Requests) specifies validation rules by annotating Bean attributes with standard annotations like @notnull, @max, etc., and validates beans through a standard validation interface.

Hibernate Validator (not Hibernate) implements the JSR349 technique for validating the annotation specification

JSR303:

Hibernate Validator extends Hibernate Validator

3-2. Use of JSR303

Spring has its own data validation framework, and supports the JSR303 standard validation framework, which can be annotated for data validation.

Add the org.hibernate dependencies to the org.hibernate package. Add the jar package to the WEB-INF/lib directory if you use an IDE. 3, in the need to validate javaBean properties on the corresponding validation annotations. 4. Add @VALID to the javaBean parameter corresponding to the processing method to be verified. 5. If BindingResult is used to handle the error, it will display the error message on the error page.

3-2-1. Importing JAR packages

<! -- https://mvnrepository.com/artifact/org.hibernate/hibernate-validator --> <dependency> < the groupId > org. Hibernate < / groupId > < artifactId > hibernate validator - < / artifactId > < version > 6.1.0. The Final < / version > </dependency>Copy the code

Put the newly imported package in web-INF /lib (IDE)

It was described in the previous article, but I won’t show it here.

3-2-3, annotate javaBean properties

Add @valid to the javaBean for method arguments

Access tests

As you can see, an error occurs when we submit data with id nullThen change the ID to 1

Error message processing

Add BindingResult to the control layer

The first step is to include the BindingResult class in the method, which will receive the message that the javaBean added the annotation error. GetFieldErrors () can be used to retrieve the set of error messages if there are any, which can either be returned directly to the page output, or the set can be iterated into a map so that it can be passed to the foreground and output error messages per field

package com.jony.controller; import com.jony.entity.User; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.validation.FieldError; import org.springframework.web.bind.annotation.RequestMapping; import javax.validation.Valid; import java.util.HashMap; import java.util.List; import java.util.Map; @Controller @RequestMapping("/valid") public class ValidController { @RequestMapping("/user") public String add(@Valid User user, BindingResult bindingResult, If (bindingresult.haserrors ()){if(bindingresult.haserrors ()){if(bindingresult.haserrors ()){ / / get all field error List < FieldError > fieldErrors = bindingResult. GetFieldErrors (); / / the error information iteration to map the for (FieldError FieldError: fieldErrors) {errMap. Put (FieldError. GetField (), FieldError. GetDefaultMessage ());  } // Put the error information in model to return to the page model.addattribute ("errors",errMap); return "user/add"; } System.out.println(user); return "show"; }}Copy the code

Error message received on page 3-2-4-2

We are adding pages and setting the value of each TAB so that the User can be brought to the view layer even if there is an error message when we jump from Controller to page again.

The 3-2-4-3, test

As you can see, since the ID cannot be null, the error message can be returned to the page after we commit, and the data of other values is not lost.

3-2-5. Use of other validation attributes

package com.jony.entity; import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.NotEmpty; import org.hibernate.validator.constraints.Range; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.NumberFormat; import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; import javax.validation.constraints.Past; import javax.validation.constraints.Pattern; import java.util.Arrays; import java.util.Date; Public class User {@notnull (message = "id cannot be empty ") @min (value=1,message = "ID must be greater than 0") private Integer ID; @notempty (message = "user name cannot be empty ") @length (min = 4, Max = 8,message =" User name must be between {min} and {Max} bits ") @pattern (regexp="^[0-9a-za-z]+$",message =" 0-9 or a-z") private String username; @past (message = "Have you crossed?" ) @DateTimeFormat(pattern="yyyy/MM/dd") private Date birthday; @ NumberFormat style = NumberFormat. Style. (CURRENCY) / / CURRENCY private Double balance; Private String[] hobbies; @numberFormat (pattern = "#,### #.##") @range (min = 2000, Max = 1000000,message = "salary must be between {min} and {Max} ") //Size Range is int Cannot be used to validate Double private Double salary; @range (min=0, Max =100,message = "Task completion must be between {min} and {Max} ") Range can control the scope of the larger @ NumberFormat style = NumberFormat. Style. (PERCENT) / * don't add % * 100 to display Plus will display precision you submit them to * / private Double taskCount; Public String[] getHobbies() {return hobbies; } public void setHobbies(String[] hobbies) { this.hobbies = hobbies; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public Double getBalance() { return balance; } public void setBalance(Double balance) { this.balance = balance; } public Double getSalary() { return salary; } public void setSalary(Double salary) { this.salary = salary; } public Double getTaskCount() { return taskCount; } public void setTaskCount(Double taskCount) { this.taskCount = taskCount; } @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + ''' + ", birthday=" + birthday + ", balance=" + balance + ", hobbies=" + Arrays.toString(hobbies) + ", salary=" + salary + ", taskCount=" + taskCount + '}'; }}Copy the code

The Springmvc form tag

Receiving error messages using HTML tags is tedious and requires some processing on both the Controller side and the view side, since spring provides us with spring form elements.

Features: Automatically binding and automatically displaying data. If a new label is added, ensure that the required object is available

The Form tag

  1. Support all HTTP request methods such as method= “put” put\delete submission

  2. Automatic data echo: The object whose data needs to be specified using modelAttribute

  3. 4. Dynamic data binding: Select, checkboxes, radiobottons, all can use Items to specify that the data source can be a list ItemValue and itemLabel are required when a List is javaBean. ItemValue and itemLabel are not required when a List is map.

4-1. Implementation differences between native HTML and Spring Form tags

** * Based on native HTML form implementation **Copy the code
  • 1. Error information is stored in the Request domain through map
  • 2. In the JSP, run ${errors.id} to obtain the corresponding error information
    • Based on the spring Form tag library implementation
  • 1. Add a handler that displays the JSP, be sure to pass an empty User to the model
  • 2. Import the Spring-Form tag library in JSP
  • 3. Be sure to add ModelAtrribute to the form label
  • 4. The corresponding form tags must all start with

4-2. Implementation process of Spring Form tag

4-2-1, add display JSP processing method

This must be passed to the User in the mode, as mentioned earlier in the method parameter to be passed directly to the view

@requestMapping ("/u/add") public String toAdd(user user){return "user/add"; }Copy the code

4-2-2. Modify Controller

You can see that the processing logic for the previous iteration error set has been unwound and put into the map

/** * Use the spring Form tag * @param user * @param bindingResult * @param model * @return */ @requestMapping ("/dataValidate") public String addNew(@Valid User user, BindingResult bindingResult, If (bindingresult.haserrors ()) {system.out.println (" validation failed "); return "user/add"; }else{ return "show"; }}Copy the code

4-2-3, JSP processing

4-2-3-1. Import the spring-form library

<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
Copy the code

4-2-3-2. Creation of the form

Make sure to add ModelAtrribute to the form tag, and then the tag starts with <form

<form:form action="${pageContext.request.contextPath}/form/user" method="post" modelAttribute="user"> <p> id: <form:input path="id"></form:input><form:errors path="id"></form:errors> </p> <p> username: <form:input path="username"></form:input><form:errors path="username"></form:errors> </p> <p> birthday:<form:input path="birthday"></form:input><form:errors path="birthday"></form:errors> </p> <p> balance:<form:input path="balance"></form:input><form:errors path="balance"></form:errors> </p> <p> salary:<form:input path="salary"></form:input><form:errors path="salary"></form:errors> </p> <p> taskCount:<form:input path="taskCount"></form:input><form:errors path="taskCount"></form:errors> </p> <p> hobbies: <%-- static datasource --%> <form:checkbox path="hobbies" value=" hobbies"> </form:label path="hobbies"> <form:checkbox path="hobbies" value=" dancing "></form:checkbox> <form:label path="hobbies"> </form:label> <hr> <%-- dynamic data source --%> <form:checkboxes path="hobbies" items="${list}"></form:checkboxes> <%--<form:select path="hobbies"></form:select>--%> < / p > < p > < form: button > submit < / form: button > < input type = "submit" value = "submit" > < / p > < / form: form >Copy the code

In the 4-2-4 s, test,

Be sure to jump from Controller to page and put in User, because our form has a modelAttribute that points to User

You can see that the error message is displayed successfully

4-2-5. Dynamic data binding

Dynamic data binding: Select, Checkboxes, Radiobottons, and all can use Items to specify data sources List (itemValue and itemLabel are required when a list is a javaBean), map(itemValue and itemLabel are not required)

4-2-5. Modify the jump view controller and add data

@requestMapping ("/u/add") public String toAdd(user user,Model Model){@requestMapping ("/u/add") public String toAdd(user user,Model Model){ List<String> strings = array.asList (" sing ", "dance "); model.addAttribute("list",strings); return "user/add"; }Copy the code

The 4-2-5-2, test

Disadvantages: By using list to pass values to the view, value and lable are the same. Generally this is not feasible, as shown in the following figure

Therefore, we need to modify the data and modify the controller again as follows:

* @param user * @return */ @requestMapping ("/u/add") public String toAdd(user user,Model Model){// List<String> strings = array.asList (" sing ", "dance "); // model.addAttribute("list",strings); Map<String,String> map=new HashMap<>(); The map. The put (" 1 ", "singing"); The map. The put (" 2 ", "dance"); model.addAttribute("list",map); return "user/add"; }Copy the code

Open up the view again, so value is the value we need.