Preventing SQL Injection in Java: JDBC, MyBatis, JPA & Hibernate Best Practices
This article explains Java persistence technologies—including JDBC, MyBatis, JPA, and Hibernate—highlights common patterns that cause SQL injection, and provides concrete techniques such as parameterized PreparedStatement, MyBatis #{ } binding, dynamic SQL whitelisting, and proper JPA/Hibernate query parameter usage to securely handle user input.
Introduction
The article covers Java persistence technologies, common scenarios that lead to SQL injection, and methods to avoid and fix injection.
Brief overview of JDBC, MyBatis, JPA/Hibernate.
Examples of vulnerable string concatenation.
Safe usage of parameterized queries with PreparedStatement.
MyBatis parameter binding using #{ } and pitfalls of ${ }.
Dynamic SQL techniques (if, choose, foreach) and how to whitelist inputs.
JPA/Hibernate HQL and native SQL injection risks and proper parameter binding.
JDBC
JDBC (Java Database Connectivity) is the core API for accessing databases in Java. Directly concatenating user input into SQL strings can cause injection.
// concat sql
String sql = "SELECT * FROM users WHERE name ='" + name + "'";
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(sql);Using parameterized queries with PreparedStatement prevents injection because the SQL is compiled once and parameters are bound later.
// use ? to bind variables
String sql = "SELECT * FROM users WHERE name = ?";
PreparedStatement ps = connection.prepareStatement(sql);
ps.setString(1, name);Some constructs like ORDER BY cannot use placeholders and require manual whitelisting.
MyBatis
MyBatis is a persistence framework between raw JDBC and full ORM. It simplifies JDBC code but still requires careful parameter handling.
Mapper interfaces are annotated or defined in XML. Using #{} generates a PreparedStatement with bound parameters, preventing injection.
@Mapper
public interface UserMapper {
User getById(int id);
}
<select id="getById" resultType="org.example.User">
SELECT * FROM user WHERE id = #{id}
</select>Using ${} injects raw strings and can lead to injection, e.g.,
<select id="getByName" resultType="org.example.User">
SELECT * FROM user WHERE name = '${name}' limit 1
</select>To safely handle dynamic ORDER BY, use a whitelist or if tags.
<select id="getUserListSortBy" resultType="org.example.User">
SELECT * FROM user
<if test="sortBy == 'name' or sortBy == 'email'">
ORDER BY ${sortBy}
</if>
<otherwise>
ORDER BY name
</otherwise>
</select>For IN clauses, use <foreach> with #{} to bind each element.
<select id="selectUserIn" resultType="com.example.User">
SELECT * FROM user WHERE name in
<foreach item="name" collection="nameList" open="(" separator="," close=")">
#{name}
</foreach>
</select>JPA & Hibernate
JPA is a standard ORM API; Hibernate is a common implementation. Both support JPQL/HQL and native SQL, and both are vulnerable to injection if queries are built by string concatenation.
// vulnerable HQL
Query<User> query = session.createQuery(
"from User where name = '" + name + "'", User.class);Correct usage employs positional or named parameters.
// positional
Query<User> query = session.createQuery(
"from User where name = ?", User.class);
query.setParameter(0, name);
// named
Query<User> query = session.createQuery(
"from User where name = :name", User.class);
query.setParameter("name", name);Native SQL should also use parameter binding.
String sql = "select * from user where name = :name";
Query query = session.createNativeQuery(sql);
query.setParameter("name", name);Additional tips include avoiding wildcard abuse in LIKE clauses and limiting input size to prevent denial‑of‑service attacks.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
