Does it make sense to use Entities as JSF Backing Beans?
#Entity
#ManagedBean
#ViewScoped
public class User {
private String firstname;
private String lastname;
#EJB
private UserService service;
public void submit() {
service.create(this);
}
// ...
}
Or is it better to keep them separately and transfer the data from the backing bean to the entity at the end?
#ManagedBean
#ViewScoped
public class UserBean {
private String firstname;
private String lastname;
#EJB
private UserService service;
public void submit() {
User user = new User();
user.setFirstname(firstname);
user.setLastname(lastname);
service.create(user);
}
// ...
}
You could do so. It's technically possible. But it does (design)functionally not make any sense. You're basically tight-coupling the model with the controller. Usually the JPA entity (model) is a property of a JSF managed bean (controller). This keeps the code DRY. You don't want to duplicate the same properties over all place, let alone annotations on those such as bean validation constraints.
E.g.
#ManagedBean
#ViewScoped
public class Register {
private User user;
#EJB
private UserService service;
#PostConstruct
public void init() {
user = new User();
}
public void submit() {
service.create(user);
}
public User getUser() {
return user;
}
}
with this Facelets page (view):
<h:form>
<h:inputText value="#{register.user.email}" />
<h:inputSecret value="#{register.user.password}" />
<h:inputText value="#{register.user.firstname}" />
<h:inputText value="#{register.user.lastname}" />
...
<h:commandButton value="Register" action="#{register.submit}" />
</h:form>
See also:
What components are MVC in JSF MVC framework?
JSF Controller, Service and DAO
Contradictory explanations of MVC in JSF
JSF 2 reusing the validation defined in the JPA entities?
why shouldn't entity bean be managed by JSF framework?
Related
I read the other posts about this subject but I still can't get it to work
This are my beans:
Bean1:
#ManagedBean()
#SessionScoped
public class Bean1 implements Serializable {
//Here are some important Properties
public String buttonPressed() {
return "bean2.xhtml";
}
}
<h:form>
<p:commandButton action="#{Bean1.buttonPressed}" value="Do Work"/>
</h:form>
Bean2:
#ManagedBean()
#SessionScoped
public class Bean2 implements Serializable {
#ManagedProperty(value = "#{Bean1}")
private Bean1 b1;
//getter/setter is here
public String doWorkOnSubmit() {
//Access important Properties from bean1
b1.getFoo()
}
}
Now I have two Problems
1.) How to call "doWorkOnSubmit" if the button in Bean1 is pressed? I can't use the constructor because it's SessionScoped and I don't know how to call doWorkOnSubmit un submit
2.)The managed property "b1" is sometimes null
Since on clicking of Do Work button you are calling Bean1.buttonPressed() action method you can call Bean2s doWorkOnSubmit() by injecting Bean2 in Bean1.
Here is your code altered:
#ManagedBean()
#SessionScoped
public class Bean1 implements Serializable {
//Here are some important Properties
/*
* Inject Bean2 here. since both beans are session scoped,
* there ain't gonna be any problem in injection.
*/
#ManagedProperty(value = "#{Bean2}")
private Bean2 b2;
//GETTER SETTER for b2
public String buttonPressed() {
//Here you will be invoking injected managed beans method.
b2.doWorkOnSubmit();
return "bean2.xhtml";
}
}
Does it make sense to use Entities as JSF Backing Beans?
#Entity
#ManagedBean
#ViewScoped
public class User {
private String firstname;
private String lastname;
#EJB
private UserService service;
public void submit() {
service.create(this);
}
// ...
}
Or is it better to keep them separately and transfer the data from the backing bean to the entity at the end?
#ManagedBean
#ViewScoped
public class UserBean {
private String firstname;
private String lastname;
#EJB
private UserService service;
public void submit() {
User user = new User();
user.setFirstname(firstname);
user.setLastname(lastname);
service.create(user);
}
// ...
}
You could do so. It's technically possible. But it does (design)functionally not make any sense. You're basically tight-coupling the model with the controller. Usually the JPA entity (model) is a property of a JSF managed bean (controller). This keeps the code DRY. You don't want to duplicate the same properties over all place, let alone annotations on those such as bean validation constraints.
E.g.
#ManagedBean
#ViewScoped
public class Register {
private User user;
#EJB
private UserService service;
#PostConstruct
public void init() {
user = new User();
}
public void submit() {
service.create(user);
}
public User getUser() {
return user;
}
}
with this Facelets page (view):
<h:form>
<h:inputText value="#{register.user.email}" />
<h:inputSecret value="#{register.user.password}" />
<h:inputText value="#{register.user.firstname}" />
<h:inputText value="#{register.user.lastname}" />
...
<h:commandButton value="Register" action="#{register.submit}" />
</h:form>
See also:
What components are MVC in JSF MVC framework?
JSF Controller, Service and DAO
Contradictory explanations of MVC in JSF
JSF 2 reusing the validation defined in the JPA entities?
why shouldn't entity bean be managed by JSF framework?
I have a problem with JSF2 #ManagedProperty annotation. I have a managed bean that create a user, then i want to pass this user to another bean to display other informations relative to that user. So my code is below :
#Named("userController")
#SessionScoped
public class UserController implements Serializable {
#EJB
private session.UserFacade ejbFacade;
private List<User> items = null;
private User selected;
private User utente;
private String user;
private String pass;
----getter and setter----
In the Schedule Bean I use the userController bean in the #PostConstruct :
#Named(value = "scheduleController")
#SessionScoped
public class ScheduleController implements Serializable {
private ScheduleModel eventModel;
private ScheduleEvent event = new DefaultScheduleEvent();
#EJB
ActivityFacade ejbFacade;
#ManagedProperty(value="#{userController}")
private UserController credentials;
public ScheduleController() {
}
#PostConstruct
public void init(){
eventModel = new DefaultScheduleModel();
List<Activity> daFare =ejbFacade.findForUser(credentials.getUtente().getIdUser());
for(int i=0;i<daFare.size();i++){
eventModel.addEvent(new DefaultScheduleEvent(daFare.get(i).getDescrizione(),daFare.get(i).getDalleOre(),daFare.get(i).getAlleOre() ));
}
}
public void setCredentials(UserController credentials) {
this.credentials = credentials;
}
When i debug the code, I see that the UserController credentials is always null.... What's wrong ? I need to specify something in faces-config.xml ?
Thanks a lot for your tips.
EDIT :
After change #ManagedProperty with #Inject and after add the below beans.xml in WEB-INF folder, the problem persist.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
The app work in this way:
A user can log in via this xhtml page:
<body>
<h:form>
<p:growl id="msgs" showDetail="true" />
<p:panel header="Inserisci i tuoi dati" style="margin: 0 auto; text-align: center" >
<h:panelGrid columns="2" cellpadding="5" style="margin: 0 auto; text-align: center">
<p:outputLabel value="Username"/>
<p:inputText value="#{userController.user}" required="true" label="Username" size="40"/>
<p:outputLabel value="Password" />
<p:password value="#{userController.pass}" required="true" label="Password" size="40"/>
<p:commandButton action="#{userController.validateLogin}" value="Login" update="msgs" ajax="false" style="text-align: center"/>
</h:panelGrid>
</p:panel>
</h:form>
</body>
After submit the form, the userController verify that this specific user exist in a DB table and then instantiate the User utente ( id, username, password, email as field ). After this, the app redirect to home.xhtml that display several user's information and select several information of a DB table selected by user's id.
Here is the code where I set User utente:
public String validateLogin(){
String output="home";
boolean exist=false;
exist=ejbFacade.logValid(user,pass);
if(!exist){
//FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO,"Username o Password errata","Contattare l'amministratore"));
return "index";
}else{
utente=ejbFacade.setUtente(user);
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage(null, new FacesMessage("Accesso eseguito con successo.","Bentornato " + user));
return output;
}
}
The code sequence in which i control the existece of a user and set the utente is:
#Stateless
public class UserFacade extends AbstractFacade<User> {
#PersistenceContext(unitName = "ImmobiliareWebPU")
private EntityManager em;
#Override
protected EntityManager getEntityManager() {
return em;
}
public UserFacade() {
super(User.class);
}
public boolean logValid(String user, String pass) {
List<User> utente ;
boolean autorizzato;
String find = "User.findByUsername&Password";
Query query = em.createNamedQuery(find, User.class);
query.setParameter("username", user);
query.setParameter("password", pass);
utente =query.getResultList();
if (utente.isEmpty()) {
autorizzato=false;
}
else{
autorizzato=true;
}
return autorizzato;
}
public User setUtente(String user) {
//SETTO L'UTENTE PER LA SESSIONE DEL PROGRAMMA
User utente;
String find = "User.findByUsername";
Query query = em.createNamedQuery(find, User.class);
query.setParameter("username", user);
utente =(User) query.getSingleResult();
return utente;
}
}
the code where I need user's information is :
#Named(value = "scheduleController")
#RequestScoped
public class ScheduleController implements Serializable {
private ScheduleModel eventModel;
private ScheduleEvent event = new DefaultScheduleEvent();
#EJB
ActivityFacade ejbFacade;
#Inject
private UserController credentials;
public ScheduleController() {
}
#PostConstruct
public void init(){
eventModel = new DefaultScheduleModel();
List<Activity> daFare =ejbFacade.findForUser(credentials.getUtente().getIdUser());
for(int i=0;i<daFare.size();i++){
eventModel.addEvent(new DefaultScheduleEvent(daFare.get(i).getDescrizione(),daFare.get(i).getDalleOre(),daFare.get(i).getAlleOre() ));
}
}
In the ejbFacade i make a simple query :
#Stateless
public class ActivityFacade extends AbstractFacade<Activity> {
#PersistenceContext(unitName = "ImmobiliareWebPU")
private EntityManager em;
#Override
protected EntityManager getEntityManager() {
return em;
}
public ActivityFacade() {
super(Activity.class);
}
public List<Activity> findForUser(int utenteId) {
//RICHIEDO LE ATTIVITA' DEL SINGOLO UTENTE
List<Activity> daFare ;
String find = "Activity.findByUserId";
Query query = em.createNamedQuery(find, Activity.class);
query.setParameter("idUser", utenteId);
daFare =query.getResultList();
return daFare;
}
}
In debug mode i see an istance of UserController with all null fields; it seems to be a new istance of UserController. I use also "import javax.enterprise.context.SessionScoped" . Where is my fault ?
When i debug the code, I see that the UserController credentials is
always null.... What's wrong?
Please note you are mixing up CDI (Context and Dependency Injection) with Managed properties, which is probably causing the described behavior. Your beans are annotated with #Named instead of #ManagedBean so this:
#ManagedProperty(value="#{userController}")
private UserController credentials;
Should be replaced by this:
#Inject
private UserController credentials;
For a really great explanation about the difference between CDI and backing beans see #BalusC answer in this topic: Backing beans (#ManagedBean) or CDI Beans (#Named)?.
I need to specify something in faces-config.xml?
No, but you should include a beans.xml like exemplified here: Packaging CDI applications. As far as I remember this file is mandatory regardless the discovery mode. Here is the beans.xml that you can auto-generate in NetBeans:
New file --> Context and Dependency Injection --> beans.xml (CDI Configuration file).
This is all I've ever needed to make CDI work:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
bean-discovery-mode="annotated">
</beans>
I want to get the value with the getter method, but it doesn't work. I use SessionScoped into my two managed-beans.
<h:outputLabel for="commentInput" value="Comment:" />
<p:inputTextarea id="commentInput" value="#{dashboardBean.currentComment}" rows="6" cols="25" label="commentInput" required="true"/>
#ManagedBean
#SessionScoped
public class DashboardBean implements Serializable
{
private String _currentComment = null;
public String getCurrentComment() {
return this._currentComment;
}
public void setCurrentComment(String _currentComment) {
this._currentComment = _currentComment;
}
}
If i call getter in this class, it's works.
But in the other class:
#ManagedBean
#SessionScoped
public class PanelListener extends AjaxBehaviorListenerImpl
{
private DashboardBean _dashDashboardBean = null;
public void editMemo(ActionEvent actionEvent)
{
System.out.println("Statements ==== [ " + _dashDashboardBean.getCurrentComment() + " ]");
}
}
I have an NullPointerException.
You need to use #ManagedProperty annotation to inject one bean into another.
#ManagedProperty("#{dashboardBean}")
private DashboardBean bean;
public DashboardBean getBean(){
return this.bean;
}
public void setBean(DashboardBean bean){
this.bean = bean;
}
Make sure the scope of the ManagedProperty is greater than or equal to the scope of bean in which you are injecting.
so here, DashBoardBean should have scope greater than or equal to PanelListener
Please note that JSF needs public getters and setters to access the fields
You have to use #ManagedProperty annotation. So try this in PanelListener, note that you need a setter to perform the bean injection. You can also only inject beana with greater or same scope to the bean with lower scopes (so for example you can inject SessionScoped to the RequestScoped but not the other way around).
#ManagedProperty("#{dashboardBean}")
private DashboardBean bean;
private void setDashboardBean(DashboardBean bean) {
this.bean = bean;
}
Form snippet:
<h:selectOneMenu id="supervisor" value="#{newTopic.supervisor}">
<s:objectConverter/>
<f:selectItems value="#{supervisors}" var="_supervisor"
itemValue="#{_supervisor}"
itemLabel="#{_supervisor.name.firstName} #{_supervisor.name.lastName}"></f:selectItems>
</h:selectOneMenu>
<h:commandButton action="#{newTopicAction.createTopic}" value="Create topic"/>
Controller action:
#Model
public class NewTopicAction implements Serializable{
#Inject
private TopicManager topicManager;
private ThesisTopic newTopic;
public String createTopic(){
topicManager.createTopic(newTopic);
return "topics?faces-redirect=true";
}
#PostConstruct
public void init(){
newTopic = new ThesisTopic();
}
#Produces
#Model
public ThesisTopic getNewTopic(){
return newTopic;
}
}
Why isn't newTopic.supervisor populated with selected supervisor, when newTopicAction.createTopic is invoked? Every other field works as expected, except this one. It may be something about selectOneMenu what i don't understand properly.