除 blob 之外的语句参数的 Hibernate 跟踪值

and*_*ndy 5 java logging hibernate

为了跟踪 hibernate SQL 语句的参数值,常见的 log4j 参数化如下所示:

<logger name="org.hibernate.SQL">
  <level value="debug" />
</logger>
<logger name="org.hibernate.type.descriptor.sql.BasicBinder">
  <level value="trace" />
</logger>
Run Code Online (Sandbox Code Playgroud)

这会产生如下日志输出:

2019-01-10 00:10:29,349 [main] DEBUG  SqlStatementLogger.logStatement(SqlStatementLogger.java:92) - select land0_.fk_land as fk_land1_24_0_ from land land0_ where land0_.fk_land=?
2019-01-10 00:10:29,349 [main] TRACE  BasicBinder.bind(BasicBinder.java:65) - binding parameter [1] as [BIGINT] - [27]
Run Code Online (Sandbox Code Playgroud)

这对于分析应用程序路径在运行时如何执行非常有用。

问题是,这BasicBinder还记录了 LOB 参数值的整个字符串表示形式(例如byte[]),这是非常无用的:

2019-01-07 13:28:45,466 [wwsservices-catalina-exec-10] TRACE   org.hibernate.type.descriptor.sql.BasicBinder: binding parameter [2] as [BLOB] - [[37, 80, 68, 70, 45, 49, 46, 52, ...
Run Code Online (Sandbox Code Playgroud)

整个 blob 的字符串表示形式被打印到日志文件中,这对我来说非常烦人。

有没有办法在 Hibernate 或 log4j 中抑制/缩短 LOB 值的日志输出,同时仍然显示其他语句参数的值?

是否可以在 log4j 中设置最大日志语句大小?

Sel*_*ron 5

Hibernate 5.2.3已修复一个问题HHH-11097 ,这也应该可以解决您的问题:

此提交中,BlobTypeDescriptor(和其他人)获得了以下内容的覆盖extractLoggableRepresentation

@Override
public String extractLoggableRepresentation(Blob value) {
    return value == null ? "null" : "BLOB{...}";
}
Run Code Online (Sandbox Code Playgroud)

默认实现(导致您的问题)现在被覆盖,如下所示:

@Override
public String extractLoggableRepresentation(T value) {
    return (value == null) ? "null" : value.toString();
}
Run Code Online (Sandbox Code Playgroud)

这应该会从日志中删除那些巨大的行。

如果您使用的是最新版本的 hibernate,您可能会使用物化 blob/原始字节数组 ( byte[])。负责此操作的类型描述符是PrimitiveByteArrayTypeDescriptor,它疯狂地实现了extractLoggableRepresentation如下方法:

@Override
public String extractLoggableRepresentation(byte[] value) {
    return (value == null) ? super.extractLoggableRepresentation( null ) : Arrays.toString( value );
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下我看到的唯一解决方案是

自定义UserType必须实现nullSafeSet并将自定义传递JavaTypeDescriptor给进行绑定和日志记录的绑定器:

@Override
public void nullSafeSet(PreparedStatement st, Object value, int index,
        final SharedSessionContractImplementor session) throws HibernateException, SQLException {

    // Simply do what 
    // org.hibernate.type.AbstractStandardBasicType.nullSafeSet(PreparedStatement, Object, int, WrapperOptions)
    // does, but using a custom descriptor.

    session.remapSqlTypeDescriptor(MaterializedBlobType.INSTANCE.getSqlTypeDescriptor())
            .getBinder(CustomPrimitiveByteArrayTypeDescriptor.INSTANCE)
            .bind(st, (byte[]) value, index, session);
}
Run Code Online (Sandbox Code Playgroud)

自定义JavaTypeDescriptor只是扩展PrimitiveByteArrayTypeDescriptor并覆盖有问题的extractLoggableRepresentation方法:

public class CustomPrimitiveByteArrayTypeDescriptor extends PrimitiveByteArrayTypeDescriptor {
    public static final CustomPrimitiveByteArrayTypeDescriptor INSTANCE = new CustomPrimitiveByteArrayTypeDescriptor();

    @Override
    public String extractLoggableRepresentation(byte[] value) {
        if (null == value) {
            return super.extractLoggableRepresentation(value);
        } else {
            return "byte[" + value.length + "]";
        }
    }
}
Run Code Online (Sandbox Code Playgroud)