First of all some details von the used software: I'm working with primefaces 6.2.11 on Wildfly 14.0.1Final with JSF 2.3.
Problemdescription: I have two entities (User & Group) related many2many and I want to remove a relation on the side which is not the owner (the entity with the mappedBy). I cant change the owner sides because this is legacy code. I know that I have to remove the relation on both ends but my code does not work. Adding values to the list is no problem but removing them is not possible. I tried this several days ago with Wildfly 10 and it was okay but not with the new one.
One problem is that the primefaces list does no notify me via ajax event when an item will be removed. If this would be the case I could handle the remove from my group list and user list in the group. Because I dont have an event like that I tried to handle this in my service which tries to save the user.
I have two entities:
@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 ...
}
I created a form to edit users. This form has a lot of inputs and one is for adding and removing groups from the user. It is a simpel primefaces selectCheckboxMenu. This allowes the user to choose new groups and remove old groups. The value(s) is/are directly retrieved from the user entity and the values which can be selected are served by an seperate service.
<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>
Converter:
@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"));
}
}
}
When the used hits a button I'm calling the following service:
@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;
}