JSF selectCheckboxMenu&Hibernate @ManyToMany删除值

时间:2018-11-20 07:27:58

标签: hibernate jsf primefaces many-to-many jsf-2.3

首先,请详细说明所使用的软件:我正在使用JSF 2.3在Wildfly 14.0.1Final上使用primefaces 6.2.11。

问题描述:我有两个与many2many相关的实体(用户和组),并且我要删除不是所有者(具有mappedBy的实体)这一侧的关系。我不能更改所有者方面,因为这是旧代码。我知道我必须删除两端的关系,但是我的代码无法正常工作。将值添加到列表中没问题,但是无法删除它们。 几天前,我在Wildfly 10上进行了尝试,还可以,但是在新版本上却不行。

一个问题是,素数列表在删除项目时不会通过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 ...
}

我创建了一个表单来编辑用户。该表格有很多输入,一个用于向用户添加和删除组。这是一个简单的素面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;
}

1 个答案:

答案 0 :(得分:0)

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

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

然后,您确实会坚持下去。合并:

baseEm.merge(user);

Persist必须仅用于创建新对象。由于您要更新对象,因此请使用合并。

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

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

JPA将自动生成必要的SQL,以删除或添加多对多表中的组实体。

还请注意,添加和删除操作基于幕后的equals和哈希码。它们必须正确编程,否则,将无反应。

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

顺便说一句,您的selectCheckboxMenu标签和转换器看起来还可以。

祝你好运!