I'm currently trying to learn JSF and JPA. I know that the patterns I use are not recommended at all, but I want to understand what's going on because I think it'll help me in the future. I've just thrown together a prototype from various sources.
The problem that I encounter with the setup described below is that apparently the JPA entities are getting detached all the time, which in turn happens because the backing bean gets serialized over and over. In fact, if I remove the Serializable interface from the entity class, I get Exiting serializeView - Could not serialize state: com.sk.Message
Since the entities are detached, nothing gets committed to the database when I call EntityManager.commit(). If I manually merge all the entities (the commented out line in onCellEdit() below) with EntityManager.merge(), the modified entities are committed to the database.
I've already found from other SO posts that I could deal with this problem by adding
<context-param>
<param-name>org.apache.myfaces.SERIALIZE_STATE_IN_SESSION</param-name>
<param-value>false</param-value>
</context-param>
to my persistence.xml. But it was also pointed out somewhere that this would only be a workaround and not a solution.
So my questions are:
Is it intended/expected that a #ViewScoped JSF backing bean gets serialized over and over again (while staying on the same view all the time), which makes it difficult to use JPA entities in it?
Is it safe/reasonable to use the SERIALIZE_STATE_IN_SESSION parameter?
As I found recommended many times, should I just forget about JSF managed beans altogether and go with CDI directly (e.g. #ConversationScope to achieve something similar)?
I'm using TomEE (MyFaces, OpenJPA) with PrimeFaces. The backing bean contains the following code:
#ViewScoped
#ManagedBean
public class MessageBean implements Serializable
{
private List<Message> messages;
public List<Message> getMessages()
{
return messages;
}
public void setMessages( List<Message> messages )
{
this.messages = messages;
}
#PostConstruct
public void init()
{
messages = PersistenceManager.getInstance().queryMessages();
}
public void onCellEdit( CellEditEvent event )
{
// PersistenceManager.getInstance().mergeMessages( messages );
PersistenceManager.getInstance().commitTransaction();
}
[...]
A Message is a JPA Entity, like this:
#Entity
#Table( name = "message" )
#NamedQuery( name = "Message.findAll", query = "SELECT a FROM Message a" )
public class Message implements Serializable
{
private static final long serialVersionUID = 1L;
#Id
#Column( unique = true, nullable = false )
private Integer dbid;
#Column( nullable = false, length = 14 )
private String no;
[...]
}
The backing bean is referenced from a JSF page using a PrimeFaces DataTable:
<h:form id="navForm">
<p:dataTable
id="messages"
value="#{messageBean.messages}"
var="message"
editable="true"
editMode="cell">
<f:facet name="header">MESSAGE</f:facet>
<p:ajax
event="cellEdit"
listener="#{messageBean.onCellEdit}"
update=":navForm:messages" />
<p:column>
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{message.no}" />
</f:facet>
<f:facet name="input">
<p:inputText
id="modelInput"
value="#{message.no}" />
</f:facet>
</p:cellEditor>
<f:facet name="header">Message number</f:facet>
</p:column>
[...]
I know I'm probably violating dozens of best practices here, but for prototyping I've created a singleton POJO, PersistenceManager, which deals with the JPA interface (and potentially other data sources). I use an application-managed, resource-local EntityManager. An excerpt looks like this:
public class PersistenceManager
{
private static PersistenceManager INSTANCE;
private EntityManagerFactory emf;
private EntityManager em;
private EntityTransaction entr;
private PersistenceManager( PersistenceType persistenceType )
{
emf = Persistence.createEntityManagerFactory( "MessagePU" );
em = emf.createEntityManager();
}
public List<Message> queryMessages()
{
TypedQuery<Message> query = em.createNamedQuery( "Message.findAll", Message.class );
return query.getResultList();
}
public void commitTransaction()
{
if ( entr != null && entr.isActive() )
{
entr.commit();
}
}
[...]
Before committing a transaction you have to start it (then close it at the end of the transaction). Where is the else statement in your commitTransaction method, in case the EntityTransaction object is not active and/or null ?
Plus, I don't see any EJB in your code. The POJO approach is not the best option in an application managed, served, and hosted by a container.
For me, the best approach to implement the persistence layer in JSF and JavaEE applications in general, is the Session Façade Pattern, you can search the web about it, there are plenty of references.
In your case, something like this would do.
A Message Facade, that manages transactions related to the Message entity.
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
#Stateless
public class MessageFacade extends AbstractFacade<Message> {
#PersistenceContext(unitName = "MessagePU")
private EntityManager em;
#Override
protected EntityManager getEntityManager() {
return em;
}
public MessageFacade() {
super(Message.class);
}
public List<Message> queryMessages()
{
TypedQuery<Message> query = em.createNamedQuery( "Message.findAll", Message.class );
return query.getResultList();
}
}
An abstract facade class implementing generic persistence functions on generic entities.
public abstract class AbstractFacade<T> {
private Class<T> entityClass;
public AbstractFacade(Class<T> entityClass) {
this.entityClass = entityClass;
}
protected abstract EntityManager getEntityManager();
public void create(T entity) {
getEntityManager().persist(entity);
}
public T edit(T entity) {
return getEntityManager().merge(entity);
}
public void remove(T entity) {
getEntityManager().remove(getEntityManager().merge(entity));
}
public T find(Object id) {
return getEntityManager().find(entityClass, id);
}
public List<T> findAll() {
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
cq.select(cq.from(entityClass));
return getEntityManager().createQuery(cq).getResultList();
}
Your managed bean would then be something like :
#ViewScoped
#ManagedBean
public class MessageBean implements Serializable
{
#EJB
private MessageFacade messageFacade;
private List<Message> messages;
public List<Message> getMessages()
{
return messages;
}
public void setMessages( List<Message> messages )
{
this.messages = messages;
}
#PostConstruct
public void init()
{
messages = messageFacade.findAll();
}
public void onCellEdit( CellEditEvent event )
{
messageFacade.edit(messages);
}
}
Related
i trie to run this code
#ManagedBean
#ApplicationScoped
public class Controller implements Serializable {
private static final long serialVersionUID = 1L;
private Benutzer benutzer;
private List<Erfasst> bisherErfasst = new ArrayList<Erfasst>();
private EntityManagerFactory emf = Persistence
.createEntityManagerFactory("CP Kontrolle");
private static Controller instance = new Controller();
public Benutzer getBenutzer() {
return benutzer;
}
public boolean anmelden(String email, int kdnr) {
EntityManager em = emf.createEntityManager();
Query query = em
.createQuery("SELECT b FROM Benutzer b WHERE b.email = :email AND b.kdnr = :kdnr");
query.setParameter("email", email);
query.setParameter("kdnr", kdnr);
List<Benutzer> liste = query.getResultList();
em.close();
if (liste.size() == 1) {
benutzer = liste.get(0);
AngemeldeteBenutzer.getAb().hinzufuegen(benutzer);
return true;
} else {
return false;
}
}
public static Controller getInstance() {
return instance;
}
[....]
}
}
The above code is my ControllerBean. From the Login-Form, user data will be checked in the "anmelden" Class and return true or false if it was successfully.If successfully, the user will be store into a list, as you can see.
#ManagedBean
#ApplicationScoped
public class AngemeldeteBenutzer implements Serializable {
private static final long serialVersionUID = 1L;
private List<Benutzer> online = new LinkedList<Benutzer>();
private static AngemeldeteBenutzer ab = new AngemeldeteBenutzer();
public static AngemeldeteBenutzer getAb() {
return ab;
}
public List<Benutzer> getOnline() {
return online;
}
public void hinzufuegen(Benutzer benutzer) {
online.add(benutzer);
}
}
This is my other Bean, which store the successfully logged user into a list.
Now i want to list all user into my table, but my table is still empty. No errors!
<h:panelGrid columns="2" id="onlinePanel" >
<h:dataTable value="#{angemeldeteBenutzer.online}" var="on">
<h:column>
<f:facet name="header">Email</f:facet>
<h:outputText value="#{on.email}"></h:outputText>
</h:column>
</h:dataTable>
</h:panelGrid>
The mistake is here:
private static Controller instance = new Controller();
public static Controller getInstance() {
return instance;
}
private static AngemeldeteBenutzer ab = new AngemeldeteBenutzer();
public static AngemeldeteBenutzer getAb() {
return ab;
}
You seem to have missed the point of a bean management framework with dependency injection support. You seem to be expecting that #{angemeldeteBenutzer} in the JSF page is referring exactly the same instance as you manually created there with new operator and are filling with users.
This is Wrong! You have there two instances of the class, one automatically created by JSF and available via #{angemeldeteBenutzer} and another one manually created by yourself and available via that getAb() method only.
Get rid of all those static fields and methods. They don't belong there. Instead, use #ManagedProperty to let JSF inject managed beans in each other. Add this code to the Controller class.
#ManagedProperty("#{angemeldeteBenutzer}")
private AngemeldeteBenutzer ab;
public AngemeldeteBenutzer getAb() {
return ab;
}
public void setAb(AngemeldeteBenutzer ab) {
this.ab = ab;
}
And replace in the same Controller class this line
AngemeldeteBenutzer.getAb().hinzufuegen(benutzer);
by
ab.hinzufuegen(benutzer);
Note: if you're already on Java EE 7, consider using CDI #Named instead of JSF #ManagedBean. When injecting via #Inject instead of #ManagedProperty, you don't need those ugly getter/setter anymore.
#Named
#ApplicationScoped
public class AngemeldeteBenutzer {
}
#Named
#ApplicationScoped
public class Controller {
#Inject
private AngemeldeteBenutzer ab;
}
Unrelated to the concrete problem, the Controller doesn't seem to be a legit application scoped bean. It looks too much like a view scoped bean due that view-specific variables and business logic. Make sure you understand the scopes: How to choose the right bean scope?
Here is my code
Pojo
public class Deal implements Serializable {
private int id;
private String name;
private String description;
private Customer customer;
//getter setter omitted
}
public class Customer implements Serializable {
private int id;
private String name;
private String email;
private String phone;
//getter setter and equal hashcode omitted
}
Managed Bean
#ManagedBean(name="dealBean")
#ViewScoped
public class DealBean implements Serializable {
private List<Customer> customerList;
private List<Deal> dealList;
private Deal deal;
#PostConstruct
public void init() {
deal = new Deal();
dealList = new ArrayList<Deal>();
customerList = new ArrayList<Customer>();
customerList.add(new Customer(1, "MPRL", "mprl#mail.com", "1234455"));
customerList.add(new Customer(2, "Total", "total#mail.com", "3434323"));
customerList.add(new Customer(3, "Petronas", "petronas#mail.com", "8989876"));
}
//getter setter omitted
}
Customer Converter
#FacesConverter("customerConverter")
public class CustomerConverter implements Converter {
#Override
public Object getAsObject(FacesContext arg0, UIComponent arg1, String customerID) {
DealBean dealBean = (DealBean) FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("dealBean");
if (dealBean != null) {
List<Customer> customerList = dealBean.getCustomerList();
for (Customer customer : customerList) {
if (customerID.equals(String.valueOf(customer.getId()))) {
return customer;
}
}
}
return null;
}
#Override
public String getAsString(FacesContext arg0, UIComponent arg1, Object obj) {
if (obj != null) {
return String.valueOf(((Customer)obj).getId());
}
return null;
}
}
XHTML
Customer : <h:selectOneMenu id="customer" value="#{dealBean.deal.customer}">
<f:converter converterId="customerConverter" />
<f:selectItems value="#{dealBean.customerList}" var="cus"
itemLabel="#{cus.name}" itemValue="#{cus}" />
</h:selectOneMenu>
When the managed bean is in request or session scope, the Customer pojo is set correctly to Deal pojo. The problem is when the managed bean is in View scope, the Customer pojo is set to Deal pojo as NULL.
I am using JSF 2.2.0
Thanks much for the help in advance.
It's not the converter, is the view scoped the one broken:
Since you're using JSF tags, you cannot use #ViewScoped annotation, because it was removed from specification and recovered only for CDI usage. You could use omnifaces view scoped or the components of apache myFaces (I personally recommend omnifaces).
You can confirm this creating a
System.out.print("Creating");
in the constructor and checking how is called each Ajax request, so the bean is not recovered and since is marked as view and is a partial request, the values are not setted again (unless you send all the form, which is not a nice solution), other workaround could be making the bean request and recover all the data each request, making it Session (but will be alive for the session), or the #ConvesationScoped, in which you'll have to destroy and start the conversation manually.
Again, my first recommendation could be change to a Java ee server compliant and use the CDI annotations since JSF are being depreciated and not updated anymore
I try to use JSF in combination with Bean Validation. Basically, everything works well, the validation works as expected, I get the correct message, but there is an exception on my Glassfish console:
Warnung: EJB5184:A system exception occurred during an invocation on EJB MyEntityFacade, method: public void com.mycompany.testbv.AbstractFacade.create(java.lang.Object)
Warnung: javax.ejb.EJBException
at com.sun.ejb.containers.EJBContainerTransactionManager.processSystemException(EJBContainerTransactionManager.java:748)
....
....
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:544)
at java.lang.Thread.run(Thread.java:744)
Caused by: javax.validation.ConstraintViolationException: Bean Validation constraint(s) violated while executing Automatic Bean Validation on callback event:'prePersist'. Please refer to embedded ConstraintViolations for details.
This exception occurs if I use custom constraints as well as predefined constraints.
Here is my sample code.
Sample Entity:
#Entity
#ValidEntity
public class MyEntity implements Serializable {
private static final long serialVersionUID = 3104398374500914142L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Size(min = 2)
private String name;
public MyEntity(String name) {
this.name = name;
}
public MyEntity() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Custom constraint:
#Constraint(validatedBy = MyValidator.class)
#Target({FIELD, METHOD, TYPE})
#Retention(RUNTIME)
public #interface ValidEntity {
String message() default "fail";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Custom validator:
public class MyValidator implements ConstraintValidator<ValidEntity, MyEntity>{
#Override
public void initialize(ValidEntity a) {
}
#Override
public boolean isValid(MyEntity t, ConstraintValidatorContext cvc) {
return false;
}
}
Sample Controller:
#Named
#SessionScoped
public class MyController implements Serializable {
private static final long serialVersionUID = -6739023629679382999L;
#Inject
MyEntityFacade myEntityFacade;
String text;
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public void saveNewEntity() {
try {
myEntityFacade.create(new MyEntity(text));
} catch (Exception e) {
Throwable t = e;
while (t != null) {
if (t instanceof ConstraintViolationException) {
FacesContext context = FacesContext.getCurrentInstance();
Set<ConstraintViolation<?>> constraintViolations = ((ConstraintViolationException) t).getConstraintViolations();
for (ConstraintViolation<?> constraintViolation : constraintViolations) {
FacesMessage facesMessage = new FacesMessage(constraintViolation.getMessage());
facesMessage.setSeverity(FacesMessage.SEVERITY_ERROR);
context.addMessage(null, facesMessage);
}
}
t = t.getCause();
}
}
}
}
Sample jsf page:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head></h:head>
<h:body>
<h:form>
<h:messages id="messages" />
<h:inputText value="#{myController.text}" />
<h:commandButton value="Save" action="#{myController.saveNewEntity()}" />
</h:form>
</h:body>
</html>
The MyEntityFacade only calls persist from entity manager.
As mentioned before, the application is running fine and the correct messages are shwon, but I want to avoid this exception in the Glassfish console.
Setting the validation mode in persistence.xml to NONE as discussed here is no option, because I want a validation.
I use JSF in version 2.2, the implementation is Mojarra. The version of Bean Validation is 1.1, the implementation is Hibernate Validator.
Application Server is Glassfish 4.0.
Class-level constraints do not work with JSF. Take a look at this answer. When you press the 'Save' button JSF checks only if name has at least 2 chars and does not take into account the ValidEntity constraint. JPA, on the other hand, complains that the bean is not valid and throws an exception.
UPDATE
1) the #Size constraint is on MyEntity.name property while in the facelet you have MyController.text property. In the JSF perspective there is nothing to validate. It has no knowledge of the MyEntity at all.
2) ValidEntity is always invalid, so JPA will always throw the exception (unless you disable validation) even if you properly set the MyEntity.name in the facelet.
<h:selectManyListbox id="sectorsListBox" size="2" multiple="multiple" value="#{Mybean.classificationSelectedItems}">
<f:selectItems id="sectors" value="#{Mybean.classificationSelectItems}"/>
</h:selectManyListbox>
Backing Bean has:
public class Mybean
{
private Map<String,String> classificationSelectItems = new LinkedHashMap<String,String>();
private List<String> classificationSelectedItems = new ArrayList<String>();
//getter and setter for both.
}
init()
{
classificationSelectItems.put("INS","Insurance")
classificationSelectItems.put("HLC","HealthCare")
}
The select many box gets initialized with these 2 values but the problem is only the last selected entry is getting stored in classificationSelectedItems. Why is that so ? And how do I get all the selected entries stored in the list of classificationSelectedItems ?
Adding FYI, the init method is class by Spring.
I have tested with an examle(reference:http://www.mkyong.com/jsf2/jsf-2-multiple-select-listbox-example/), good luck :)
Facelets:
<h:form id="form">
<h:selectManyListbox value="#{user.favFood1}" >
<f:selectItems value="#{user.favFood2Value}" />
</h:selectManyListbox>
<h:commandButton value="test"/>
</h:form>
Bean:
#ManagedBean(name = "user")
#ViewScoped
public class UserBean implements Serializable {
private static final long serialVersionUID = 1L;
public List<String> favFood1;
private Map<String, Object> food2Value;
public UserBean() {
favFood1 = new ArrayList<String>();
food2Value = new LinkedHashMap<String, Object>();
food2Value.put("Food2 - Fry Checken", "Fry Checken1"); //label, value
food2Value.put("Food2 - Tomyam Soup", "Tomyam Soup2");
food2Value.put("Food2 - Mixed Rice", "Mixed Rice3");
}
public List<String> getFavFood1() {
return favFood1;
}
public void setFavFood1(List<String> favFood1) {
this.favFood1 = favFood1;
}
public Map<String, Object> getFavFood2Value() {
return food2Value;
}
}
I noticed exactly this behaviour when I used a Collection in the setter method, like
public void setClassificationSelectedItems(Collection<String> in){
// store it somewhere
}
This setter is called during the restore phase but not during the update phase, so the previously set value will be set, but never the new one. If you use a List, it works as expected:
public void setClassificationSelectedItems(List<String> in){
// store it somewhere
}
Note that you will need to redeploy the application after such a change because the JSP needs to be recompiled but this isn’t done automatically.
I think that the question is simple, I get the error:
Caused by: java.lang.NullPointerException
at co.edu.unal.bienestar.dao.UserDao.save(UserDao.java:27)
at co.edu.unal.bienestar.facade.UserFacade.createUser(UserFacade.java:18)
at co.edu.unal.bienestar.mb.UserMB.createUser(UserMB.java:40)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.apache.el.parser.AstValue.invoke(AstValue.java:262)
at org.apache.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:278)
at com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:105)
at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:88)
... 27 more
When I try the create a new user, with a application which only have a entity class:
#Entity
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String username;
private String password;
private Role role;
<here getters and setters>
}
a Data Access Object:
public class UserDao {
#PersistenceContext
private EntityManager em;
public void save(User user) {
em.persist(user);
}
public void delete(User user) {
User userToBeRemoved = em.merge(user);
em.remove(userToBeRemoved);
}
public User update(User user) {
return em.merge(user);
}
public List<User> findAll() {
TypedQuery<User> query = em.createQuery(
"SELECT u FROM User u ORDER BY u.id", User.class);
return query.getResultList();
}
}
a facade:
public class UserFacade {
private UserDao userDao = new UserDao();
public void createUser(User user) {
userDao.save(user);
}
public List<User> listAll() {
List<User> result = userDao.findAll();
return result;
}
}
User Managed Bean:
#SessionScoped
#ManagedBean(name = "userMB")
public class UserMB implements Serializable {
public static final String INJECTION_NAME = "#{userMB}";
private static final long serialVersionUID = 1L;
private User user;
private UserFacade userfacade;
public User getUser() {
if (user == null) {
user = new User();
}
return user;
}
public void setUser(User user) {
this.user = user;
}
public void createUser() {
getUserfacade().createUser(user);
}
public UserFacade getUserfacade() {
if (userfacade == null){
userfacade = new UserFacade();
}
return userfacade;
}
}
And finally from JSF page I call the method like this:
<f:view>
<h:form>
<h1><h:outputText value="Create/Edit"/></h1>
<h:panelGrid columns="2">
<h:outputLabel value="Username:"/>
<h:inputText id="username" value="#{userMB.user.username}" title="username" />
<h:outputLabel value="Password:" />
<h:inputText id="password" value="#{userMB.user.password}" title="password" />
<h:outputLabel value="ID:" />
<h:inputText id="id" value="#{userMB.user.id}" title="id" />
</h:panelGrid>
<h:commandButton action="#{userMB.createUser}" value="create"/>
</h:form>
</f:view>
Where is my mistake?
#PersistenceContext works only in managed classes. Your UserDAO (and UserFacade) seems to be completely unmanaged and created manually. This way the EntityManager won't be injected at all. You'd need to manually create it as well. Right now it is null, which thus explains the NullPointerException.
Make the UserDAO (and UserFacade) a #Stateless EJB
#Stateless
public class UserDAO {
// ...
}
and use #EJB to inject them (and remove lazy loading in getter)
#EJB
private UserFacade userFacade; // Also on userDAO.
Or, if your environment doesn't support EJBs (e.g. Tomcat), then you'd need to either upgrade it to TomEE or to install OpenEJB on top of it, or to look for an alternate framework to manage your service facades and DAOs. Spring is often used, but why would you choose for it if Java EE 6 already offers the EJB3 awesomeness?
BalusC wrote: "#PersistenceContext works only in managed classes."
So, can it be an option to create the EntityManager in the UserMB by #PersistenceContext annotation, and pass it on to the DAO?
(I am aware that this is not the idea behind EE6, but if the alternatives are so complicated, one might think about this. Then you can stay with tomcat, and have a life cycle managed EntityManager, if I got this right.)
(Sorry for sending this as an answer instead of a comment, but i have to less points.)