Arthur Vinicius
Arthur Vinicius

Reputation: 47

JPA persisting instead of updating

I came across a strange problem, whenever I try to edit a register in my application instead of update the state of the entity, it persists a new one. Here goes my update method

public void update(Object obj) {
    EntityManagerFactory fac = Persistence.createEntityManagerFactory("crud");
    EntityManager em = fac.createEntityManager();
    em.merge(obj);
    em.getTransaction().begin();
    em.getTransaction().commit();
    em.close();
    fac.close();
}

My guess is when I submit the edit form and the setters are called the Id is somehow set to 0. Is that possible to happen ? The form looks like this :

<h:form>
                <p:panelGrid columns="2" style="margin: 0 auto;">
                    <f:facet name="header">
                        Edit form
                    </f:facet>
                    Name:
                    <h:inputText id="nome" value="#{editUserBean.userToEdit.name}"/>
                    Age:
                    <h:inputText value="#{editUserBean.userToEdit.age}" converter="javax.faces.Integer"/>
                    Pass:
                    <h:inputText value="#{editUserBean.userToEdit.password}"/>
                    Gender:
                    <h:selectOneRadio value="#{editUserBean.userToEdit.gender}" style="font-size: 12px;">
                        <f:selectItem itemValue="Masc" itemLabel="Masc" />
                        <f:selectItem itemValue="Fem" itemLabel="Fem" />
                    </h:selectOneRadio>
                    <f:facet name="footer">
                        <div align="center">
                            <h:commandButton value="save" actionListener="#{editUserBean.save}" icon="ui-icon-check"/>
                        </div>
                    </f:facet>
                </p:panelGrid>
</h:form>

Hibernate is querying this :

Hibernate: 
    select
        hibernate_sequence.nextval 
    from
        dual
Hibernate: 
    insert 
    into
        users
        (age, gender, name, password, id) 
    values
        (?, ?, ?, ?, ?)

Upvotes: 2

Views: 885

Answers (3)

Anthony Accioly
Anthony Accioly

Reputation: 22481

The actual problem is in the JSF layer.

All evidence points that editUserBean is request scoped. Which means that you will get a new bean per request.

So, we are working with a new userToEdit bean on every request. Its properties will be either null or a default value for primitives (e.g., 0 for int and long), unless you explicitly declared a default value for the property inside the bean. This includes the id, which in your case is 0.

When you merge your bean, there is no entity with id 0 in the persistence context or a respective entry in the database, so merge will persist a new entity. If you are generating values for the primary key, odds are that you will never have 0 as an id, so the JPA layer will keep persisting new entities.

One simple fix for this problem consists in storing the user information in a bean with a broader scope, say session scoped or user defined conversation scoped:

@ConversationScoped
class UserToEdit extends User {
   // ... or if you want, create a HAVE-A relationship
}

Then you can inject your bean as usual and the stored id will survive between requests (don't forget to clean up; correctly demarcate conversation boundaries and/or clean the session bean).


Another workaround - if you absolutely must keep the user info in the request scope - is using a hidden field to hold the value of the id (as well as any non-editable property that you wish). This way the user id will also be submitted on the next request and the model will be correctly updated.

<h:inputHidden value="#{editUserBean.userToEdit.id}" />

Warning: While this workaround is pretty popular, only do it if the current user is able to update information of any other user. Why? A malicious user can easily manipulate the DOM in order to change the user.id and / or submit a fake request containing any id to abuse the system.

Upvotes: 1

Jonathan S. Fisher
Jonathan S. Fisher

Reputation: 8886

Couple of things... First inject your PersistenceContext like this into your EJB:

@PersistenceContext
private EntityManager em;

Next, mark your function, or the entire EJB with the proper automatic transaction handling:

@Stateless
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public class MyServiceEJBThingy {
    @PersistenceContext
    private EntityManager em;

In your method, don't do anything but your business logic. Transaction sematics are now handled for you automagically:

@Stateless
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public class MyServiceEJBThingy {
    @PersistenceContext
    private EntityManager em;

    public void update(Object obj) {
        //GUI independent business logic here
        em.merge(obj);
    }
}

Finally, now inject your EJB in your view controller (assuming you're using the MVVM pattern):

@RequestScoped (or something)
public class EditUserBean {
   @Inject
   private MyServiceEJBThingy myServiceEJBThingy;


   public void update(Object obj) {
       // GUI logic here
       myServiceEJBThingy.update(obj);
   }
}

Much simpler yes? :) Java can be very simple and productive. I'm not sure what container you're using, but I'd suggest using Apache TomEE. Check out their huge library of up-to-date examples here. Good luck!!!

Upvotes: 1

Pratik.S
Pratik.S

Reputation: 470

I don't know if you have already tried this but check the hibernate configuration file...and then may be one of the tag contains the value create. Change it to update. Hope this helps

Upvotes: 1

Related Questions