SQL Parameter Binder for MyBatis, Hibernate and JDBC Logs
Paste a MyBatis log with ==> Preparing and ==> Parameters lines, a Hibernate log with binding parameter lines, or any parameterized SQL plus its values, and get executable SQL with every placeholder replaced by a correctly quoted literal. The binder understands JDBC ? markers, PostgreSQL $1..$n (including repeated and two-digit numbers), and named :name, @name and #{name} parameters. It walks the SQL character by character, so question marks, colons and dollar signs inside strings, quoted identifiers, comments, dollar-quoted bodies and :: casts are left alone, and it reports any placeholder without a value and any value left over. Nothing is uploaded: everything runs in your browser.
Do more than sql parameter binder for mybatis, hibernate and jdbc logs — meet Chat2DB
Chat2DB is an AI-powered SQL client for Windows, macOS and Linux. Write SQL in natural language, format and optimize queries automatically, and manage MySQL, PostgreSQL, Oracle and 20+ other databases in one workspace.
How ORMs and drivers log SQL parameters
Almost every Java, Node.js or Python data layer sends SQL to the database as a prepared statement: the statement text with placeholders goes first, and the parameter values travel separately in a binary bind message. That is why logs never show the final SQL - the database itself never sees one. Each framework prints the two halves in its own format, and this binder stitches them back together.
MyBatis
With the mapper namespace logger at DEBUG (for example logging.level.com.example.mapper=debug in Spring Boot, or mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl), MyBatis prints "==> Preparing:" with every #{...} already turned into ?, then "==> Parameters:" with each value followed by its Java class in parentheses, and finally "<== Total" or "<== Updates". A null value is printed as a bare null. ${...} placeholders are string substitutions and are already inlined in the Preparing line. With the batch executor one Preparing line is followed by several Parameters lines; each becomes its own statement here.
Hibernate and Spring Data JPA
spring.jpa.show-sql or the org.hibernate.SQL logger prints the SQL with ? markers. Values come from a separate TRACE logger: org.hibernate.type.descriptor.sql.BasicBinder in Hibernate 5 (binding parameter [1] as [VARCHAR] - [abc]) and org.hibernate.orm.jdbc.bind in Hibernate 6 (binding parameter (1:VARCHAR) <- [abc]). The JDBC type in the brackets decides whether the value is quoted. Note that Hibernate prints a real NULL and the string "null" identically, so the binder treats [null] as SQL NULL.
Plain JDBC, node-postgres and other drivers
JDBC PreparedStatement uses positional ? markers (?? is the escape for a literal question mark in the PostgreSQL driver, which matters for jsonb operators such as ?, ?| and ?& - in ? mode every single ? outside strings and comments is treated as a placeholder). node-postgres, pgx, asyncpg and PostgreSQL's own PREPARE use $1, $2 ... and may repeat the same number; $10 is a different parameter from $1. Named styles such as :name (JPA, SQLAlchemy text(), Oracle), @name (SQL Server, Dapper) and #{name} (MyBatis mapper XML) take a JSON object or name=value lines. Quotes, comments, dollar-quoted bodies and :: casts are never touched.
Why the inlined SQL is not the same as the prepared statement
The output is meant for debugging: paste it into a SQL client, run EXPLAIN on it, or share a reproducible query in a bug report. It is not equivalent to what the application executed. With bind parameters the database can reuse a cached plan (PostgreSQL switches to a generic plan after five executions, Oracle and SQL Server cache plans per statement text), whereas literal values can produce a different, sometimes better, sometimes worse, plan - so a slow query may be fast when inlined, or the reverse. Implicit type conversion also differs: a bound VARCHAR compared to a numeric column is handled by the driver, while a quoted literal is cast by the server. Most importantly, never copy the inlined form back into application code. Building SQL by concatenating values is exactly how SQL injection happens; keep using placeholders in production and use this tool only to read logs.
How to use
- Paste a MyBatis or Hibernate log (timestamps, thread names and several statements are fine), or plain SQL with ?, $n or named placeholders. The input type is detected automatically; override it with the Input and Placeholders selectors if needed.
- For plain SQL, enter the parameters one per line or as a JSON array (JSON object or name=value lines for named placeholders). Wrap a value in single quotes to force a string, and use Quote every value, Booleans and Backslash escapes to match your database.
- Check the summary and any mismatch warnings, then copy the executable SQL into your SQL client to run or EXPLAIN it.
Frequently asked questions
How do I see the actual SQL with parameters from a MyBatis log?
MyBatis never logs the final SQL; it logs the Preparing statement with ? markers and a separate Parameters line such as 1(Integer), abc(String), null. Copy both lines (or a whole block of log output, prefixes included) into the binder. It pairs each Preparing line with the Parameters line that follows, splits the values on their (Type) suffixes so strings containing commas or parentheses stay intact, quotes String, Timestamp and LocalDateTime values, leaves Integer, Long and BigDecimal unquoted, and turns a bare null into NULL.
Why are some question marks or $1 in my SQL not replaced?
Placeholders inside single-quoted strings, double-quoted or backtick identifiers, -- and /* */ comments and PostgreSQL $$ dollar-quoted bodies are not parameters, so they are kept as written, and :: casts are never mistaken for :name parameters. A ?? is the JDBC escape for a literal ? and is output as a single ?. If a real placeholder has no value, it is left in place and listed in the warnings, as is any parameter that was never used. PostgreSQL jsonb operators ?, ?| and ?& look exactly like JDBC markers, so check those by hand.
Where can I run and EXPLAIN the bound SQL?
Paste the output into any SQL client connected to a development copy of the database. Chat2DB (https://chat2db.ai/download, or the web version at https://app.chat2db.ai) runs it against MySQL, PostgreSQL, Oracle, SQL Server and 20+ other databases, shows the execution plan, and its AI assistant can explain why the query is slow or rewrite it. Keep in mind that the inlined query may get a different plan from the prepared statement, and never paste inlined SQL back into application code.
