今天同事碰到一个JPA Query 的问题, 在业务上我们有4个entity
CustomerSetType
一对多个 CustomerSet
CustomerSet
多对一 CustomerSetType, 多对一 master CustomerSet, 一对多 Customer
Customer
一对多 Client
Client
由于业务性质就不具体贴出代码, 基本的实体间关系就如上述所说。 我们使用的是ElipseLink 的 JPA 实现. 在配置多对一, 一对多的 FetchType 都是默认的设置。 也就是说多对一默认的fetchType 是 Eager, 一对多的是Lazy。
现在有一个NameQuery : select cs from CustomerSet cs where cs.customerType.code = 'ABC'
写一个简单的unit test 跑跑, 从log 看到EclipseLink 大约用了 700 多个 query。 汗一个, 难怪客户不爽, 要求改进。
改进的思路是使用 JPA query 的Hint , 只改query的配置, 保持代码annotation的配置不变还是默认方式。 这样基本上以前的代码就不需要改动。
具体的进行过程中碰到的问题:
1, 好像EclipseLink 在 NamedQuery 中 采用标准的JPA lint 名字 , 比如
<hint name="LEFT_FETCH" value="cs.customerSetType"></hint>
从log 的结果来看没有具体效果
2, 只好到EclipseLink 官方网站 找线索, 突然想起上次做查询 需要 Cursor 的时候用Hint 的时候 也碰到一个问题 标准的JPA hint name 它就是不支持。 根据这个提示找到 一个 hint name : eclipselink.left-join-fetch, 为什么要用到 left join 呢? 因为这个CustomerSet 下面可能是还没有关联上 Customer , 同理 Customer 还没有关联上 Client 的。 CustoomerSet,Customer 到Client 有3级, 我们只需要配置 fetch join 到 cs.customerlist.clientlist , 从输出的sql 可以看到有两个 left join , 一个是 customer set left join customer, 一个是 left join client。 再看sql 已经减少到 几十个。为什么还还有这么多呢? 在customer set 实体上一个有 3个一对多的关系, 我们现在只处理掉一个, 还有两个呢, 它到 customer set type, 它自身join 的 master customer set。 如法炮制, 但是它们俩就不需要 left join 了, eclipselink.join-fetch 搞定。
再来看输出的sql 只有 4个了, 主要是因为它自己的级联出来的 master customer set 需要再去搞定 type name 啥的, 基本上没有办法再精简了。
回头想想, 如果现在的OR Mapping 能做到这个效果,效率不比我们自己native sql 出来再拼装 对象的方式差吧。 做个记号,要去down 个 EclipseLink 的代码来看看它的实现补补课了。