どうもこじらです。
LIKEとIN句を組み合わせたくてLIKE ANYをSpringのアプリケーションから実行しようとしたら色々と躓きました。
色々試しまくった結果正しいやり方が分かったので一応共有です。
Springのバージョンは3.3.3です。PostgreSQLのバージョンは17.2です。
Hibernateのバージョンは…Springに内包されてるやつだし別にいいか…。
事象
エラーは以下です。
org.postgresql.util.PSQLException: : op ANY/ALL (array) requires array on right side
ANYなのに配列渡されてねぇよ。
みたいなエラー内容です。
解決方法
LIKEを”~~”に変更したり、バインド変数に::arrayをつけたり迷走しまくりましたが、結果はシンプルでした。
Repositoryインターフェース
修正前のRepositoryインターフェースにおける該当のメソッドはこんな感じです。
原因の範囲はここだけです。
@Meta(comment = "find by name containing") @Query(value = "SELECT * FROM table_a WHERE name LIKE ANY(:names)", nativeQuery = true) List<TableA> findByNameContaining(List<String> names);
修正後はこうです。
@Meta(comment = "find by name containing") @Query(value = "SELECT * FROM table_a WHERE name LIKE ANY(:names)", nativeQuery = true) List<TableA> findByNameContaining(String[] names);
メソッドの引数をListから配列に変更しました。
原因は、JavaにおけるListをPostgres側における配列にうまくパースできていないことのようで、渡した値が文字列として認識されてしまっていたようです。
余談にはなりますが、Listで渡すことを諦めてString型で渡してもうまくいくっちゃうまくいくということ。
‘{%田中%, %高橋%}’みたいな文字列を渡せばうまくいくんだと思います。
まぁ、SQLインジェクション対策って何?みたいな実装方法になるのでやらないですがw
IN句の場合、ListというかCollectionクラスの値であればいい感じにパースしてくれていましたが、LIKE ANYのパターンでは、Spring Data JPAだかHibernateだか誰のロジックかは知りませんがSQLをうまく解釈してくれず、文字列として扱ってしまうっぽいですね。
Listはうまくいきませんでしたが配列は話が別らしいです。
配列をバインドパラメータに渡した場合、JPQLが検知してPostgresにおける配列にいい感じに変換してくれるらしく、String[]で渡せばSQLで小細工をせずに値を渡すことができます。
JavaでのList→配列
Stream#toArrayは初めて使いました。
List<String> nameList = List.of("%田中%", "%高橋%"); String[] nameArr = nameList.stream().toArray(String[]::new);
いやースマートだなぁ。
Stream#toList同様にJava16からじゃないとこの書き方できないですけどね。
Java16より前のバージョンの場合、Collectors.toArrayも存在してないっぽいんでStream使わず古き良き変換をした方が良さそうです。
“%”をJava上で連結させる必要があるところはスマートじゃないですが、まぁしょうがないか…。
ちょっと荒めの記事になってしまいましたが、今回はこんな感じで。
こじらでした
じゃ