removeif() 不适用于 jpa 返回的列表
removeif() not working on list returned by jpa
我无法理解为什么我不能对 jpa 返回的集合使用 removeIf() 但我可以使用迭代器来这样做。
@PersistenceContext(unitName = "my-pu")
private EntityManager em;
@Override
public void removeUserFromGroup(String username, Group group) {
Query query = em.createNamedQuery("Group.getByName", Group.class);
query.setParameter("name", group.getGroupName());
Group qGroup = (Group) query.getSingleResult();
// this works
// Iterator<User> i = qGroup.getUsers().iterator();
// while (i.hasNext()) {
// User o = i.next();
// if (o.getUsername().equals(username)) {
// System.out.println("eqqq");
// i.remove();
// }
// }
System.out.println("class: " + qGroup.getUsers().getClass().getName());
// org.eclipse.persistence.indirection.IndirectList
qGroup.getUsers().removeIf(u -> u.getUsername().equals(username));// doesn't work
}
出现这种奇怪行为的典型原因是您有 hashCode(和 equals)的自定义实现。如果在这种情况下您更改了对象并导致 hashCode 不同,那么对于 JDK 集合,即使使用 iterator.remove() 也无法从 Set 中删除该对象。
JDK 集合通过重新计算 hashCode 并使用该哈希执行对象的删除来实现删除。如果散列已更改,则删除失败,并且 JDK 实现将忽略它,尽管它们 return 作为删除的结果为真,这意味着集合实际上已经更改,即使它没有更改。悲伤但真实。
完美,另一种方法是抛出一个新的列表来解决问题...例如:
if (!CollectionUtils.isEmpty(getEvento().getAtividade())) {
Set<Atividade> listAtividade = getEvento().getAtividade();
getEvento().setAtividade(new HashSet<>());
getEvento().getAtividade().addAll(listAtividade);
}
getEvento().getAtividade().removeIf(a ->
a.getEspecialidade().getId().equals(especialidade.getId()));
or
getEvento().getAtividade().removeAll(getListAtividadeNotSelected());
最有可能的实际答案通常既不是 Equals / Hash
(请将 Lombok 与 @EqualsAndHashCode
一起使用)也不是创建新列表(只是隐藏了实际问题)。
事实上,很可能是 Arrays.asList
或 Collections.singletonList
的用法创建了一个不可变的 ArrayList
.
假设您在 JPA 中有一个 OneToMany/ManyToMany 关系,所以一个集合 - 假设它是一个有成员的组。组/用户都是正确的 JAP @Entity
var user1 = new User();
var user2 = new User();
var group = new Group();
group.addMembers(Arrays.asList(user1,user2));
group = groupRepository.save(group);
// will throw an exception
group.getMembers().removeIf((user) user.getId() == 1);
原因是,Arrays.asList(user1,user2)
创建了一个实际的 不可变 数组,其大小强制固定。 运行 删除它,将失败 OperationNotSupport
。
因为 Arryas.asList
和 Collections.singletonList
无论如何都很笨重(因为你必须为 1+ 选择前者,为 1 元素选择后者)并且在这两种情况下你都可以使用你可能不知道的不可变数组的,而是使用 Guavas
Lists.newArrayList(user1,user2);
Lists.newArrayList(user1);
这适用于任何大小,并且数组是可变的。
请注意,如果您使用 JPA 工具,这个问题可能会变得更加隐蔽,因为它需要一个可变列表,并且如果它无法操作它,可能会通过您传递更多加密的异常。
我无法理解为什么我不能对 jpa 返回的集合使用 removeIf() 但我可以使用迭代器来这样做。
@PersistenceContext(unitName = "my-pu")
private EntityManager em;
@Override
public void removeUserFromGroup(String username, Group group) {
Query query = em.createNamedQuery("Group.getByName", Group.class);
query.setParameter("name", group.getGroupName());
Group qGroup = (Group) query.getSingleResult();
// this works
// Iterator<User> i = qGroup.getUsers().iterator();
// while (i.hasNext()) {
// User o = i.next();
// if (o.getUsername().equals(username)) {
// System.out.println("eqqq");
// i.remove();
// }
// }
System.out.println("class: " + qGroup.getUsers().getClass().getName());
// org.eclipse.persistence.indirection.IndirectList
qGroup.getUsers().removeIf(u -> u.getUsername().equals(username));// doesn't work
}
出现这种奇怪行为的典型原因是您有 hashCode(和 equals)的自定义实现。如果在这种情况下您更改了对象并导致 hashCode 不同,那么对于 JDK 集合,即使使用 iterator.remove() 也无法从 Set 中删除该对象。 JDK 集合通过重新计算 hashCode 并使用该哈希执行对象的删除来实现删除。如果散列已更改,则删除失败,并且 JDK 实现将忽略它,尽管它们 return 作为删除的结果为真,这意味着集合实际上已经更改,即使它没有更改。悲伤但真实。
完美,另一种方法是抛出一个新的列表来解决问题...例如:
if (!CollectionUtils.isEmpty(getEvento().getAtividade())) {
Set<Atividade> listAtividade = getEvento().getAtividade();
getEvento().setAtividade(new HashSet<>());
getEvento().getAtividade().addAll(listAtividade);
}
getEvento().getAtividade().removeIf(a ->
a.getEspecialidade().getId().equals(especialidade.getId()));
or
getEvento().getAtividade().removeAll(getListAtividadeNotSelected());
最有可能的实际答案通常既不是 Equals / Hash
(请将 Lombok 与 @EqualsAndHashCode
一起使用)也不是创建新列表(只是隐藏了实际问题)。
事实上,很可能是 Arrays.asList
或 Collections.singletonList
的用法创建了一个不可变的 ArrayList
.
假设您在 JPA 中有一个 OneToMany/ManyToMany 关系,所以一个集合 - 假设它是一个有成员的组。组/用户都是正确的 JAP @Entity
var user1 = new User();
var user2 = new User();
var group = new Group();
group.addMembers(Arrays.asList(user1,user2));
group = groupRepository.save(group);
// will throw an exception
group.getMembers().removeIf((user) user.getId() == 1);
原因是,Arrays.asList(user1,user2)
创建了一个实际的 不可变 数组,其大小强制固定。 运行 删除它,将失败 OperationNotSupport
。
因为 Arryas.asList
和 Collections.singletonList
无论如何都很笨重(因为你必须为 1+ 选择前者,为 1 元素选择后者)并且在这两种情况下你都可以使用你可能不知道的不可变数组的,而是使用 Guavas
Lists.newArrayList(user1,user2);
Lists.newArrayList(user1);
这适用于任何大小,并且数组是可变的。
请注意,如果您使用 JPA 工具,这个问题可能会变得更加隐蔽,因为它需要一个可变列表,并且如果它无法操作它,可能会通过您传递更多加密的异常。