Understanding the Difference Between ${} and #{} in MyBatis and Their Impact on SQL Injection
This article explains how MyBatis' ${} and #{} placeholders differ in SQL generation, demonstrates their usage with code examples, analyzes the SQL injection risks of ${} versus the safety of #{} through prepared statements, and provides practical guidelines for choosing the appropriate placeholder in various scenarios.
MyBatis provides two placeholder syntaxes, ${} and #{}, which behave differently when constructing SQL statements.
1.1 Demonstration of ${} and #{}
Database table user contains sample data.
List<User> findByUsername(String username);
List<User> findByUsername2(String username);Mapper XML using
#{}: <!-- Using #{} -->
<select id="findByUsername" parameterType="java.lang.String" resultType="com.lscl.entity.User">
select * from user where username like #{username}
</select>
<!-- Using ${} (value must be provided) -->
<select id="findByUsername2" parameterType="java.lang.String" resultType="com.lscl.entity.User">
select * from user where username like '%${value}%'
</select>Test code executes both queries and prints the results, confirming that both approaches can retrieve data.
1.2 SQL Injection Issue
The ${} syntax directly concatenates the supplied value into the SQL string, which can lead to injection attacks, whereas #{} uses a prepared‑statement placeholder that safely escapes characters.
// Vulnerable test
List<User> userList2 = userDao.findByUsername2(" aaa' or 1=1 -- ");The generated SQL shows the injected condition, returning all rows.
1.3 Detailed Difference
#{}acts like a JDBC ? placeholder, adding quotes and escaping special characters, preventing injection. ${} inserts the raw value, so no escaping occurs.
1.4 How #{ } Prevents Injection
Under the hood, MyBatis calls PreparedStatement.setString(), which examines each character and escapes dangerous ones (e.g., single quotes) before sending the query to MySQL.
public void setString(int parameterIndex, String x) throws SQLException {
// ... checks for null, builds escaped string, sets bytes ...
}Debugging with breakpoints shows the final parameter passed to MySQL is safely quoted, e.g., 'aaa\' or 1=1 --'.
1.5 Application Scenarios
#{}should be used for ordinary condition values (WHERE clauses) because it prevents injection. ${} is useful when the value must become part of the SQL syntax itself, such as dynamic table names or column lists, but the developer must ensure the input is trusted.
1.6 Summary
• #{} uses prepared statements, automatically adds quotes, and escapes dangerous characters, thus protecting against SQL injection. • ${} performs raw string substitution, which can introduce injection vulnerabilities if untrusted input is used. • Choose #{} for parameter values and reserve ${} for cases where the value must participate in SQL syntax and is guaranteed safe.
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.
Java Architect Essentials
Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow together.
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.
