List<Long>ids=Arrays.asList(newLong[]{1L,2L,3L});List<PersonEntity>persons=em.createQuery("SELECT p FROM Person p WHERE p.id IN :ids").setParameter("ids",ids).getResultList();
此方法虽然在性能上没啥问题,但仍有以下缺点:
1.有些数据库如 Oracle 对 IN 子句的参数个数有限制,如果传入的主键数量太多,会产生 SQL 语法级别的错误
14:32:57,602 DEBUG SQL:92 – select personenti0_.id as id1_0_0_, personenti0_.firstName as firstNam2_0_0_, personenti0_.lastName as lastName3_0_0_ from Person personenti0_ where personenti0_.id in (?,?,?)
从下面生成的日志可以看出,如果我们传入的 id 数量大于批大小,那么 Hibernate 会生成多个 select 语句:
12
15:20:52,314 DEBUG SQL:92 – select personenti0_.id as id1_0_0_, personenti0_.firstName as firstNam2_0_0_, personenti0_.lastName as lastName3_0_0_ from Person personenti0_ where personenti0_.id in (?,?)
15:20:52,331 DEBUG SQL:92 – select personenti0_.id as id1_0_0_, personenti0_.firstName as firstNam2_0_0_, personenti0_.lastName as lastName3_0_0_ from Person personenti0_ where personenti0_.id in (?)
PersonEntityp=em.find(PersonEntity.class,1L);log.info("Fetched PersonEntity with id 1");Sessionsession=em.unwrap(Session.class);List<PersonEntity>persons=session.byMultipleIds(PersonEntity.class).enableSessionCheck(true).multiLoad(1L,2L,3L);
日志如下:
123
15:34:07,449 DEBUG SQL:92 – select personenti0_.id as id1_0_0_, personenti0_.firstName as firstNam2_0_0_, personenti0_.lastName as lastName3_0_0_ from Person personenti0_ where personenti0_.id=?
15:34:07,471 INFO TestMultiLoad:118 – Fetched PersonEntity with id 1
15:34:07,476 DEBUG SQL:92 – select personenti0_.id as id1_0_0_, personenti0_.firstName as firstNam2_0_0_, personenti0_.lastName as lastName3_0_0_ from Person personenti0_ where personenti0_.id in (?,?)
从代码中可以看出我们首先加载了 id 为 1 的实体,然后我们再批量加载 id 为 1,2,3 的实体,但是 Hibernate 检测到 id 为 1 的实体已经在缓存中了,生成的 SQL 则只加载剩余的两个实体。
总结
在开发中按主键批量加载实体情况还是比较常见的,我们可以简单的通过 JPQL 进行实现,但要考虑生成的 SQL 是否能符合数据库的限制,以及在性能上是否存在问题。Hibernate 引入的 MultiIdentifierLoadAccess 接口为开发人员提供了开箱即用的功能,让我们能通过简单的调用 API 即可实现性能优良的功能,把关注点更多的放在业务上而不是技术实现上。