Weird behavior editing Entity with @ManyToMany and Ajax4JSF
fernando_jmt Apr 26, 2007 9:34 AMHi Folks.
After several days trying to found a reason to the problem I will describe here I gave up.
Maybe someone here had a similar problem or can see WTF I'm doing wrong.
I have a seam application and I'm using Ajax4JSF to perform some validations using a4j:support. I took as base the booking example of seam.
The problem is when I try to edit a simple entity in a conversation, the entity is a User entity (username, firstname, lastname) and this entity has a @ManyToMany relationship with a Role entity, this relationship is displayed as checkboxes in the page and here I'm using s:convertEntity to make this work. All works fine without a4j:support in the input fields. But, when I add the a4j:support to some field, then the entity is persited in the database every time a a4j request is done (a4j:support onblur). This means every time I press the tab key in the form page I can see something like this in the server console:
Hibernate: update users set password=?, firstname=?, lastname=?, email=?, version=?, country=?, city=?, province=?, street= ?, enabled=? where username=? and version=? Hibernate: update users set password=?, firstname=?, lastname=?, email=?, version=?, country=?, city=?, province=?, street= ?, enabled=? where username=? and version=? Hibernate: update users set password=?, firstname=?, lastname=?, email=?, version=?, country=?, city=?, province=?, street= ?, enabled=? where username=? and version=? Hibernate: update users set password=?, firstname=?, lastname=?, email=?, version=?, country=?, city=?, province=?, street= ?, enabled=? where username=? and version=? Hibernate: update users set password=?, firstname=?, lastname=?, email=?, version=?, country=?, city=?, province=?, street= ?, enabled=? where username=? and version=? Hibernate: update users set password=?, firstname=?, lastname=?, email=?, version=?, country=?, city=?, province=?, street=
But, I did not press any button or submit the form, and even the action (seam component) is never called.
Here is my code:
User entity:
@Entity @Table(name = "users") @Name("user") public class User implements Serializable { private static final long serialVersionUID = 1559233654671826129L; @Id @Column(name = "username") @Length(max = 50) @NotNull private String username; @Column(name = "password", nullable = false) @Length(min = 6, max = 200) @NotNull private String password; @Column(name = "firstname", nullable = false) @NotNull @Length(max = 80) private String firstName; @Column(name = "lastname", nullable = false) @NotNull @Length(max = 80) private String lastName; @Version @Column(name = "version") private long version; ... @ManyToMany(fetch = FetchType.LAZY) @JoinTable( name = "userrole", joinColumns = @JoinColumn(name = "username"), inverseJoinColumns = @JoinColumn(name = "name") ) @OrderBy("name asc") private List<Role> roles; ....
Edit xhtml page:
<ui:define name="body"> <h:form id="userForm"> <h:commandButton action="#{userAction.update}" value="#{messages['Common.save']}" styleClass="button" /> <a4j:commandButton action="#{userAction.cancel}" value="#{messages['Common.cancel']}" styleClass="button" immediate="true"/> </div> <rich:panel id="formPanel"> <f:facet name="header">#{messages['User.edit']}</f:facet> <s:decorate id="usernameDecorate" template="/include/viewField.xhtml"> <ui:define name="label">#{messages['User.username']}</ui:define> #{user.username} </s:decorate> <s:decorate id="firstNameDecorate" template="/include/inputField.xhtml"> <ui:define name="label">#{messages['User.firstName']}</ui:define> <h:inputText id="firstName" value="#{user.firstName}" required="true" maxlength="80" tabindex="4" styleClass="input"> <a4j:support event="onblur" reRender="firstNameDecorate"/> </h:inputText> </s:decorate> <s:decorate id="lastNameDecorate" template="/include/inputField.xhtml"> <ui:define name="label">#{messages['User.lastName']}</ui:define> <h:inputText id="lastName" value="#{user.lastName}" required="true" maxlength="80" tabindex="5" styleClass="input"> <a4j:support event="onblur" reRender="lastNameDecorate"/> </h:inputText> </s:decorate> //If I remove below field, all works fine <s:decorate template="/include/inputField.xhtml"> <ui:define name="label">#{messages['User.assignRoles']}</ui:define> <h:selectManyCheckbox value="#{user.roles}" id="userRoles" tabindex="13" styleClass="checkBox"> <s:selectItems value="#{roleList}" var="role" label="#{messages[role.description]}"/> <s:convertEntity/> </h:selectManyCheckbox> </s:decorate> </rich:panel> </h:form>
User action component
@Stateful @Name("userAction") @Scope(ScopeType.CONVERSATION) @Restrict("#{s:hasRole('admin')}") public class UserAction implements Serializable, IUserAction { private static final long serialVersionUID = -8072889837049283608L; @In private EntityManager entityManager; @In(required = false) @Out(required = false) private User user; @In private FacesMessages facesMessages; @Begin(ifOutcome = "userEdit") public String select(User selectedUser) { log.debug("selecting user"); try { user = entityManager.find(User.class, selectedUser.getUsername()); user.setConfirmPassword(user.getPassword()); setOriginalPassword(user.getPassword()); return "userEdit"; } catch (EntityNotFoundException e) { facesMessages.addFromResourceBundle(FacesMessage.SEVERITY_INFO, "Common.error.notFound", selectedUser.getFullName()); return "userList"; } } @End public String update() { try { entityManager.merge(user); } catch (OptimisticLockException e) { user = currentUser; facesMessages.addFromResourceBundle(FacesMessage.SEVERITY_ERROR, "Common.error.concurrency"); return null; } facesMessages.addFromResourceBundle(FacesMessage.SEVERITY_INFO, "Common.message.updated", user.getFullName()); return "userList"; } @End public String cancel() { return "userList"; }
Maybe you are thinking that this post should be done in Ajax4JSF forum, but I'm not sure about this, because it seems like a em.merge() is done, but I don't know by whom. Anyway I hope you can give some hints in order to solve this problem.
Thanks in advance.