如何使用Hibernate打印带有参数值的查询string

是否有可能在Hibernate打印生成的SQL查询真正的价值而不是问号?

如果用Hibernate API不可能的话,你会如何build议打印带有实际值的查询?

您需要启用以下类别的日志logging :

  • org.hibernate.SQL – 设置为debug以logging执行时的所有SQL DML语句
  • org.hibernate.type – 设置为tracelogging所有的JDBC参数

所以一个log4jconfiguration可能看起来像这样:

 # logs the SQL statements log4j.logger.org.hibernate.SQL=debug # Logs the JDBC parameters passed to a query log4j.logger.org.hibernate.type=trace 

第一个等同于hibernate.show_sql=true legacy 属性 ,第二个打印绑定参数等等。

另一个解决scheme(基于非hibernate)将使用像P6Spy这样的JDBC代理驱动程序。

为了方便起见,这里是Logback(SLF4J)的configuration示例

 <appender name="SQLROLLINGFILE"> <File>/tmp/sql.log</File> <rollingPolicy> <FileNamePattern>logFile.%d{yyyy-MM-dd}.log</FileNamePattern> </rollingPolicy> <layout> <Pattern>%-4date | %msg %n</Pattern> </layout> </appender> <logger name="org.hibernate.SQL" additivity="false" > <level value="DEBUG" /> <appender-ref ref="SQLROLLINGFILE" /> </logger> <logger name="org.hibernate.type" additivity="false" > <level value="TRACE" /> <appender-ref ref="SQLROLLINGFILE" /> </logger> 

你的sql.log(例子)中的输出如下所示:

 2013-08-30 18:01:15,083 | update stepprovider set created_at=?, lastupdated_at=?, version=?, bundlelocation=?, category_id=?, customer_id=?, description=?, icon_file_id=?, name=?, shareStatus=?, spversion=?, status=?, title=?, type=?, num_used=? where id=? 2013-08-30 18:01:15,084 | binding parameter [1] as [TIMESTAMP] - 2012-07-11 09:57:32.0 2013-08-30 18:01:15,085 | binding parameter [2] as [TIMESTAMP] - Fri Aug 30 18:01:15 CEST 2013 2013-08-30 18:01:15,086 | binding parameter [3] as [INTEGER] - 2013-08-30 18:01:15,086 | binding parameter [4] as [VARCHAR] - com.mypackage.foo 2013-08-30 18:01:15,087 | binding parameter [5] as [VARCHAR] - 2013-08-30 18:01:15,087 | binding parameter [6] as [VARCHAR] - 2013-08-30 18:01:15,087 | binding parameter [7] as [VARCHAR] - TODO 2013-08-30 18:01:15,087 | binding parameter [8] as [VARCHAR] - 2013-08-30 18:01:15,088 | binding parameter [9] as [VARCHAR] - MatchingStep@com.mypackage.foo 2013-08-30 18:01:15,088 | binding parameter [10] as [VARCHAR] - PRIVATE 2013-08-30 18:01:15,088 | binding parameter [11] as [VARCHAR] - 1.0 2013-08-30 18:01:15,088 | binding parameter [12] as [VARCHAR] - 32 2013-08-30 18:01:15,088 | binding parameter [13] as [VARCHAR] - MatchingStep 2013-08-30 18:01:15,089 | binding parameter [14] as [VARCHAR] - 2013-08-30 18:01:15,089 | binding parameter [15] as [INTEGER] - 0 2013-08-30 18:01:15,089 | binding parameter [16] as [VARCHAR] - 053c2e65-5d51-4c09-85f3-2281a1024f64 

hibernate.cfg.xml更改为:

 <property name="show_sql">true</property> <property name="format_sql">true</property> <property name="use_sql_comments">true</property> 

在“log4j.properties”中包含log4j和以下条目:

 log4j.logger.org.hibernate=INFO, hb log4j.logger.org.hibernate.SQL=DEBUG log4j.logger.org.hibernate.type=TRACE log4j.appender.hb=org.apache.log4j.ConsoleAppender log4j.appender.hb.layout=org.apache.log4j.PatternLayout 

Log4JDBC是一个很好的解决scheme,它打印到数据库的确切的 SQL与参数的地方,而不是这里最stream行的答案不这样做。 这样做的一个主要便利是您可以直接将SQL复制到您的数据库前端并按原样执行。

http://log4jdbc.sourceforge.net/

https://code.google.com/p/log4jdbc-remix/

后者还输出查询结果的表格表示。

示例输出显示生成的SQL,带有参数以及来自查询的结果集表:

 5. insert into ENQUIRY_APPLICANT_DETAILS (ID, INCLUDED_IN_QUOTE, APPLICANT_ID, TERRITORY_ID, ENQUIRY_ID, ELIGIBLE_FOR_COVER) values (7, 1, 11, 1, 2, 0) 10 Oct 2013 16:21:22 4953 [main] INFO jdbc.resultsettable - |---|--------|--------|-----------|----------|---------|-------| 10 Oct 2013 16:21:22 4953 [main] INFO jdbc.resultsettable - |ID |CREATED |DELETED |CODESET_ID |NAME |POSITION |PREFIX | 10 Oct 2013 16:21:22 4953 [main] INFO jdbc.resultsettable - |---|--------|--------|-----------|----------|---------|-------| 10 Oct 2013 16:21:22 4953 [main] INFO jdbc.resultsettable - |2 |null |null |1 |Country 2 |1 |60 | 10 Oct 2013 16:21:22 4953 [main] INFO jdbc.resultsettable - |---|--------|--------|-----------|----------|---------|-------| 

2016年更新

最近我已经使用了log4jdbc-log4j2( https://code.google.com/archive/p/log4jdbc-log4j2/ )与SLF4j和logback。 我的设置所需的Maven依赖关系如下所示:

 <dependency> <groupId>org.bgee.log4jdbc-log4j2</groupId> <artifactId>log4jdbc-log4j2-jdbc4.1</artifactId> <version>1.16</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4j.version}</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>${logback.version}</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>$logback.version}</version> </dependency> 

然后,驱动程序和数据库的url如下所示:

 database.driver.class=net.sf.log4jdbc.sql.jdbcapi.DriverSpy database.url=jdbc:log4jdbc:hsqldb:mem:db_name #Hsql database.url=jdbc:log4jdbc:mysql://localhost:3306/db_name #MySQL 

我的logback.xmlconfiguration文件如下所示:这将输出带有参数的所有SQL语句以及所有查询的结果集表。

 <?xml version="1.0" encoding="UTF-8"?> <configuration> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n </pattern> </encoder> </appender> <logger name="jdbc.audit" level="ERROR" /> <logger name="jdbc.connection" level="ERROR" /> <logger name="jdbc.sqltiming" level="ERROR" /> <logger name="jdbc.resultset" level="ERROR" /> <!-- UNCOMMENT THE BELOW TO HIDE THE RESULT SET TABLE OUTPUT --> <!--<logger name="jdbc.resultsettable" level="ERROR" /> --> <root level="debug"> <appender-ref ref="STDOUT" /> </root> </configuration> 

最后,我必须在类path的根目录创build一个名为log4jdbc.log4j2.properties的文件,例如Mevn项目中的src / test / resources或src / main / resources。 这个文件有一行,如下所示:

 log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator 

以上将取决于你的日志库。 有关更多信息,请参阅https://code.google.com/archive/p/log4jdbc-log4j2上的文档;

示例输出:

 10:44:29.400 [main] DEBUG jdbc.sqlonly - org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:70) 5. select memberrole0_.member_id as member_i2_12_0_, memberrole0_.id as id1_12_0_, memberrole0_.id as id1_12_1_, memberrole0_.member_id as member_i2_12_1_, memberrole0_.role_id as role_id3_12_1_, role1_.id as id1_17_2_, role1_.name as name2_17_2_ from member_roles memberrole0_ left outer join roles role1_ on memberrole0_.role_id=role1_.id where memberrole0_.member_id=104 10:44:29.402 [main] INFO jdbc.resultsettable - |----------|---|---|----------|--------|---|-----| |member_id |id |id |member_id |role_id |id |name | |----------|---|---|----------|--------|---|-----| |----------|---|---|----------|--------|---|-----| 

您可以将类别行添加到log4j.xml中:

 <category name="org.hibernate.type"> <priority value="TRACE"/> </category> 

并添加hibernate属性:

 <property name="show_sql">true</property> <property name="format_sql">true</property> <property name="use_sql_comments">true</property> 

打开org.hibernate.typelogging器来查看实际参数如何绑定到问号上。

在使用弹簧引导的情况下,只需configuration这个:

aplication.yml

 logging: level: org.hibernate.SQL: DEBUG org.hibernate.type: TRACE 

aplication.properties

 logging.level.org.hibernate.SQL=DEBUG logging.level.org.hibernate.type=TRACE 

也没有什么了

HTH

你可以使用数据源代理来完成 ,就像我在这篇文章中描述的那样。

假设你的应用需要一个dataSource bean(例如通过@Resource ),你可以这样configurationdatasource-proxy

 <bean id="actualDataSource" class="bitronix.tm.resource.jdbc.PoolingDataSource" init-method="init" destroy-method="close"> <property name="className" value="bitronix.tm.resource.jdbc.lrc.LrcXADataSource"/> <property name="uniqueName" value="actualDataSource"/> <property name="minPoolSize" value="0"/> <property name="maxPoolSize" value="5"/> <property name="allowLocalTransactions" value="false" /> <property name="driverProperties"> <props> <prop key="user">${jdbc.username}</prop> <prop key="password">${jdbc.password}</prop> <prop key="url">${jdbc.url}</prop> <prop key="driverClassName">${jdbc.driverClassName}</prop> </props> </property> </bean> <bean id="proxyDataSource" class="net.ttddyy.dsproxy.support.ProxyDataSource"> <property name="dataSource" ref="testDataSource"/> <property name="listener"> <bean class="net.ttddyy.dsproxy.listener.ChainListener"> <property name="listeners"> <list> <bean class="net.ttddyy.dsproxy.listener.CommonsQueryLoggingListener"> <property name="logLevel" value="INFO"/> </bean> <bean class="net.ttddyy.dsproxy.listener.DataSourceQueryCountListener"/> </list> </property> </bean> </property> </bean> <alias name="proxyDataSource" alias="dataSource"/> 

现在Hibernate输出vs数据源代理:

 INFO [main]: ntdlCommonsQueryLoggingListener - Name:, Time:1, Num:1, Query:{[select company0_.id as id1_6_, company0_.name as name2_6_ from Company company0_][]} INFO [main]: ntdlCommonsQueryLoggingListener - Name:, Time:0, Num:1, Query:{[insert into WarehouseProductInfo (id, quantity) values (default, ?)][19]} INFO [main]: ntdlCommonsQueryLoggingListener - Name:, Time:0, Num:1, Query:{[insert into Product (id, code, company_id, importer_id, name, version) values (default, ?, ?, ?, ?, ?)][phoneCode,1,-5,Phone,0]} 

datasource-proxy查询包含参数值,您甚至可以添加自定义JDBC语句拦截器,以便您可以从集成testing中直接捕获N + 1个查询问题 。

将以下属性和值添加到您的log4j或logbackconfiguration中:

 org.hibernate.sql=DEBUG org.hibernate.type.descriptor.sql.BasicBinder=TRACE 
 **If you want hibernate to print generated sql queries with real values instead of question marks.** **add following entry in hibernate.cfg.xml/hibernate.properties:** show_sql=true format_sql=true use_sql_comments=true **And add following entry in log4j.properties :** log4j.logger.org.hibernate=INFO, hb log4j.logger.org.hibernate.SQL=DEBUG log4j.logger.org.hibernate.type=TRACE log4j.appender.hb=org.apache.log4j.ConsoleAppender log4j.appender.hb.layout=org.apache.log4j.PatternLayout 

这个答案对于这个问题有点不同。 有时候,我们只需要将sql仅用于运行时的debugging目的。 在这种情况下,有一种更简单的方法,在编辑器上使用debugging。

  • 在org.hibernate.loader.Loader.loadEntityBatch上放置一个断点(或者在栈上导航)。
  • 当执行暂停时,查看variablesthis.sql的值;

这是hibernate3.我不确定这在其他版本上工作。

解决scheme是正确的,但也logging结果对象的所有绑定。 为了防止这种情况,可以创build一个单独的appender并启用筛选,例如:

 <!-- A time/date based rolling appender --> <appender name="FILE_HIBERNATE" class="org.jboss.logging.appender.DailyRollingFileAppender"> <errorHandler class="org.jboss.logging.util.OnlyOnceErrorHandler"/> <param name="File" value="${jboss.server.log.dir}/hiber.log"/> <param name="Append" value="false"/> <param name="Threshold" value="TRACE"/> <!-- Rollover at midnight each day --> <param name="DatePattern" value="'.'yyyy-MM-dd"/> <layout class="org.apache.log4j.PatternLayout"> <!-- The default pattern: Date Priority [Category] Message\n --> <param name="ConversionPattern" value="%d %-5p [%c] %m%n"/> </layout> <filter class="org.apache.log4j.varia.StringMatchFilter"> <param name="StringToMatch" value="bind" /> <param name="AcceptOnMatch" value="true" /> </filter> <filter class="org.apache.log4j.varia.StringMatchFilter"> <param name="StringToMatch" value="select" /> <param name="AcceptOnMatch" value="true" /> </filter> <filter class="org.apache.log4j.varia.DenyAllFilter"/> </appender> <category name="org.hibernate.type"> <priority value="TRACE"/> </category> <logger name="org.hibernate.type"> <level value="TRACE"/> <appender-ref ref="FILE_HIBERNATE"/> </logger> <logger name="org.hibernate.SQL"> <level value="TRACE"/> <appender-ref ref="FILE_HIBERNATE"/> </logger> 

我喜欢这个log4j:

 log4j.logger.org.hibernate.SQL=trace log4j.logger.org.hibernate.engine.query=trace log4j.logger.org.hibernate.type=trace log4j.logger.org.hibernate.jdbc=trace log4j.logger.org.hibernate.type.descriptor.sql.BasicExtractor=error log4j.logger.org.hibernate.type.CollectionType=error 
 <appender name="console" class="org.apache.log4j.ConsoleAppender"> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n" /> </layout> </appender> <logger name="org.hibernate" additivity="false"> <level value="INFO" /> <appender-ref ref="console" /> </logger> <logger name="org.hibernate.type" additivity="false"> <level value="TRACE" /> <appender-ref ref="console" /> </logger> 

使用Hibernate 4和slf4j / log4j2,我尝试在log4j2.xmlconfiguration中添加以下内容:

 <Logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="trace" additivity="false"> <AppenderRef ref="Console"/> </Logger> <Logger name="org.hibernate.type.EnumType" level="trace" additivity="false"> <AppenderRef ref="Console"/> </Logger> 

但没有成功。

我通过这个线程发现,需要configurationhibernate使用的jboss日志logging框架来loggingslf4j。 我将以下参数添加到应用程序的VM参数中:

 -Dorg.jboss.logging.provider=slf4j 

它的function就像一个魅力。

如果你使用的是hibernate 3.2.xx使用

 log4j.logger.org.hibernate.SQL=trace 

代替

 log4j.logger.org.hibernate.SQL=debug 

你可以login这个:

 net.sf.hibernate.hql.QueryTranslator 

输出示例:

 2013-10-31 14:56:19,029 DEBUG [net.sf.hibernate.hql.QueryTranslator] HQL: select noti.id, noti.idmicrosite, noti.fcaducidad, noti.fpublicacion, noti.tipo, noti.imagen, noti.visible, trad.titulo, trad.subtitulo, trad.laurl, trad.urlnom, trad.fuente, trad.texto from org.ibit.rol.sac.micromodel.Noticia noti join noti.traducciones trad where index(trad)='ca' and noti.visible='S' and noti.idmicrosite=985 and noti.tipo=3446 2013-10-31 14:56:19,029 DEBUG [net.sf.hibernate.hql.QueryTranslator] SQL: select noticia0_.NOT_CODI as x0_0_, noticia0_.NOT_MICCOD as x1_0_, noticia0_.NOT_CADUCA as x2_0_, noticia0_.NOT_PUBLIC as x3_0_, noticia0_.NOT_TIPO as x4_0_, noticia0_.NOT_IMAGEN as x5_0_, noticia0_.NOT_VISIB as x6_0_, traduccion1_.NID_TITULO as x7_0_, traduccion1_.NID_SUBTIT as x8_0_, traduccion1_.NID_URL as x9_0_, traduccion1_.NID_URLNOM as x10_0_, traduccion1_.NID_FUENTE as x11_0_, traduccion1_.NID_TEXTO as x12_0_ from GUS_NOTICS noticia0_ inner join GUS_NOTIDI traduccion1_ on noticia0_.NOT_CODI=traduccion1_.NID_NOTCOD where (traduccion1_.NID_CODIDI='ca' )and(noticia0_.NOT_VISIB='S' )and(noticia0_.NOT_MICCOD=985 )and(noticia0_.NOT_TIPO=3446 ) 

Log4Jdbc插件将最适合您的要求。 它显示以下 –

 1. Complete SQL query being hit to the db 2. Parameter values being passed to the query 3. Execution time taken by each query 

请参考下面的链接来configurationLog4Jdbc-

 https://code.google.com/p/log4jdbc/ 

logging工作,但不完全你想要的或我想要一段时间,但P6Spy确实工作,

这里是简单的教程,以及为P6Spy MKYONG教程 。

对我来说,它就像魅力一样。

  1. 下载P6Spy库

获取“p6spy-install.jar”

  1. 提取它

提取p6spy-install.jar文件,查找p6spy.jarspy.properties

  1. 添加库依赖项

p6spy.jar添加到您的项目库依赖项中

  1. 修改P6Spy属性文件

修改您的数据库configuration文件。 您需要用P6Spy JDBC驱动程序replace现有的JDBC驱动程序 – com.p6spy.engine.spy.P6SpyDriver

原始是MySQL JDBC驱动程序 – com.mysql.jdbc.Driver

 <session-factory> <property name="hibernate.bytecode.use_reflection_optimizer">false</property> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.password">password</property> <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/mkyong</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> <property name="show_sql">true</property> </session-factory> 

将其更改为P6Spy JDBC驱动程序 – com.p6spy.engine.spy.P6SpyDriver

 <session-factory> <property name="hibernate.bytecode.use_reflection_optimizer">false</property> <property name="hibernate.connection.driver_class">com.p6spy.engine.spy.P6SpyDriver </property> <property name="hibernate.connection.password">password</property> <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/mkyong</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> <property name="show_sql">true</property> </session-factory> 
  1. 修改P6Spy属性文件修改P6Spy属性文件 – spy.properties

real driverreplace现有的MySQL JDBC驱动程序

 realdriver=com.mysql.jdbc.Driver #specifies another driver to use realdriver2= #specifies a third driver to use realdriver3= 

更改日志文件位置更改logfile属性中的日志文件位置,所有SQL语句都将login到该文件中。

视窗

 logfile = c:/spy.log 

* nix中

 logfile = /srv/log/spy.log 
  1. “spy.properties”复制到项目类path中

“spy.properties”复制到你的项目根目录下,确保你的项目可以find“spy.properties”,否则会提示“spy.properties”文件没有findexception。

使用Wireshark或类似的东西:

上面提到的答案都不会用正确的参数打印sql或是一个痛苦。 我通过使用WireShark实现了这一点, WireShark从查询中捕获从应用程序发送到Oracle / Mysql等的所有sql /命令。

 <!-- A time/date based rolling appender --> <appender name="FILE" class="org.apache.log4j.RollingFileAppender"> <param name="File" value="logs/system.log" /> <param name="Append" value="true" /> <param name="ImmediateFlush" value="true" /> <param name="MaxFileSize" value="200MB" /> <param name="MaxBackupIndex" value="100" /> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%d %d{Z} [%t] %-5p (%F:%L) - %m%n" /> </layout> </appender> <appender name="journaldev-hibernate" class="org.apache.log4j.RollingFileAppender"> <param name="File" value="logs/project.log" /> <param name="Append" value="true" /> <param name="ImmediateFlush" value="true" /> <param name="MaxFileSize" value="200MB" /> <param name="MaxBackupIndex" value="50" /> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%d %d{Z} [%t] %-5p (%F:%L) - %m%n" /> </layout> </appender> <logger name="com.journaldev.hibernate" additivity="false"> <level value="DEBUG" /> <appender-ref ref="journaldev-hibernate" /> </logger> <logger name="org.hibernate" additivity="false"> <level value="INFO" /> <appender-ref ref="FILE" /> </logger> <logger name="org.hibernate.type" additivity="false"> <level value="TRACE" /> <appender-ref ref="FILE" /> </logger> <root> <priority value="INFO"></priority> <appender-ref ref="FILE" /> </root> 

这里的所有答案都是有用的,但是如果你使用Spring应用程序上下文XML来设置会话工厂,那么设置log4j SQL级别variables只能让你成为那里的一部分,你还必须设置hibernate.show_sqlvariables在应用上下文本身让Hibernate开始实际显示值。

ApplicationContext.xml有:

 <property name="hibernateProperties"> <value> hibernate.jdbc.batch_size=25 ... <!-- Other parameter values here --> hibernate.show_sql=true </value> </property> 

而你的log4j文件需要

 log4j.logger.org.hibernate.SQL=DEBUG 

mysql的jdbc驱动程序已经提供了一个方便的方式来满足这个要求,你至less要有jar版本> = mysql-connect-jar-5.1.6.jar

第1步:[configuration您的jdbc.url添加logging器和自定义日志logging]

  jdbc.url=jdbc:mysql://host:port/your_db?logger=com.mysql.jdbc.log.Slf4JLogger&profileSQL=true&profilerEventHandler=com.xxx.CustomLoggingProfilerEventHandler 

现在,它正在使用slf4j日志logging,如果你的默认日志logging是log4j,那么必须添加slf4j-api,slf4j-log4j12依赖关系才能使用slf4j日志logging

第2步:[写你的自定义日志logging]

 package com.xxx; import java.sql.SQLException; import java.util.Properties; import com.mysql.jdbc.Connection; import com.mysql.jdbc.log.Log; public class CustomLoggingProfilerEventHandler implements ProfilerEventHandler { private Log log; public LoggingProfilerEventHandler() { } public void consumeEvent(ProfilerEvent evt) { /** * you can only print the sql as this.log.logInfo(evt.getMessage()) * you can adjust your sql print log level with: DEBUG,INFO * you can also handle the message to meet your requirement */ this.log.logInfo(evt); } public void destroy() { this.log = null; } public void init(Connection conn, Properties props) throws SQLException { this.log = conn.getLog(); } } 

这是什么为我工作,在log4j.file下面的属性设置:

 log4j.logger.org.hibernate.type.descriptor.sql.BasicBinder=TRACE 

hibernate属性设置:

 hibernate.show_sql=true