Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.
Recently had a friend in discussing the difference between the # {} and ${}, has mentioned the # {} is replaced with string, and my personal understanding is that its role is largely placeholder, eventually replace string is not necessarily the result of the way, such as when we pass parameter type is plastic, eventually joining together of SQL, the reasonable should be plastic, instead of a string
Let’s take a look at how different parameter types in the Mapper interface are replaced in the final concatenation SQL
I. Environment configuration
We use SpringBoot + Mybatis + MySql to build the example demo
- Springboot: 2.2.0. RELEASE
- Mysql: 5.7.22
1. Project configuration
<dependencies>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
Copy the code
The core relies on mybatis-spring-boot-starter. For version selection, go to the MVN repository and find the latest one
Another unavailable db configuration information is appliaction.yml
spring:
datasource:
url: JDBC: mysql: / / 127.0.0.1:3306 / story? useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password:
Copy the code
2. Database tables
Database for testing
CREATE TABLE `money` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL DEFAULT ' ' COMMENT 'Username',
`money` int(26) NOT NULL DEFAULT '0' COMMENT 'money',
`is_deleted` tinyint(1) NOT NULL DEFAULT '0',
`create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Creation time',
`update_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Update Time'.PRIMARY KEY (`id`),
KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=551 DEFAULT CHARSET=utf8mb4;
Copy the code
The test data, mainly the name field, is a string with a value of one number
INSERT INTO `money` (`id`, `name`, `money`, `is_deleted`, `create_at`, `update_at`)
VALUES
(120.'120'.200.0.'the 2021-05-24 20:04:39'.'the 2021-09-27 19:21:40');
Copy the code
II. Determine the parameter type
This article ignores the details of Po, mapper interface and XML file in Mybatis. Interested partners can directly view the source code at the bottom (or view the previous blog can also be).
1. The parameter type is integer
For the above case, define an interface to query data by name, but the name parameter is an integer
Mapper interfaces:
/** * int; /** * int@param name
* @return* /
List<MoneyPo> queryByName(@Param("name") Integer name);
Copy the code
The corresponding XML file is as follows
<select id="queryByName" resultMap="BaseResultMap">
select * from money where `name` = #{name}
</select>
Copy the code
Name = 120; name = ‘120’; name = ‘120’;
So how do you determine what the resulting SQL will look like? This section describes a direct output mysql SQL execution log method
Run the following two commands on the mysql server to enable SQL execution logging
set global general_log = "ON";
show variables like 'general_log%';
Copy the code
When we access the above interface, we will find that the SQL statement sent to mysql is still integer after parameter substitution
select * from money where `name` = 120
Copy the code
2. Specify the jdbcType
When using #{}, ${}, we sometimes see jdbcType specified in addition to the parameter. Does this have any effect on the final SQL generation if we specify this in the XML?
<select id="queryByNameV2" resultMap="BaseResultMap">
select * from money where `name` = #{name, jdbcType=VARCHAR} and 0=0
</select>
Copy the code
The generated SQL is as follows
select * from money where `name` = 120 and 0=0
Copy the code
In actual SQL, this jdbcType does not affect the final SQL parameter concatenation, so what is it mainly used for? (This applies mainly to type conversion exceptions that may occur when null is passed in.)
3. The parameter type is String
When we pass arguments of type string, the final SQL sermon should be quoted
/** * if the parameter type is string, the "*" is automatically added@param name
* @return* /
List<MoneyPo> queryByNameV3(@Param("name") String name);
Copy the code
The corresponding XML
<select id="queryByNameV3" resultMap="BaseResultMap">
select * from money where `name` = #{name, jdbcType=VARCHAR} and 1=1
</select>
Copy the code
The final SQL generated above is as follows
select * from money where `name` = '120' and 1=1
Copy the code
4. TypeHandler implements mandatory quotation marks for parameter substitution
After reading the above sections, you can basically draw a simple inference (of course, it needs to be analyzed from the source code)
- SQL parameter replacement is not simply replaced by a string. In fact, it is determined by the parameter Java parameter type. If Java parameter type is string, the CONCatenated SQL is string format. The parameter is an integer, and the SQL is also an integer
So the question is, why do we need to know this?
- The key issue is index failure
SQL > select * from money where name = 120; SQL > select * from money where name = 120
Based on this, we have an application scenario. In order to avoid index failure due to parameter type problems, we want the name parameter to be passed, regardless of the actual parameter type, the final concatenated SQL, is a string format.
We implement this scenario with the help of a custom TypeHandler
@MappedTypes(value = {Long.class, Integer.class})
@MappedJdbcTypes(value = {JdbcType.CHAR, JdbcType.VARCHAR, JdbcType.LONGVARCHAR})
public class StrTypeHandler extends BaseTypeHandler<Object> {
/** * Java type to JDBC type **@param ps
* @param i
* @param parameter
* @param jdbcType
* @throws SQLException
*/
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, String.valueOf(parameter));
}
/** * JDBC type to Java type **@param rs
* @param columnName
* @return
* @throws SQLException
*/
@Override
public Object getNullableResult(ResultSet rs, String columnName) throws SQLException {
return rs.getString(columnName);
}
@Override
public Object getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return rs.getString(columnIndex);
}
@Override
public Object getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
returncs.getString(columnIndex); }}Copy the code
Then, in the XML, specify TypeHandler
/** * Implements Java <-> JDBC type interchange via a custom TypeHandler, which is passed in as int/long and converted to String *@param name
* @return* /
List<MoneyPo> queryByNameV4(@Param("name") Integer name);
Copy the code
<select id="queryByNameV4" resultMap="BaseResultMap">
select * from money where `name` = #{name, jdbcType=VARCHAR, typeHandler=com.git.hui.boot.mybatis.handler.StrTypeHandler} and 2=2
</select>
Copy the code
In this way, the output SQL will carry a single quotation mark, which can solve the problem from the source of the incorrect parameter type, resulting in the failure of the index
select * from money where `name` = '120' and 2=2
Copy the code
5. Summary
This article uses a simple example to test the impact of different parameter types in the Mapper interface on the final SQL generation
- If the parameter type is integer, the final SQL parameter substitution is also integer.
# {}
It’s not a simple string substitution. - If the parameter type is string, the final SQL parameter replacement is carried automatically
' '
(The ${}
Note that it does not automatically enclose single quotes, you need to add them manually.)
When we want to generate a string substitution for whatever type of argument we pass, we can use a custom TypeHandler to do this. This way, we can avoid indexing problems caused by implicit type conversion at the source
Finally, the question comes, is the above conclusion reliable? Where is the final SQL concatenated in Mybatis? What is the flow of this SQL concatenation?
About the whole process of SQL splicing, the follow-up blog will be online soon, I am a grey, pass by the big guy to help point a thumbs-up, price collection, give an evaluation
III. Can’t miss the source code and related knowledge points
0. Project
- Project: github.com/liuyueyi/sp…
- Source: github.com/liuyueyi/sp…
Series of blog posts:
- 【DB series 】Mybatis series of CURD basic use posture
- 【DB series 】Mybatis series of CURD basic use posture – notes
- 【DB series 】 Several posture of Mybatis parameter transfer
- 【DB series 】 The use of escape characters in Mybatis
1. Wechat official account: Yash Blog
All letter is better than no book, the above content, purely one’s words, due to the limited personal ability, it is hard to avoid omissions and mistakes, such as finding bugs or better suggestions, welcome criticism and correction, not grudging gratitude
Below a gray personal blog, record all the study and work of the blog, welcome everyone to go to stroll
- A grey Blog Personal Blog blog.hhui.top
- A Grey Blog-Spring feature Blog Spring.hhui.top