大家使用的时候都知道#{}是起到一个占位符的作用,在JDBC中也就是对应的一个预编译,也就是对应JDBC中的PreparedStatement,源码中看到示例如下:
PreparedStatement pstmt = con.prepareStatement("UPDATE EMPLOYEES
SET SALARY = ? WHERE ID = ?");
pstmt.setBigDecimal(1, 153833.00)
pstmt.setInt(2, 110592)
1. 猜想
所以Mybatis中的#{}是怎么完成预编译的效果呢?根据这里可以猜想,应该是Mybatis中将#{}最后解析成了?占位符。
2. 验证
那就写个例子来debug看一下,我们以查询为例
@Data
public class A {
private Integer id;
private Integer age;
private String name;
private Date createTime;
}
Mapper如下
@Mapper
public interface ADAO {
@Select("SELECT * FROM A WHERE id = #{id}")
A selectA(int id);
}
测试类ADAOTest
@RunWith(SpringRunner.class)
@SpringBootTest
public class ADAOTest {
@Autowired
private ADAO adao;
@Test
public void selectA() {
A a = adao.selectA(2);
System.out.println(a);
}
这里只在关键地方打上断点,org.apache.ibatis.builder.SqlSourceBuilder#parse
已经完成了占位符?的替换,接下来就是获取PreparedStatement,在org.apache.ibatis.executor.SimpleExecutor#prepareStatement中。
这里的核心步骤已经标注出来了,有兴趣的话可以打个断点看看详细流程
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
// 在这一步中进行connect.prepareStatement(sql)
stmt = handler.prepare(connection, transaction.getTimeout());
// 这一步执行执行填充参数 stmt.setInt(1,"xxx");
handler.parameterize(stmt);
return stmt;
}
最后执行查询org.apache.ibatis.executor.statement.PreparedStatementHandler#query
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
}
3. 总结
猜想是对的,mybatis自定义的占位符#{} 最后被替换成JDBC的占位符?
所以,其实可以总结一下,其实所有的ORM框架,底层的原理都是JDBC的标准。
所以不管是Mybatis的占位符,还是JPA中的占位符,最终的执行流程都是解析成?这个占位符,别问为什么,问这就是标准。