Skip to Content
Course content

134: Executing Parameterized Queries with PreparedStatement

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I've spent a lot of time reviewing code from developers who are just starting out with database connectivity in Java. There is one pattern that consistently makes me nervous: the habit of building SQL queries using string concatenation. It seems intuitive at first—you have a variable, you have a query, so you just glue them together. But in a production environment, this is a disaster waiting to happen.

The temptation of string concatenation

Imagine you're building a simple member directory. You want to let a user search for a profile by their username. The "naive" approach looks something like this:

String username = request.getParameter("user");
String sql = "SELECT * FROM users WHERE username = '" + username + "'";
Statement statement = connection.createStatement();
ResultSet rs = statement.executeQuery(sql);

On the surface, this works. If the user types "alice", the query becomes SELECT * FROM users WHERE username = 'alice'. Everything is fine. But here is the problem: you are trusting the user to provide data, but you're treating that data as part of the executable command. You've essentially given the user a megaphone and told the database to do whatever the megaphone says.

When your database becomes an open door

This is where SQL Injection comes in. If I'm a malicious actor and I enter ' OR '1'='1 into your search box, your Java code happily concatenates that into the string. The resulting query sent to the database becomes:

SELECT * FROM users WHERE username = '' OR '1'='1'

Since '1'='1' is always true, the database ignores the username check entirely and returns every single user in your table. In a real-world scenario, a clever attacker could use this to bypass login screens, dump your entire customer list, or even drop your tables. I can't stress this enough: never, ever trust raw input in a SQL string.

Letting the driver handle the heavy lifting

The professional way to handle this is with a PreparedStatement. Instead of building a string, you define a template for your query using placeholders—represented by question marks (?). You send this template to the database first, and then you send the data separately.

String username = request.getParameter("user");
String sql = "SELECT * FROM users WHERE username = ?"; 
PreparedStatement pstmt = connection.prepareStatement(sql);
pstmt.setString(1, username); // The 1 refers to the first '?'
ResultSet rs = pstmt.executeQuery();

Notice the difference. By using pstmt.setString(), you aren't just plugging a string into a gap. You're telling the JDBC driver, "This value is data, and only data." If a user tries to enter ' OR '1'='1 now, the database will literally look for a user whose username is the string "' OR '1'='1". It won't execute it as code, and the attack fails.

The hidden performance win

Beyond security, there is a performance trade-off here that often goes unmentioned. When you use a regular Statement, the database has to parse, compile, and optimize the query every single time you run it. If you're running the same query a thousand times with different IDs, that's a lot of wasted effort.

A PreparedStatement is "pre-compiled." The database analyzes the query template once and caches the execution plan. When you swap out the parameters, the database just plugs the new values into the existing plan. It's faster, cleaner, and fundamentally more secure. It's simply the only way I'll accept parameterized queries in a professional codebase.




📋 Practical Task

Securing the Warehouse SKU Lookup

You have been handed a legacy piece of code for a warehouse management system. The current implementation for looking up a product by its SKU is vulnerable to SQL Injection. Your task is to refactor the findProductBySku method to use a PreparedStatement instead of string concatenation.

// VULNERABLE CODE - FIX THIS
public Product findProductBySku(Connection conn, String sku) throws SQLException {
    String sql = "SELECT * FROM products WHERE sku = '" + sku + "'";
    Statement stmt = conn.createStatement();
    ResultSet rs = stmt.executeQuery(sql);
    
    if (rs.next()) {
        return new Product(rs.getInt("id"), rs.getString("name"));
    }
    return null;
}

Requirements:

  • Replace the Statement with a PreparedStatement.
  • Use a parameterized placeholder (?) for the SKU value.
  • Ensure the SKU parameter is bound correctly using the appropriate setXXX method.
  • Maintain the existing return logic.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.