2
I am making a simple CRUD, using JSF, JPA and CDI.
I have two screens:
- A screen with a datatable listing the data, in each row has the edit and delete button, at the beginning of the screen has an insert button, the boões edit and insert direct to the second screen.
- The second screen is the form, with the inputs and save button
I have a bean ViewScoped
to take care of both screens and step the parameters from one screen to the other with the f:setPropertyActionListener
, everything works according to both insert and edit.
Now when the bean validating some business rule prevents the user from editing (in this case returning null
to the action of the button), the screen returns for the user to correct the data and submit again, but when this happens mine EntityManager
has already closed, as it is configured to live in the scope of request
(what for bank control is great).
Right now the mistake is org.hibernate.PersistentObjectException: detached entity passed to persist
.
If the bean
does not accuse error editing works, since Entitymanager is already open, because it was used to load the object that is displayed on the screen.
Changing the scope of Entitymanager would create the problem of having to manage it when it closes and I don’t want that.
I wonder what I can do to get around this problem and how you solve it.
Producer of the Entitymanager
@Produces
@RequestScoped
public EntityManager createEntityManager() {
return factory.createEntityManager();
}
public void closeEntityManager(@Disposes EntityManager manager) {
manager.close();
}
Bean that takes care of the list screen and the 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 from item in 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>
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;
}
}