(See the previous iteration.)What's newI have incorporated all the points of Dmytro Maslenko's answer. My code seems a little bit tidier.CodeAddPersonController.javapackage net.coderodde.web.db.fun.controllers;import java.io.IOException;import java.io.PrintWriter;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import net.coderodde.web.db.fun.model.FunnyPerson;/** * This controller is responsible for creating new persons. *  * @author Rodion rodde Efremov * @version 1.6 (Aug 8, 2017) */@WebServlet(name = AddPersonController, urlPatterns = {/add_person})public class AddPersonController extends HttpServlet {    /**     * Handles the HTTP <code>GET</code> method.     *     * @param request  the servlet request.     * @param response the servlet response.     * @throws ServletException if a servlet-specific error occurs.     * @throws IOException if an I/O error occurs.     */    @Override    protected void doGet(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        response.setContentType(text/html;charset=UTF-8);        try (PrintWriter out = response.getWriter()) {            out.println(Please use the POST method!);        }    }    /**     * Handles the HTTP <code>POST</code> method.     *     * @param request  the servlet request.     * @param response the servlet response.     * @throws ServletException if a servlet-specific error occurs.     * @throws IOException if an I/O error occurs.     */    @Override    protected void doPost(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        response.setContentType(text/html;charset=UTF-8);        try (PrintWriter out = response.getWriter()) {            String firstName = request.getParameter(first_name);            String lastName = request.getParameter(last_name);            String email = request.getParameter(email);            if (firstName.isEmpty()) {                out.println(The first name is empty.);                return;            }            if (lastName.isEmpty()) {                out.println(The last name is empty.);                return;            }            if (email.isEmpty()) {                out.println(The email is empty.);                return;            }            FunnyPerson person = new FunnyPerson();            person.setFirstName(firstName);            person.setLastName(lastName);            person.setEmail(email);            try {                DataAccessObject.instance().addPerson(person);                out.println(Person  + person +  created!);            } catch (RuntimeException ex) {                out.println(Error:  + ex.getCause().getMessage());            }        }    }    /**     * Returns a short description of the servlet.     *     * @return a String containing servlet description     */    @Override    public String getServletInfo() {        return This servlet adds new persons to the database.;    }}CreateDatabaseController.javapackage net.coderodde.web.db.fun.controllers;import java.io.IOException;import java.io.PrintWriter;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;/** * This controller creates the database if it is not yet created. *  * @author Rodion rodde Efremov * @version 1.6 (Aug 8, 2017) */@WebServlet(name = CreateDatabaseController, urlPatterns = {/create})public class CreateDatabaseController extends HttpServlet {    /**     * If not yet created, this request creates the database and the table.     *     * @param request  the servlet request.     * @param response the servlet response.     * @throws ServletException if a servlet-specific error occurs.     * @throws IOException if an I/O error occurs.     */    protected void processRequest(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        response.setContentType(text/html;charset=UTF-8);        try (PrintWriter out = response.getWriter()) {            try {                DataAccessObject.instance().createDatabase();                out.println(Database created!);            } catch (RuntimeException ex) {                out.println(Error:  + ex.getCause().getMessage());            }        }    }    /**     * Handles the HTTP <code>GET</code> method.     *     * @param request  the servlet request.     * @param response the servlet response.     * @throws ServletException if a servlet-specific error occurs.     * @throws IOException if an I/O error occurs.     */    @Override    protected void doGet(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        processRequest(request, response);    }    /**     * Handles the HTTP <code>POST</code> method.     *     * @param request  the servlet request.     * @param response the servlet response.     * @throws ServletException if a servlet-specific error occurs.     * @throws IOException if an I/O error occurs.     */    @Override    protected void doPost(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        processRequest(request, response);    }    /**     * Returns a short description of the servlet.     *     * @return a String containing servlet description.     */    @Override    public String getServletInfo() {        return Creates the database and the table.;    }}DataAccessObject.javapackage net.coderodde.web.db.fun.controllers;import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;import java.util.Objects;import java.util.regex.Matcher;import java.util.regex.Pattern;import net.coderodde.web.db.fun.model.FunnyPerson;public final class DataAccessObject {    /**     * For validating the email addresses.     */    public static final Pattern VALID_EMAIL_ADDRESS_REGEX =     Pattern.compile(^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$,                    Pattern.CASE_INSENSITIVE);    /**     * The SQL command for inserting a person.     */    private static final String INSERT_PERSON_SQL =             INSERT INTO funny_persons (first_name, last_name, email) VALUES  +            (?, ?, ?);;    /**     * Creates a new database if not already created.     */    private static final String CREATE_DATABASE_SQL =             CREATE DATABASE IF NOT EXISTS funny_db;;    /**     * Switches to 'funny_db'.     */    private static final String USE_DATABASE_SQL = USE funny_db;    /**     * Creates the table if not already created.     */    private static final String CREATE_TABLE_SQL =            CREATE TABLE IF NOT EXISTS funny_persons (\n +                id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,\n +                first_name VARCHAR(40) NOT NULL,\n +                last_name VARCHAR(40) NOT NULL,\n +                email VARCHAR(50) NOT NULL,\n +                created TIMESTAMP);;    /**     * The SQL for selecting a user given his/her ID.     */    private static final String GET_USER_BY_ID_SQL =             SELECT * FROM funny_persons WHERE id = ?;;    private final MysqlDataSource mysqlDataSource;    private DataAccessObject(MysqlDataSource mysqlDataSource) {        this.mysqlDataSource = Objects.requireNonNull(                mysqlDataSource,                 The MysqlDataSource is null.);    }    private static final DataAccessObject INSTANCE;    static {        MysqlDataSource mysqlDataSource = new MysqlDataSource();        mysqlDataSource.setUser(root);        mysqlDataSource.setPassword(your_password);        mysqlDataSource.setURL(jdbc:mysql://localhost:3306/funny_db);        INSTANCE = new DataAccessObject(mysqlDataSource);    }    public static DataAccessObject instance() {        return INSTANCE;    }    /**     * Adds a person to the database.     *      * @param person the person to add.     */    public void addPerson(FunnyPerson person) {        checkPerson(person);        try (Connection connection = mysqlDataSource.getConnection()) {            try (PreparedStatement statement =                     connection.prepareStatement(INSERT_PERSON_SQL)) {                statement.setString(1, person.getFirstName().trim());                statement.setString(2, person.getLastName().trim());                statement.setString(3, person.getEmail().trim());                statement.executeUpdate();            }        } catch (SQLException ex) {            throw new RuntimeException(ex);        }    }    /**     * Creates the empty database and the table.     */    public void createDatabase() {        try (Connection connection = mysqlDataSource.getConnection()) {            try (Statement statement = connection.createStatement()) {                statement.executeUpdate(CREATE_DATABASE_SQL);                statement.executeUpdate(USE_DATABASE_SQL);                statement.executeUpdate(CREATE_TABLE_SQL);            }        } catch (SQLException ex) {            throw new RuntimeException(ex);        }    }    /**     * Gets a user by his/her ID.     *      * @param id the ID of the user.     * @return a {@code FunnyPerson}object or {@code null}if there is not such     *         user.     */    public FunnyPerson getUserById(int id) {        try (Connection connection = mysqlDataSource.getConnection()) {            try (PreparedStatement statement =                     connection.prepareStatement(GET_USER_BY_ID_SQL)) {                statement.setInt(1, id);                try (ResultSet resultSet = statement.executeQuery()) {                    if (!resultSet.next()) {                        return null;                    }                    FunnyPerson person = new FunnyPerson();                    person.setId(resultSet.getInt(id));                    person.setFirstName(resultSet.getString(first_name));                    person.setLastName(resultSet.getString(last_name));                    person.setEmail(resultSet.getString(email));                    person.setCreated(resultSet.getDate(created));                    return person;                }            }        } catch (SQLException ex) {            throw new RuntimeException(ex);        }    }    private void checkPerson(FunnyPerson person) {        Objects.requireNonNull(person, The person is null.);        Objects.requireNonNull(person.getFirstName(),                                The first name is null.);        Objects.requireNonNull(person.getLastName(), The last name is null.);        Objects.requireNonNull(person.getEmail(), The email is null.);        if (person.getFirstName().trim().isEmpty()) {            throw new IllegalArgumentException(The first name is empty.);        }        if (person.getLastName().trim().isEmpty()) {            throw new IllegalArgumentException(The last name is empty.);        }        if (!validate(person.getEmail().trim())) {            throw new IllegalArgumentException(Invalid email address.);        }    }    /**     * Checks the email address.     *      * @param email the email address to validate.     * @return {@code true} if {@code email} is a valid email address.     */    private static boolean validate(String email ) {        Matcher matcher = VALID_EMAIL_ADDRESS_REGEX .matcher(email );        return matcher.find();    }}ShowPersonController.javapackage net.coderodde.web.db.fun.controllers;import com.google.gson.Gson;import java.io.IOException;import java.io.PrintWriter;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import net.coderodde.web.db.fun.model.FunnyPerson;/** * This controller is responsible for viewing persons. *  * @author Rodion rodde Efremov * @version 1.6 (Aug 8, 2017) */@WebServlet(name = ShowPersonController, urlPatterns = {/show/*})public class ShowPersonController extends HttpServlet {    /**     * Processes requests for both HTTP <code>GET</code> and <code>POST</code>     * methods.     *     * @param request  the servlet request.     * @param response the servlet response.     * @throws ServletException if a servlet-specific error occurs.     * @throws IOException if an I/O error occurs.     */    protected void processRequest(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        response.setContentType(text/html;charset=UTF-8);        try (PrintWriter out = response.getWriter()) {            String path = request.getPathInfo();            if (path.startsWith(/)) {                path = path.substring(1);            }            String[] tokens = path.split(/);            if (tokens.length == 0) {                out.println(Error: At least the user ID is required.);                return;            }            String idString = tokens[0];            int id = -1;            try {                id = Integer.parseInt(idString);            } catch (NumberFormatException ex) {                out.println(Error:  + idString +  is not an integer.);                return;            }            FunnyPerson person = DataAccessObject.instance().getUserById(id);            if (person == null) {                out.println(Error: no person with ID =  + id + .);                return;            }            String matchFirstName = null;            if (tokens.length == 2) {                matchFirstName = tokens[1];            }            if (!person.getFirstName().equals(matchFirstName)) {                response.sendRedirect(/DBWebFun/show/ + id + / +                                       person.getFirstName());                return;            }            Gson gson = new Gson();            out.println(gson.toJson(person));        }    }    /**     * Handles the HTTP <code>GET</code> method.     *     * @param request servlet request     * @param response servlet response     * @throws ServletException if a servlet-specific error occurs     * @throws IOException if an I/O error occurs     */    @Override    protected void doGet(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        processRequest(request, response);    }    /**     * Handles the HTTP <code>POST</code> method.     *     * @param request servlet request     * @param response servlet response     * @throws ServletException if a servlet-specific error occurs     * @throws IOException if an I/O error occurs     */    @Override    protected void doPost(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        processRequest(request, response);    }    /**     * Returns a short description of the servlet.     *     * @return a String containing servlet description     */    @Override    public String getServletInfo() {        return Shows the user info via ID/first_name;    }}The entire Maven project is here.Critique requestPlease tell me anything that comes to mind.
Practicing Java servlets, MySQL and JSON - follow-up
java;mysql;json;controller;servlets