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.

Programmer DD
Programmer DD
Programmer DD
Preventing SQL Injection in Java: JDBC, MyBatis, JPA & Hibernate Best Practices

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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaMyBatisSecurityJDBCSQL injectionHibernatejpa
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.