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.