Search

Build a Servlet-based Application That Executes SQL Statements Against a Database

0 views

Planning the Project

When you decide to give yourself a quick, interactive tool for running arbitrary SQL against a database, the first step is to understand what you actually need to build. The goal is to create a small web application that accepts user input, passes that input to the database, and then displays the results or a confirmation message. You’ll be combining three core Java EE technologies: Java Servlets to handle requests and database logic, JSP to generate the user interface, and a plain Java helper class to keep your servlet code clean.

The “SQL Gateway” concept is handy when you’re prototyping a new schema, debugging a query, or simply want to poke at a database without writing a full application. Think of it as a lightweight, self‑contained SQL console that runs inside a browser. Because it relies on JDBC, it can target any relational database that offers a JDBC driver - MySQL, PostgreSQL, Oracle, SQL Server, and others. The code we’ll write uses MySQL as an example, but the changes required to switch databases are minimal: swap the driver class, adjust the JDBC URL, and supply the proper credentials.

Before writing a single line of code, outline the flow of the application. A user opens a browser, lands on a JSP page, types a query into a textarea, and clicks “Execute.” The request hits a servlet that pulls the text, determines whether it’s a SELECT or an update/DDL statement, runs the query, and then forwards back to the JSP. The servlet stores the user’s SQL and the formatted result in the session so the JSP can display them. Finally, the JSP renders the input area, the result table or message, and a new request form. This pattern keeps the presentation layer separate from business logic and keeps the servlet focused on database interaction.

In practice, the project requires: a servlet container (Apache Tomcat, Jetty, etc.), a database server (MySQL for this tutorial), and a build tool (Maven or plain IDE project). All code will be written in Java 8 or later, but the core concepts remain the same for newer releases. Keep the structure simple: place the servlet and helper class in a package such as com.example.sql, put the JSP under webapp/sql_gateway.jsp, and create a web.xml file that maps the servlet. This setup will keep the project organized and ready for further expansion.

Setting Up the Development Environment

Download and install a recent version of Apache Tomcat from the official site. You’ll need the catalina directory to deploy the web application. Next, install MySQL Community Server if you don’t already have a database running. Create a database named murach (or any name you prefer) and grant a user account full privileges on it. The tutorial uses the root account with no password for simplicity, but for production you should set a strong password and use a dedicated application user.

To get the JDBC driver, download the MySQL Connector/J jar from the MySQL website. Copy the jar into the lib directory of Tomcat (or add it to your project’s classpath if you’re using Maven). If you’re using a different database, download the corresponding driver and adjust the classpath accordingly.

Open your IDE and create a dynamic web project. Name it something descriptive, like SqlGatewayApp. Inside the src folder, create the package com.example.sql and add two classes: SQLGatewayServlet and SQLUtil. In the webapp folder, create WEB-INF/web.xml to declare the servlet mapping. The file might look like this:

Prompt
<web-app></p> <p> <servlet></p> <p> <servlet-name>SQLGatewayServlet</servlet-name></p> <p> <servlet-class>com.example.sql.SQLGatewayServlet</servlet-class></p> <p> </servlet></p> <p> <servlet-mapping></p> <p> <url-pattern>/sql/gateway</url-pattern></p> <p> </servlet-mapping></p> <p></web-app>

Place the JSP file at webapp/sql_gateway.jsp. This file will present the user interface and show results. Once all files are in place, build the project and deploy the .war file to Tomcat. Start the server and navigate to http://localhost:8080/SqlGatewayApp/sql_gateway.jsp to see the empty form. From here, you can start typing SQL and testing the application.

Crafting the User Interface with JSP

The JSP combines a simple form with a message display area. At the top of the file, a scriptlet pulls any previously stored SQL statement and result message from the session. This approach preserves the user's input and the last query’s outcome between requests, giving the user a consistent experience. The scriptlet looks like this:

Prompt
<%</p> <p> String sqlStatement = (String) session.getAttribute("sqlStatement");</p> <p> if (sqlStatement == null) sqlStatement = "";</p> <p> String message = (String) session.getAttribute("message");</p> <p> if (message == null) message = "";</p> <p>%>

Next, the form itself is straightforward. It contains a textarea sized to roughly 60 characters wide and eight rows tall, and a submit button. When the user submits, the request is sent to the servlet via a POST request. The textarea is pre‑filled with the current sqlStatement so the user can modify or re‑run a query. Here’s the HTML:

Prompt
<form method="post" action="sql/gateway"></p> <p> <b>SQL statement:</b><br></p> <p> <textarea name="sqlStatement" cols="60" rows="8">${sqlStatement}</textarea><br><br></p> <p> <input type="submit" value="Execute"></p> <p></form>

Below the form, a table displays the message returned by the servlet. The servlet will set this string to either an HTML table containing query results or a plain message indicating success or an error. The JSP simply prints the string inside a table with one row, like so:

Prompt
<b>SQL result:</b><br></p> <p><table cellpadding="5" border="1"></p> <p> ${message}</p> <p></table>

Using expression language (EL) instead of scriptlets makes the JSP cleaner and easier to read. With the layout and placeholders in place, you can now focus on implementing the servlet that handles the request and interacts with the database.

Building the Servlet to Handle SQL

The core of the application is the SQLGatewayServlet. It performs three main tasks: establishing a JDBC connection during initialization, parsing the user’s query to determine its type, executing the query, and then forwarding the result back to the JSP.

During init(), the servlet loads the MySQL driver and opens a connection that persists for the life of the servlet. This approach reduces connection overhead, but in a real application you might use a connection pool instead. The init() method looks like this:

Prompt
public void init() throws ServletException {</p> <p> try {</p> <p> Class.forName("com.mysql.cj.jdbc.Driver");</p> <p> String dbURL = "jdbc:mysql://localhost:3306/murach";</p> <p> String username = "root";</p> <p> String password = "";</p> <p> connection = DriverManager.getConnection(dbURL, username, password);</p> <p> } catch (ClassNotFoundException e) {</p> <p> throw new ServletException("MySQL driver not found.", e);</p> <p> } catch (SQLException e) {</p> <p> throw new ServletException("Could not connect to database.", e);</p> <p> }</p> <p>}

The destroy() method simply closes the connection when the servlet is taken out of service:

Prompt
public void destroy() {</p> <p> try {</p> <p> if (connection != null && !connection.isClosed()) {</p> <p> connection.close();</p> <p> }</p> <p> // Log error if you have a logger</p> <p> }</p> <p>}

Both doPost() and doGet() delegate to the same processing logic. The servlet extracts the sqlStatement from the request, trims whitespace, and checks the first six characters to decide if it’s a SELECT or another type of statement:

Prompt
protected void doPost(HttpServletRequest request,</p> <p> HttpServletResponse response)</p> <p> throws IOException, ServletException {</p> <p> doGet(request, response);</p> <p>}</p> <p>protected void doGet(HttpServletRequest request,</p> <p> HttpServletResponse response)</p> <p> throws IOException, ServletException {</p> <p> String sqlStatement = request.getParameter("sqlStatement");</p> <p> String message = "";</p> <p> try {</p> <p> Statement stmt = connection.createStatement();</p> <p> sqlStatement = sqlStatement.trim();</p> <p> String sqlType = sqlStatement.substring(0, Math.min(6, sqlStatement.length()));</p> <p> if ("select".equalsIgnoreCase(sqlType)) {</p> <p> ResultSet rs = stmt.executeQuery(sqlStatement);</p> <p> message = SQLUtil.getHtmlRows(rs);</p> <p> } else {</p> <p> int affected = stmt.executeUpdate(sqlStatement);</p> <p> if (affected == 0) {</p> <p> message = "<tr><td>Statement executed successfully.</td></tr>";</p> <p> } else {</p> <p> message = "<tr><td>Statement executed successfully. " +</p> <p> affected + " row(s) affected.</td></tr>";</p> <p> }</p> <p> }</p> <p> stmt.close();</p> <p> message = "<tr><td>Error executing the SQL statement: <br>" +</p> <p> e.getMessage() + "</td></tr>";</p> <p> }</p> <p> HttpSession session = request.getSession();</p> <p> session.setAttribute("sqlStatement", sqlStatement);</p> <p> session.setAttribute("message", message);</p> <p> RequestDispatcher dispatcher =</p> <p> request.getRequestDispatcher("/sql_gateway.jsp");</p> <p> dispatcher.forward(request, response);</p> <p>}

Notice that the servlet forwards back to the same JSP, passing along the input and result via session attributes. This keeps the request lifecycle short and gives the JSP all the data it needs to render.

Writing a Utility to Render Results

The servlet delegates the heavy lifting of converting a ResultSet into an HTML table to a static helper class, SQLUtil. Keeping this logic separate from the servlet makes the code more readable and easier to test. The utility method iterates over the metadata to build column headings, then loops through each row and column to add the data. Because multiple threads could call this method concurrently, it’s declared synchronized to avoid interference.

Prompt
public class SQLUtil {</p> <p> public static synchronized String getHtmlRows(ResultSet results) throws SQLException {</p> <p> StringBuilder html = new StringBuilder();</p> <p> ResultSetMetaData meta = results.getMetaData();</p> <p> int columnCount = meta.getColumnCount();</p> <p> // Header row</p> <p> html.append("<tr>");</p> <p> for (int i = 1; i <p> html.append("<th>")</p> <p> .append(meta.getColumnLabel(i))</p> <p> .append("</th>");</p> <p> }</p> <p> html.append("</tr>");</p> <p> // Data rows</p> <p> while (results.next()) {</p> <p> html.append("<tr>");</p> <p> for (int i = 1; i <p> html.append("<td>")</p> <p> .append(results.getString(i))</p> <p> .append("</td>");</p> <p> }</p> <p> html.append("</tr>");</p> <p> }</p> <p> return html.toString();</p> <p> }</p> <p>}

The method returns a string containing only <tr> and <td> tags, which the JSP places inside its result table. This design keeps the JSP lean and lets the servlet focus on business logic.

Deploying and Testing the Application

After writing the code, package the application into a .war file and drop it into Tomcat’s webapps directory. Tomcat will auto‑deploy the archive. Browse to http://localhost:8080/SqlGatewayApp/sql_gateway.jsp and you should see the query form. Try a simple query such as SELECT * FROM customers; to see a table of data. Next, run an update: UPDATE customers SET name='Alice' WHERE id=1;. The result area should show a row‑count message. Finally, test a DDL statement: DROP TABLE temp;. The message area will confirm execution with no rows affected.

If anything fails, check the Tomcat logs for stack traces. Common issues include incorrect driver class names, missing JDBC driver jar, or mismatched database credentials. Once you confirm the application works for a few statements, you can begin using it as a quick debugging tool during development.

Extending and Customizing

While the basic application works, there are several ways to enhance it for real‑world use. First, replace the single shared Connection with a connection pool such as HikariCP or Apache DBCP. This change reduces latency for concurrent users and handles database disconnects gracefully.

Second, add input validation or a list of safe statements to prevent accidental schema changes. For example, you could restrict DDL statements or log every executed query for audit purposes. Integrating a lightweight logging framework (SLF4J with Logback) lets you capture queries and errors without cluttering the console.

Third, consider moving the user interface to a modern frontend framework. Even a simple Bootstrap layout can improve usability: color the success messages green, errors red, and format the textarea with a monospace font. If you want to support syntax highlighting, integrate a library like CodeMirror or Ace Editor.

Finally, package the application as a Maven project. Add dependencies for the servlet API, JDBC driver, and any optional libraries. This approach simplifies build, version control, and deployment to cloud environments.

With these extensions, the SQL Gateway becomes a versatile component in your developer toolkit, allowing quick, interactive database exploration from any browser.

Suggest a Correction

Found an error or have a suggestion? Let us know and we'll review it.

Share this article

Comments (0)

Please sign in to leave a comment.

No comments yet. Be the first to comment!

Related Articles