-
1. Re: EntityManager merge and the seam-registration example.
justinwalsh Jul 21, 2006 1:29 AM (in response to hchafi)Persist is used to insert a new instance of an object to the database.
The underlying persistence engine (most probably hibernate) needs to know if your object is is a new object, or if is a detached instance (an instance already present in the database but not associated with the session). The usual mechanism for doing this is by inspecting the @Id field for a null/non-null value. (There are other mechanisms which you can employ using hibernate - but I'm not 100% sure if these are std J2EE 5 ways)
[For more information on this check out the hibernate forums/docs http://forum.hibernate.com]
So - if you try and persist (insert) an object that already has an identifer - you are going to get the exception you mentioned.
Entity beans by default are bound to the conversational context. Have you explicitly bound the user to the session? Show us the code that is creating the user and the user object. -
2. Re: EntityManager merge and the seam-registration example.
hchafi Jul 21, 2006 1:54 AM (in response to hchafi)import static org.jboss.seam.ScopeType.SESSION; import java.io.Serializable; ... import org.jboss.seam.annotations.Scope; @Entity @Name("user") @Scope(SESSION) @Table(name="users", uniqueConstraints={@UniqueConstraint(columnNames={"username"})}) public class User implements Serializable { private static final long serialVersionUID = 9093363133205674398L; private Long id; private String username = null; private String password = null; private Boolean seller = null; private SellerInfo sellerInfo = null; private ContactInfo contactInfo = null; private String firstName; private String lastName; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public ContactInfo getContactInfo() { return contactInfo; } public void setContactInfo(ContactInfo contactInfo) { this.contactInfo = contactInfo; } public User() { super(); } @NotNull @Length(min=5, max=15) public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @NotNull @Length(min=5, max=15) public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public SellerInfo getSellerInfo() { return sellerInfo; } public void setSellerInfo(SellerInfo sellerInfo) { this.sellerInfo = sellerInfo; } @Id @GeneratedValue(strategy=GenerationType.AUTO) public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Boolean getSeller() { return seller; } public void setSeller(Boolean seller) { this.seller = seller; } }
My impression was that @Scope did the binding.
The remaining code is almost identical to initial example code, but for completness sake.<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://jboss.com/products/seam/taglib" prefix="s" %> <html> <head> <title>Register New User</title> </head> <body> <f:view> <h:form> <table border="0"> <s:validateAll> <tr> <td>Username</td> <td><h:inputText value="#{user.username}" required="true"/></td> </tr> <tr> <td>First Name</td> <td><h:inputText value="#{user.firstName}" required="true"/></td> </tr> <tr> <td>Last Name</td> <td><h:inputText value="#{user.lastName}" required="true"/></td> </tr> <tr> <td>Password</td> <td><h:inputSecret value="#{user.password}" required="true"/></td> </tr> </s:validateAll> </table> <h:messages/> <h:commandButton type="submit" value="Register" action="#{register.register}"/> </h:form> </f:view> </body> </html>
Finally the RegisterAction, all I changed is the em.persist to em.mergeimport java.util.List; import javax.ejb.Stateless; ... import org.jboss.seam.log.Log; import com.genietown.model.User; @Stateless @Name("register") public class RegisterAction implements Register { @In @Valid private User user; @PersistenceContext private EntityManager em; @Logger private Log log; public String register() { List existing = em.createQuery("select username from User where username=:username") .setParameter("username", user.getUsername()) .getResultList(); if (existing.size()==0) { em.merge(user); log.info("Registered new user #{user.username}"); return "/registered.jsp"; } else { FacesMessages.instance().add("User #{user.username} already exists"); return null; } } }
Thanks for your help. To restate the question, why wouldn't merge simply update the user info instead of creating a new user? Obviously the later is what I want, but I am not sure why this works the way it is setup currently.
-hc -
3. Re: EntityManager merge and the seam-registration example.
javidjamae Sep 14, 2006 8:00 PM (in response to hchafi)I know this is a late reply, but...
Section 3.7 of the hibernate docs tells you that if the id is null (transient object), then it will persist, but if the id is populated (detached object) then it will merge.
You have a transient object.
Do you have the app populating the register form with an existing employee, or are you just trying to type in the name of an existing user, hoping that it will update the info? I'm guessing you're trying to do the latter and that's why it isn't doing what you want.
You'll probably need to add a search screen that finds all the users, allows you to select one (using DataModel/DataModelSelection) then populates the form for you (by using @Out to outject the user). You'll also have to make sure that the search selection starts a conversation (@Begin) and the update feature ends the conversation (@End) because you are spanning multiple requests. Make sure the user object has conversation scope (the default for entity beans, so don't define the @Scope annotation).