【发布时间】:2016-02-12 12:22:56
【问题描述】:
在我的一项网络服务中检测到缺陷后,我将错误追踪到以下单行:
return this.getTemplate().getDomains().stream().anyMatch(domain -> domain.getName().equals(name));
当我确定域列表包含一个名称等于提供的name 的域时,此行返回错误。所以在挠了一阵头之后,我最终把整条线都分开了,看看发生了什么。我在调试会话中得到以下信息:
请注意以下行:
List<Domain> domains2 = domains.stream().collect(Collectors.toList());
根据调试器,domains 是一个包含两个元素的列表。但是在申请.stream().collect(Collectors.toList()) 之后,我得到了一个完全空的列表。如果我错了,请纠正我,但据我了解,这应该是身份操作并返回相同的列表(如果我们严格,则返回相同的列表)。那么这里发生了什么???
在你问之前:不,我根本没有操纵那个屏幕截图。
为了将其放在上下文中,此代码在有状态请求范围的 EJB 中执行,使用 JPA 托管实体在扩展的持久性上下文中具有字段访问权限。这里有一些与手头问题相关的代码部分:
@Stateful
@RequestScoped
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class DomainResources {
@PersistenceContext(type = PersistenceContextType.EXTENDED) @RequestScoped
private EntityManager entityManager;
public boolean templateContainsDomainWithName(String name) { // Extra code included to diagnose the problem
MetadataTemplate template = this.getTemplate();
List<Domain> domains = template.getDomains();
List<Domain> domains2 = domains.stream().collect(Collectors.toList());
List<String> names = domains.stream().map(Domain::getName).collect(Collectors.toList());
boolean exists1 = names.contains(name);
boolean exists2 = this.getTemplate().getDomains().stream().anyMatch(domain -> domain.getName().equals(name));
return this.getTemplate().getDomains().stream().anyMatch(domain -> domain.getName().equals(name));
}
@POST
@RolesAllowed({"root"})
public Response createDomain(@Valid @EmptyID DomainDTO domainDTO, @Context UriInfo uriInfo) {
if (this.getTemplate().getLastVersionState() != State.DRAFT) {
throw new UnmodifiableTemplateException();
} else if (templateContainsDomainWithName(domainDTO.name)) {
throw new DuplicatedKeyException("name", domainDTO.name);
} else {
Domain domain = this.getTemplate().createNewDomain(domainDTO.name);
this.entityManager.flush();
return Response.created(uriInfo.getAbsolutePathBuilder().path(domain.getId()).build()).entity(new DomainDTO(domain)).type(MediaType.APPLICATION_JSON).build();
}
}
}
@Entity
public class MetadataTemplate extends IdentifiedObject {
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "metadataTemplate", orphanRemoval = true) @OrderBy(value = "creationDate")
private List<Version> versions = new LinkedList<>();
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true) @OrderBy(value = "name")
private List<Domain> domains = new LinkedList<>();
public List<Version> getVersions() {
return Collections.unmodifiableList(versions);
}
public List<Domain> getDomains() {
return Collections.unmodifiableList(domains);
}
}
我已经包含了getVersions 和getDomains 方法,因为我有类似的操作在版本上完美运行。我能找到的唯一显着区别是versions 被急切地获取,而domains 被懒惰地获取。但据我所知,代码正在事务中执行,并且正在加载域列表。如果不是,我会得到一个惰性初始化异常,不是吗?
更新:按照@Ferrybig 的建议,我对这个问题进行了更深入的调查,似乎与不正确的延迟加载没有任何关系。如果我以经典方式遍历集合,我仍然无法使用流获得正确的结果:
boolean found = false;
for (Domain domain: this.getTemplate().getDomains()) {
if (domain.getName().equals(name)) {
found = true;
}
}
List<Domain> domains = this.getTemplate().getDomains();
long estimatedSize = domains.spliterator().estimateSize(); // This returns 0!
domains.spliterator().forEachRemaining(domain -> {
// Execution flow never reaches this point!
});
因此,即使集合已加载,您似乎仍然有这种奇怪的行为。这似乎是用于管理惰性集合的代理中缺少或空的拆分器实现。你怎么看?
顺便说一句,这是部署在 Glassfish / EclipseLink 上
【问题讨论】:
-
首先出现的是延迟加载中的一个错误,实现该方法的api可能不支持延迟加载列表上的splititerator或正常迭代,你能检查一下返回吗
splitIterator()和iterator()的结果,看看它们是否有元素? -
你能发布导入吗?
-
domains.spliterator().estimateSize()返回 0,并且不能使用forEachRemaining遍历元素(消费者内部的代码永远不会被执行)。iterator确实可以正常工作,让我可以遍历元素。不知道这是否相关,但 spliterator 返回一个java.util.Vector$VectorSpliterator对象,而iterator返回一个java.util.Collections$UnmodifiableCollection$1(我猜是匿名内部类) -
@hahn 这些类中有很多导入,你在想什么,所以我可以只发布相关的吗?
-
好奇,“收藏家”是否来自第三方库
标签: java jpa jakarta-ee jax-rs jboss-arquillian