2

首先是所用软件的一些细节:我在 Wildfly 14.0.1Final 和 JSF 2.3 上使用 primefaces 6.2.11。

问题描述:我有两个与many2many 相关的实体(用户和组),我想删除不是所有者的关系(具有mappedBy 的实体)。我不能更改所有者,因为这是遗留代码。我知道我必须删除两端的关系,但我的代码不起作用。将值添加到列表中没有问题,但无法删除它们。几天前我用 Wildfly 10 试过这个,没问题,但新的不行。

一个问题是,primefaces 列表在删除项目时不会通过 ajax 事件通知我。如果是这种情况,我可以处理从我的组列表和组中的用户列表中删除。因为我没有这样的事件,所以我试图在我的服务中处理这个试图拯救用户的事件。

我有两个实体:

@Entity
@Table(name = "USER")
public class User implements Serializable {
    private static final long serialVersionUID = -1699265057144017750L;

    @Column(name = "ID", nullable = false)
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(name = "USER_NAME", length = 45, unique = true)
    @Basic
    @NotNull
    private String userName;

    @ManyToMany(fetch = FetchType.LAZY, targetEntity = Group.class, mappedBy = "userList")
    @OrderBy("groupName")
    private Set<Group> groupList;

    // getter setter ...
}

@Entity
@Table(name = "GROUP")
public class User implements Serializable {
    private static final long serialVersionUID = -3403518747362744630L;

    @Column(name = "ID", nullable = false)
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @ManyToMany(fetch = FetchType.LAZY, targetEntity = User.class, cascade = {CascadeType.MERGE, CascadeType.PERSIST})
    @JoinTable(name = "USER_GROUP", 
        joinColumns = {@JoinColumn(name = "GROUP_ID", referencedColumnName = "ID")}, 
        inverseJoinColumns = {@JoinColumn(name = "USER_ID", referencedColumnName = "ID")})
    @OrderBy("userName")
    private Set<User> userList;

    // getter setter ...
}

我创建了一个表单来编辑用户。此表单有很多输入,其中一个用于向用户添加和删除组。这是一个简单的primefaces selectCheckboxMenu。这允许用户选择新组并删除旧组。值直接从用户实体中检索,并且可以选择的值由单独的服务提供。

<p:selectCheckboxMenu id="groupList" multiple="true" filter="true" filterMatchMode="contains"
                      value="#{editUserBean.authUser.groupList}"
                      required="true" requiredMessage="At least one group is needed"
                      converter="cs.argos.gui.jsf.converter.groupConverter" collectionType="java.util.LinkedHashSet">
    <f:selectItems value="#{groupService.groups}" var="group" itemValue="#{group}" itemLabel="#{group.authGroupName}"/>
</p:selectCheckboxMenu>

转换器:

@FacesConverter(value = "groupConverter", managed = true)
    public class GroupConverter implements Converter {

    @Inject
    GroupService groupService;

    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String value) {

        if(null == value || value.isEmpty()) {
            return null;
        }

        try {
            return groupService.getGroupWithCollections(Integer.parseInt(value));
        } catch (NumberFormatException e) {
            throw new ConverterException(new FacesMessage("Invalid Group ID"), e);
        }
    }

    @Override
    public String getAsString(FacesContext context, UIComponent component, Object value) {

        Group group = (Group) value;

        if (null == group) {
            return "";
        }

        if(null != group.getId()) { 
            return ""+group.getId();
        } else {
            throw new ConverterException(new FacesMessage("Invalid Group ID"));
        }
    }
}

当使用按下按钮时,我正在调用以下服务:

@Transactional(Transactional.TxType.REQUIRED)
public User persistWithDeps(User user2Save) {

    User user = user = baseEm.contains(user2Save) ? user2Save : baseEm.merge(user2Save);

    // Check if we have a new object (case when: baseEm.merge(user2Save))
    if (user != user2Save) {
        if (null != user.getGroupList()) {

            // remove groups
            if (!user2Save.getGroupList().containsAll(user.getGroupList())) {

                // remove groups from user
                Iterator<Group> iterator = user.getGroupList().iterator();
                while (iterator.hasNext()) {
                    Group group = iterator.next();

                    if (!user2Save.getGroupList().contains(group)) {
                        group.getuser2SaveList().remove(user);
                        iterator.remove();
                    }
                }
            }

            // Add groups to user
            user.getGroupList().forEach(group -> {
                if (!group.getUserList().contains(user)) {
                    group.getUserList().add(user);
                }
            });
        }
    }

    baseEm.persist(user);
    return user;
}
4

1 回答 1

0

它与 JPA 相关。您将级联放在错误的实体上:

@ManyToMany(fetch = FetchType.LAZY, targetEntity = Group.class, mappedBy = "userList", , cascade = {CascadeType.MERGE, CascadeType.PERSIST})

然后,您确实坚持 iso 合并:

baseEm.merge(user);

Persist 只能用于创建新对象。由于您正在更新对象,因此请使用合并。

此外,persistWithDeps 方法的编写方式非常奇怪。当 Primefaces 正确更新 User 对象中的 groupList 时,这就足够了:

public User persistWithDeps(User user2Save) {
    return baseEm.merge(user2save);
}

JPA 将自动生成在多对多表中删除或添加组实体所需的 SQL。

另请注意,添加和删除操作是基于幕后的 equals 和 hashcode。它们必须被正确编程,否则可能会导致什么也没有发生。

因此,尝试用我的方法替换该方法。如果它仍然不起作用,请设置断点return baseEm.merge(user2save);并检查 user.getGroupList()。它必须具有您期望的正确数量的组。如果是这样,并且合并仍然不起作用,则需要更改 equals 和 hashcode 方法。如果在断点上您没有预料到的组数不正确,这意味着您的 JSF 链接 bean (editUserBean) 和对persistWithDeps 的调用之间出现了问题。

顺便说一句,您的 selectCheckboxMenu 标签和转换器看起来不错。

祝你好运!

于 2018-11-29T08:23:07.453 回答