Reputation: 47
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
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
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
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