EclipseLink1.1.3 + Oracleでのおはなし。
下のようなJPQLを動的に生成しました。
SELECT e FROM Employee e LEFT OUTER JOIN FETCH e.company
するとあろうことか、下記のようなSQLが。
SELECT * FROM EMPLOYEE t0, COMPANY t1 WHERE t0.COMPANY_ID =* t1.ID ORA-00936: 式がありません。
なんじゃそりゃあ!
Oracleなんだから外部結合の演算子は"(+)="でしょう?
当然OraclePlatform(Oracle11Platform)を使用した上でこの挙動です。
しかも再現性は超不安定で、サーバーを起動してから上記のJPQLを実行する最初の1回でまれに発生します。
わけがわからなくて困っていたのですが、ようやく解決しました。
DatasourcePlatform
public Map getPlatformOperators() { if (platformOperators == null) { synchronized (this) { if (platformOperators == null) { initializePlatformOperators(); } } } return platformOperators; } protected void initializePlatformOperators() { this.platformOperators = new HashMap(); // Outer join addOperator(ExpressionOperator.equalOuterJoin()); 〜 省略 〜 }
ExpressionOperator
public static ExpressionOperator equalOuterJoin() { return simpleRelation(EqualOuterJoin, "=*"); }
OraclePlatform
protected void initializePlatformOperators() { super.initializePlatformOperators(); addOperator(operatorOuterJoin()); 〜 省略 〜 } protected ExpressionOperator operatorOuterJoin() { ExpressionOperator result = new ExpressionOperator(); result.setSelector(ExpressionOperator.EqualOuterJoin); Vector v = oracle.toplink.internal.helper.NonSynchronizedVector.newInstance(2); v.addElement(" (+) = "); result.printsAs(v); result.bePostfix(); result.setNodeClass(RelationExpression.class); return result; }
double-checked locking?
そんなかわいいもんじゃあなかった。
機能してないよ!
この排他制御機能してないよ!
つまり…OraclePlatform#initializePlatformOperators()の1行目の
super.initializePlatformOperators();
が実行されて、DatasourcePlatform#initializePlatformOperators()の1行目の
this.platformOperators = new HashMap();
が実行されてしまうと、DatasourcePlatform#getPlatformOperators()の1行目の
if (platformOperators == null) {
がtrueでなくなってしまい、中途半端にしか初期化されていないplatformOperatorsがreturnされる可能性があるわけですね!
一時はどうなることかと思ったけど、原因がわかってよかったです。