在JPA懒惰列表上流

sim*_*imo 8 java jpa eclipselink java-8 java-stream

我有JPA实体,列表如下:

@OneToMany(mappedBy = "scadaElement", orphanRemoval = true)
private List<ElementParameter> elementParameters;
Run Code Online (Sandbox Code Playgroud)

和地图形式ElementParameter

@ManyToOne
@JoinColumn(name = "SCADAELEMENT_ID")
ScadaElement scadaElement;
Run Code Online (Sandbox Code Playgroud)

当我获得带有elementParameters列表的实体并对其执行流时,即使我使用.size()触发列表时,也不执行任何操作,但是当我使用for循环执行相同操作时,它也能正常工作.

System.out.println("elements size: " + s.getElementParameters().size());
s.getElementParameters()
            .stream()
            .forEach(
                    a -> { 
                        System.out.println("elementId: " + a.getId());
                    }
            );
Run Code Online (Sandbox Code Playgroud)

是否有任何解决方案可以使该流工作?我使用eclipselink作为JPA提供者.

Hol*_*ger 7

显然,你指的是这个问题.使用从实际实现继承的反模式(这里Vector)的这些惰性列表无法适应基类的演变.请注意,根据反模式的实现方式,有两种可能的结果

  • 如果在第一次使用时,延迟填充的列表会填充自身(它是继承状态的术语),新的继承方法将在第一次访问触发器属性时立即开始工作
  • 但是,如果列表覆盖所有访问器方法以强制委派给另一个实现,而不更新基类的状态,则基类的未被覆盖的方法将永远不会开始工作,即使已填充列表(来自子类'的观点)

显然,第二种情况适用于你.触发列表的填充不会使继承的forEach方法起作用.请注意,通过配置关闭延迟填充可能是更简单的解决方案.


对我来说,最简洁的解决方案是,如果IndirectList继承AbstractList并遵守Collection API标准,现在,在Collection API被取代后差不多二十年了Vector(我应该提一下年轻的JPA实际上是多少?).不幸的是,开发人员没有走那条路.相反,反模式通过创建另一个继承自已继承自未设计用于继承的类的类的类来最大化.该类重写了Java 8中引入的方法,并且可能在下一个Java发行版中获得另一个子类.

因此,好消息是,开发者希望每一个List是一个Vector没有拿定主意,但坏消息是这是行不通的,因为有时候,你会不会使用JPA 2.6得到扩展的Java 8特定版本.但显然,JPA 2.7将起作用.

所以你可以推导出一些替代解决方案:

  • 关掉懒惰的人口
  • 继续使用Java 7
  • 等待JPA 2.7
  • 只需复制集合,例如,
    List<ElementParameter> workList=new ArrayList<>(elementParameters);
    workList将支持所有Collection&Stream操作