JSF selectCheckboxMenu & Hibernate @ManyToMany удалить значения - PullRequest
0 голосов
/ 20 ноября 2018

Прежде всего, некоторые сведения о используемом программном обеспечении: я работаю с основными лицами 6.2.11 в Wildfly 14.0.1, наконец, с JSF 2.3.

Описание проблемы: у меня есть две сущности (User & Group), связанные с 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 голосов
/ 29 ноября 2018

Это связано с JPA. Вы поместили каскад не на ту сущность:

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

Тогда вы сохраняете i.s.o. слияния:

baseEm.merge(user);

Persist должен использоваться только для создания новых объектов. Поскольку вы обновляете объект, используйте слияние.

Кроме того, метод persistWithDeps написан очень причудливо. Когда Primefaces правильно обновляет groupList в объекте User, этого достаточно:

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

JPA автоматически сгенерирует необходимый SQL для удаления или добавления групповой сущности в таблице «многие ко многим».

Также следует помнить, что операции добавления и удаления основаны на равенстве и хэш-коде за кулисами. Они должны быть правильно запрограммированы, и в противном случае это может привести к тому, что ничего не произойдет.

Итак, попробуйте заменить метод на мой. Если это все еще не работает, установите точку останова на return baseEm.merge(user2save); и проверьте user.getGroupList (). Он должен иметь правильное количество групп, которые вы ожидаете. Если это так, и объединение все еще не работает, вам нужно изменить методы equals и hashcode. Если на точке останова у вас неправильное количество групп, которых вы не ожидали, это означает, что что-то пошло не так между вашим связанным JSF-компонентом (editUserBean) и вызовом persistWithDeps.

Кстати, ваш тег и конвертер selectCheckboxMenu выглядят нормально.

Удачи!

...