在实际开发过程中,如果选择了springboot+jpa的方式,那么和数据库的交互方式就被框架层'过度'封装,别误会,这里的过度封装没有贬义,只是个人水平有限,在翻阅hibernate以及jpa相关源码时,有点一头雾水,折腾蛮久,就是没定位到sql最终在数据库中执行的形式,大多数场景下,yml配置中配置
jpa: show-sql: true 或者
logging: level: org: hibernate: type.descriptor.sql.BasicBinder: trace SQL: debug 这两种形式可以在控制台中输出预执行sql和参数,通过自己替换占位参数也能拼出来sql。大多数场景也就可以满足日常调试需求。 记一次我遇到的场景。 循环执行一条查询sql,发现第二批sql始终返回为空,观察控制台打印的sql,将sql拼上去拿到数据库中执行发现可以查到数据,但是代码层面始终返回为空。 处理成的sql如下: SELECT ss.* FROM sim_signal_schedules ss WHERE ss.data_source = 2 AND ss.last_updated_date >= 'Mon Oct 25 11:03:26 CST 2021' and ss.schedule_id in ( 'b77b8df3-3272-4da3-9ff6-34fedc3ac823','bdf45c6e-47ed-44aa-b385-d5f04fe6cab0','d34878f9-fa12-4d17-bea1-45a37fc9eaa7'); 时间 Mon Oct 25 11:03:26 CST 2021是完全copy控制台输出的入参,in()里面的参数也是,数据库中通过这条sql确实有数据。 尝试解决的思路有: 1. 怀疑参数中存在特殊字符导致编码导致的(其实我这里不存在中文和一些特殊字符,关键问题是我for循环调用,前面已经有执行成功的iteration) 2. 怀疑jpa中sql书写的方式导致(虽然不想排查这个,因为还是存在成功的查询,没法解释) 3. sql最终的执行形态 最后发现有效的解决方案是3,通过修改配置,输出了jdbc最终的执行sql,发现是jpa中输出参数的binder对时间类型的数据类型的表达和数据库的表达不一致, 数据库中最终的执行sql为:SELECT ss.* FROM sim_signal_schedules ss WHERE ss.data_source = 2 AND ss.last_updated_date >= '10/25/2021 11:03:26.632'
and ss.schedule_id in ( 'b77b8df3-3272-4da3-9ff6-34fedc3ac823','bdf45c6e-47ed-44aa-b385-d5f04fe6cab0','d34878f9-fa12-4d17-bea1-45a37fc9eaa7'); 问题出现了,数据库中date类型中的数据精确到了毫秒,但是binder输出的时候只是精确到秒,坑爹的是我当前的场景下数据还真就精确到了毫秒,这个不同框架下对于date类型的表达,让我找了半天。。。 控制台输出jdbc的配置如下: pml中引入依赖:
<dependency> <groupId>com.googlecode.log4jdbc</groupId> <artifactId>log4jdbc</artifactId> <version>1.2</version> </dependency> yml文件配置:
datasource: ##打印最终执行sql配置 isPrimary: 1 platform: postgres url: jdbc:log4jdbc:postgresql://10.127.0.11:5432/ftc_simulation?stringtype=unspecified&useAffectedRows=true //注意这里在jdbc和postgresql中添加了log4j username: sim_dev password: sim_dev driver-class-name: net.sf.log4jdbc.DriverSpy //driver-class-name有修改 type: com.zaxxer.hikari.HikariDataSource