I'm doing a simple CRUD using JSF, JPA and CDI.
I have two screens:
- A screen with a datatable listing the data, on each line has the edit and delete button, at the beginning of the screen has an insert button, the edit and insert buttons direct to the second screen.
- The second screen is the form, with the inputs and the save button
I have a bean ViewScoped
to take care of both screens and pass the parameters from one screen to the other with f:setPropertyActionListener
, everything works accordingly, both insert and edit.
null
to the button action), the screen returns to the user to correct the data and submit again, but when this happens my EntityManager
already closed because it is set to live in the scope of request
(which for bank control is great).
At this point the error is org.hibernate.PersistentObjectException: detached entity passed to persist
.
If bean
does not acknowledge error, the edit works, since the EntityManager is already open because it was used to load the object that appears on the screen.
Changing the scope of the EntityManager would create the problem of having to manage it when closing and I do not want it.
I would like to know what I can do to get around this problem and how you resolve it.
EntityManager Producer
@Produces
@RequestScoped
public EntityManager createEntityManager() {
return factory.createEntityManager();
}
public void closeEntityManager(@Disposes EntityManager manager) {
manager.close();
}
Bean caring for the listing screen and form.
@Named
@ViewScoped
public class PaisMB implements Serializable {
private static final long serialVersionUID = 1L;
@Inject
private PaisFA paisFa;
private LazyDataModel<Pais> lazyModel = null;
private Pais paisSelecionado = null;
public LazyDataModel<Pais> getLazyModel() {
if (lazyModel == null) {
lazyModel = paisFa.listaPais();
}
return lazyModel;
}
public Pais getPaisSelecionado() {
if (paisSelecionado == null) {
paisSelecionado = new Pais();
}
return paisSelecionado;
}
public void setPaisSelecionado(Pais paisSelecionado) {
this.paisSelecionado = paisSelecionado;
}
public void deletePaisSelecionado() {
try {
paisFa.deletePais(getPaisSelecionado());
JSFUtil.sendInfoMessageToUser("O país \"" + getPaisSelecionado().getNome() + "\" foi deletado com sucesso.");
} catch (Exception ex) {
(...)
}
}
public String salvarPaisSelecionado() {
try {
paisFa.savePais(getPaisSelecionado());
JSFUtil.sendInfoMessageToUser("O país \"" + getPaisSelecionado().getNome() + "\" foi salvo com sucesso.");
return "/paisLista?faces-redirect=true";
} catch (Exception ex) {
(...)
}
return null;
}
}
Edit item in the data listing.
<p:menuitem value="Editar" icon="fa fa-edit" action="paisForm?faces-redirect=true&includeViewParams=true">
<f:setPropertyActionListener target="#{paisMB.paisSelecionado}" value="#{paisMB.paisSelecionado}" />
</p:menuitem>
Receiving the parameter in the form.
<f:metadata>
<f:viewParam name="pais" value="#{paisMB.paisSelecionado}" converter="#{dbEntityCO}" />
</f:metadata>
To convert
@Named("dbEntityCO")
@ApplicationScoped
public class DBEntityCO implements Converter {
@Inject
private EntityManager entityManager;
@Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
// aqui usa o EntityManager
(...)
return objeto;
}
@Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
(...)
return string;
}
}