Why MyBatis # vs $ Matters: Fixing SQL IN Errors and Avoiding Injection

This article explains the difference between MyBatis #{ } and ${ } placeholders, why using ${ } can cause SQL injection and IN‑clause errors, and how to safely resolve the issue with proper parameter handling and the foreach tag.

Java Backend Technology
Java Backend Technology
Java Backend Technology
Why MyBatis # vs $ Matters: Fixing SQL IN Errors and Avoiding Injection

Introduction

This issue was discovered during a code optimization when some data could not be retrieved after a functional update; the root cause was the handling of # and $ in a MyBatis SQL statement.

SQL statements with # and $
SQL statements with # and $

Difference

#{ } is processed as a prepared‑statement parameter: MyBatis replaces it with ? and sets the value via PreparedStatement, automatically adding single quotes around string values (e.g., "'4,44,514'").

${ } performs direct string substitution; the value is inserted as‑is without surrounding quotes, which can lead to SQL injection vulnerabilities.

SQL injection occurs when malicious SQL commands are inserted into web forms, query strings, or other inputs, allowing attackers to execute arbitrary queries.

Application Scenarios

Use #{ } for DAO parameters in mapped SQL to benefit from pre‑compilation and safe handling.

Use ${ } only for dynamic identifiers such as column or table names, and never for user‑provided values.

Problem Analysis

Initially the SQL appeared correct when run directly in the database, but after outputting the generated SQL from the application, it was clear that the IN clause values were being wrapped in single quotes, turning wwlr.LabelId in(4,44,514) into wwlr.LabelId in('4,44,514'), causing missing data.

SQL output configuration
SQL output configuration

Solution

Quick Fix

Replacing # with $ seemed to work locally but failed in the Docker deployment because the environment applies an additional SQL‑injection protection layer that disables the unsafe substitution.

Using foreach Tag

The proper solution is to convert the string parameter into a List<Integer> and use MyBatis's foreach tag to build the IN clause safely.

foreach tag syntax
foreach tag syntax
Original source: https://m.toutiaocdn.com/i6685496024770806280
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.

JavaSQLMyBatisSQL injection
Java Backend Technology
Written by

Java Backend Technology

Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!

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.