Explain Codes LogoExplain Codes Logo

Cleanest way to build an SQL string in Java

sql
prepared-statements
sql-injection
spring-jdbc
Nikita BarsukovbyNikita Barsukov·Nov 1, 2024
TLDR

For building SQL strings in Java, it's best to resort to PreparedStatement that uses ? placeholders. Not only does this ensure security but it also facilitates efficiency.

Example:

// Instantiate PreparedStatement with SQL query PreparedStatement statement = connection.prepareStatement( "SELECT * FROM users WHERE name = ? AND age = ?"); // Set the query parameters statement.setString(1, "Alice"); statement.setInt(2, 30);

This technique helps shield applications against SQL injection, simplifies dynamic value insertion, and results in clean, readable code.

Prepare for a dynamic world with Prepared Statements

In cases when dealing with multifaceted SQL queries, maintaining queries within the codes can lead to a mess. Storing them in an external properties file works great:

// Load the query from an external .properties file Properties prop = new Properties(); prop.load(new FileInputStream("queries.properties")); String userQuery = prop.getProperty("SELECT_USERS"); // An unnamed superhero flies in PreparedStatement statement = connection.prepareStatement(userQuery);

Where queries.properties is:

SELECT_USERS=SELECT * FROM users WHERE name = ? AND age = ?

This allows your code to be clutter-free and well-structured when dealing with multiple queries.

Utility class: your vigilant watchman

A utility class for loading and managing queries streamlines your process:

// Define a utility class to simplify query loading. public class QueryLoader { private Properties queries; public QueryLoader(String path) throws IOException { queries = new Properties(); queries.load(new FileInputStream(path)); } public String getQuery(String key) { return queries.getProperty(key); } }

Using it is easy:

// Load your properties file with your SQL queries QueryLoader loader = new QueryLoader("queries.properties"); // Your vigilante comes to the rescue again PreparedStatement statement = connection.prepareStatement(loader.getQuery("SELECT_USERS"));

jOOQ: for queries that matter

In cases of large scale applications and complex SQL, a library like jOOQ is a life-saver. It offers a fluent API that allows constructing type-safe SQL queries. More details on jOOQ.org.

// Create a DSLContext for your SQL dialect DSLContext sql = DSL.using(SQLDialect.MY_SQL); // Finally, something that looks like English! Result<Record> result = sql.select() .from("users") .where(field("name").eq("Alice")) .and(field("age").eq(30)) .fetch();

SQLJ: not your regular SQL

SQLJ lets you weld SQL statements directly within Java:

// Embed SQL directly within Java code. Cool, right? #sql { SELECT age, name FROM users INTO :age, :name WHERE id = :userId };

It even conducts compile-time checking and allows efficient binding of Java variables to SQL query parameters using the : prefix.

The Groovy groove

If the environment allows for Groovy, you can enjoy the benefits of its readable syntax for SQL strings:

// From Java to Groovy. That escalated quickly! String query = """SELECT * FROM users WHERE name = '$name' AND age = $age"""

But be sure to resort to parameterized queries, even in Groovy, to prevent nasty SQL injection attacks.

Robust SQL string construction with Spring JDBC

The Spring Framework makes SQL handling even more robust with its NamedParameterJdbcTemplate:

// Define parameters map MapSqlParameterSource params = new MapSqlParameterSource() .addValue("name", "Alice") .addValue("age", 30); NamedParameterJdbcTemplate template = new NamedParameterJdbcTemplate(dataSource); // JDBC doing the heavy lifting for you template.queryForObject("SELECT * FROM users WHERE name = :name AND age = :age", params, new BeanPropertyRowMapper<>(User.class));

This not only separates SQL from Java variables but also makes the code safer and mainstream.