persistence.xmlに下記のpropertyを追加するとログにSQLが出力されるようになる。
<property name="eclipselink.logging.level.sql" value="FINE"/>
しかし、なぜかログに吐かれたSQLはシングルクォートが取り除かれている。
Query query = em.createNativeQuery("SELECT ID, VALUE FROM TABLE WHERE DATE = ? AND KND = '1'");
query.getResultList();
↓
SELECT ID, VALUE FROM TABLE WHERE DATE = ? AND KND = 1
=> bind [2016-11-16]
二つ連続しているシングルクォートはシングルクォート一つに変換されているので
文字列として展開されてしまっているということだろうか。
余計なことを!
原因を突き止めてやろうとeclipselink.jarのソースを奥深くまで辿っていくと、
ログメッセージに対してMessageFormatをかましているところがあった。
MessageFormatではメタ文字をエスケープするためにシングルクォートを使うので、
これが原因で間違いない。
public static buildMessage(/* 省略 */) {
/*
省略
*/
return MessageFormat.format(message, arguments);
}
eclipselinkがログに吐いているSQLはあくまで実行するために書かれたものなので、
あらかじめエスケープしておくなんてことはできない。
周辺を探してみてもMessageFormatをかましていないルートがない。
どっかにある設定を変えるだけでなんとかなんだろと思ってたのに…
いろいろ調べた結果、なんらかの設定でログの出力方法を変えるのは無理っぽい。
だったらログを出力しているクラス自体をなんとかして差し替えられないかと思い、調べてみた。
まず、上記のbuildMessageに辿り着くまでのスタックトレースはこんな感じ
EclipseLinkLocalization.buildMessage(String, String, Object[], boolean) line: 48
TraceLocalization.buildMessage(String, Object[], boolean) line: 30
TraceLocalization.buildMessage(String, boolean) line: 34
DefaultSessionLog(AbstractSessionLog).formatMessage(SessionLogEntry) line: 1003
DefaultSessionLog.log(SessionLogEntry) line: 142
IsolatedClientSession(AbstractSession).log(SessionLogEntry) line: 3491
IsolatedClientSession(AbstractSession).log(int, String, String, Object[], Accessor, boolean) line: 4681
DatabaseAccessor.basicExecuteCall(Call, AbstractRecord, AbstractSession, boolean) line: 615
DatabaseAccessor.executeCall(Call, AbstractRecord, AbstractSession) line: 558
IsolatedClientSession(AbstractSession).basicExecuteCall(Call, AbstractRecord, DatabaseQuery) line: 2002
IsolatedClientSession(ClientSession).executeCall(Call, AbstractRecord, DatabaseQuery) line: 298
CallQueryMechanism(DatasourceCallQueryMechanism).executeCall(DatasourceCall) line: 242
CallQueryMechanism(DatasourceCallQueryMechanism).executeCall() line: 228
CallQueryMechanism(DatasourceCallQueryMechanism).executeSelectCall() line: 299
CallQueryMechanism(DatasourceCallQueryMechanism).executeSelect() line: 281
DataReadQuery.executeNonCursor() line: 197
DataReadQuery.executeDatabaseQuery() line: 152
DataReadQuery(DatabaseQuery).execute(AbstractSession, AbstractRecord) line: 899
DataReadQuery.execute(AbstractSession, AbstractRecord) line: 137
DataReadQuery(DatabaseQuery).executeInUnitOfWork(UnitOfWorkImpl, AbstractRecord) line: 798
RepeatableWriteUnitOfWork(UnitOfWorkImpl).internalExecuteQuery(DatabaseQuery, AbstractRecord) line: 2896
RepeatableWriteUnitOfWork(AbstractSession).executeQuery(DatabaseQuery, AbstractRecord, int) line: 1804
RepeatableWriteUnitOfWork(AbstractSession).executeQuery(DatabaseQuery, AbstractRecord) line: 1786
RepeatableWriteUnitOfWork(AbstractSession).executeQuery(DatabaseQuery, List) line: 1751
EJBQueryImpl<X>(QueryImpl).executeReadQuery() line: 258
EJBQueryImpl<X>(QueryImpl).getResultList() line: 469
以下省略
これを見ると4行目のDefaultSessionLogが差し替えできそう。ロガーっぽくて、SessionLogというスーパークラスまたはインターフェースを持っていそうな名前だし。
DefaultSessionLogでググると下記のページがヒットした。
ロギング - IBM
ページ下部のLoggerの項目によると、eclipselink.logging.loggerプロパティを設定することでDefaultSessionLogの差し替えができそう。
自前のロガーを作る。
public class MySessionLog extends DefaultSessionLog {
@Override
protected String formatMessage(SessionLogEntry entry){
return entry.getMessage();
}
}
変に処理が変わって欲しくないのでDefaultSessionLogを継承し、MessageFormatをかましてるところだけをオーバーライド。
そのままメッセージを返すようにする。
続いてpersistence.xmlに以下のプロパティを追加。
<property name="eclipselink.logging.logger" value="{package名}.MySessionLog"/>
これでMySessionLogを使ってログが出力されるようになり、シングルクォートも消えなくなった。