JPA的执行流程
大致整理下JPA的执行流程,中间省略了很多过程。
核心的类方法
- org.hibernate.sql.results.jdbc.internal.DeferredResultSetAccess#executeQuery
private void executeQuery() {
final LogicalConnectionImplementor logicalConnection = getPersistenceContext().getJdbcCoordinator().getLogicalConnection();
final QueryOptions queryOptions = executionContext.getQueryOptions();
try {
//这个地方是展示SQL的地方。trace级别
LOG.tracef( "Executing query to retrieve ResultSet : %s", finalSql );
// prepare the query
preparedStatement = statementCreator.apply( finalSql );
// set options
.....这部分代码省略
// bind parameters
// todo : validate that all query parameters were bound?
//这里才是我们这篇文章的主要想去探索的,如何打印展示的sql参数.
int paramBindingPosition = 1;
paramBindingPosition += limitHandler.bindLimitParametersAtStartOfQuery( limit, preparedStatement, paramBindingPosition );
for ( JdbcParameterBinder parameterBinder : jdbcSelect.getParameterBinders() ) {
parameterBinder.bindParameterValue(
preparedStatement,
paramBindingPosition++,
jdbcParameterBindings,
executionContext
);
}
.....这部分代码省略
}
由上面代码不难看出,两个点:
我们想打印的SQL:finalSql
我们想打印的SQL 预执行中的绑定的参数: jdbcSelect.getParameterBinders()
- 从parameterBinder.bindParameterValue调用点入口进入。
整个调用链路:
着重看一下org.hibernate.type.descriptor.jdbc.BasicBinder#bind方法:
@Override
public final void bind(PreparedStatement st, J value, int index, WrapperOptions options) throws SQLException {
if ( value == null ) {
//从代码中看出,日志的级别是trace级别。开启的JbdcBindingLogging的trace级别的日志就可以打印参数.
if ( JdbcBindingLogging.TRACE_ENABLED ) {
JdbcBindingLogging.logNullBinding(
index,
jdbcType.getDefaultSqlTypeCode()
);
}
doBindNull( st, index, options );
}
else {
if ( JdbcBindingLogging.TRACE_ENABLED ) {
JdbcBindingLogging.logBinding(
index,
jdbcType.getDefaultSqlTypeCode(),
getJavaType().extractLoggableRepresentation( value )
);
}
doBind( st, value, index, options );
}
}
从代码中看出,日志的级别是trace级别。开启的JbdcBindingLogging的trace级别的日志就可以打印参数.
如何开启JbdcBindingLogging的trace级别日志。
public @interface SubSystemLogging {
/**
* Base category name for sub-system style logging
*/
String BASE = "org.hibernate.orm";
.....
}
public interface JdbcBindingLogging {
String NAME = SubSystemLogging.BASE + ".jdbc.bind";
Logger LOGGER = Logger.getLogger( NAME );
boolean TRACE_ENABLED = LOGGER.isTraceEnabled();
}
由上面的两段代码的定义,可以看出,JdbcBindingLogging的类中定义的Logger的Name为"org.hibernate.orm.jdbc.bind",有Logger基础的应该知道再知道如何配置日志,这下知道怎么打印SQL参数了吧。
springboot的application.yml文件中设置:
logging:
level:
org:
hibernate:
orm:
jdbc:
bind: trace
即可打印日志:
Hibernate: select u1_0.id,u1_0.create_time,u1_0.nickname,u1_0.password,u1_0.update_time,u1_0.username from user u1_0 where u1_0.nickname=? limit ?,?
2023-02-14T13:44:01.545+08:00 TRACE 97770 --- [nio-1234-exec-8] org.hibernate.orm.jdbc.bind : binding parameter [1] as [VARCHAR] - [五玄]
2023-02-14T13:44:01.549+08:00 TRACE 97770 --- [nio-1234-exec-8] org.hibernate.orm.jdbc.bind : binding parameter [2] as [INTEGER] - [10]
2023-02-14T13:44:01.550+08:00 TRACE 97770 --- [nio-1234-exec-8] org.hibernate.orm.jdbc.bind : binding parameter [3] as [INTEGER] - [10]
总结
为了打印日志,网上找了许久,各类大咖没说一个所以然来,自己翻出来看看,总结一下供后续有需要的人借鉴。