Mybatis #占位符原理

1,275 阅读1分钟

博客索引

大家使用的时候都知道#{}是起到一个占位符的作用,在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);

}

@Select执行原理

这里只在关键地方打上断点,org.apache.ibatis.builder.SqlSourceBuilder#parse

占位符替换.png

已经完成了占位符?的替换,接下来就是获取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中的占位符,最终的执行流程都是解析成?这个占位符,别问为什么,问这就是标准。