How to save an entity for operations such as voting - jsf

let's take a youtube video page for example. after the page is rendered. you can upvote or downvote the video, and comment.
I'm having a similar case here. where an article is displayed with it's title and body(text). and I want to add the option for user so that they can vote. Two Entity looks like this:
public class Article implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private BigInteger id;
private User from;
private String title;
private String body;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "article")
private List<VoteArticle> votes = new ArrayList<>();
public class VoteArticle implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private BigInteger id;
#ManyToOne
#JoinColumn(name = "Article_ID", referencedColumnName = "id")
private Article article;
and I have jsf view that have a view param as the article's id article.xhtml?t=4
before rendering the view I look up and setup the article in the RequestScoped Bean (ArticleBean), then The view is rendered.
now as we all know since the bean is #RequestScoped all fields are re-initiliazed for every next request. So My question is, Do I need to get the article entity from the database every time I need to make an operation (voting, commenting) or is there a better way?
Right now my solution is to take the parameter view ".xhtml?t" and make a new Article entity, set its Id and use it.
Is something wrong with my solution? how do you implement such case? Thanks.

It sounds like setting your controller code to #ViewScoped instead of #RequestScoped would solve your problem.
You could make a method init() and give it a #PostConstruct annotation. Within init() you can pull your Article object from the database. Because the controller is view scoped these objects will stay until the user leaves the page.
You can call methods like voteUp() voteDown() on your ViewScoped controller via ajax calls and modify your Article object without having to pull it fresh from the DB each time.

Related

Primefaces: OrderList fills backend list with String instead of Object

In my backend view, I have a list of some POJO:
BackingView.java
#Getter #Setter private List<SomePOJO> pojoList = ...
SomePojo.java
#EqualsAndHashCode(callSuper = false, of = {"id"})
public class SomePojo implements Serializable {
private static final long serialVersionUID = 1L;
#Getter #Setter private Long id;
#Getter #Setter private Long date;
#Getter #Setter private Date name;
....
#Override
public String toString() {
return String.format("%s[id=%s]", getClass().getSimpleName(), id);
}
//making a readable string-representation of the object to display on the orderList
public String toStringOrderlistDisplay() {
return "Pojo with id " + id + "and so on... "
}
This list acts as you would expect in all regards. Recently, I wanted to add a feature to my frontend which allows the user to manually order this list. For that purpose I use Primefaces OrderList, like this:
Frontend.xhtml
<p:orderList id="ordList" widgetVar="ordList"
value="#{backingview.pojoList }" var="rfg"
controlsLocation="left" responsive="true" itemValue="#{rfg}"
itemLabel="#{rfg.toStringOrderlistDisplay()}">
</p:orderList>
As soon as I add the above p:orderList to my .xhtml file, I can see when inspecting in debug mode that pojoList no longer contains instances of PojoClass but rather plain strings (to be more precise, the string representation of the PojoClass-Object (What the toString() method would return)).
If I simply remove the orderList this problem does not occur. What is going on here? How is such a thing even possible in Java?
I tried using a converter as shown here CountryConverter.java, and it worked.
Maybe when you don't specify a convertor, it uses a default .toString() implementation and can't serialize back to a PojoClass.
You can submit an issue here to see if it's a bug.

How can CDI be called after the user class has been instantiated?

I have a class that looks like this:
#Named
public class TableView {
#PersistenceContext protected EntityManager em;
#Resource protected UserTransaction utx;
And of course I can get an instance during the construction of my bean like this:
#Inject private TableView view;
I believe it is CDI that has the job of filling in the EntityManager and the UserTransaction. However, after my user class has been instantiated I sometimes want another instance of TableView so how do I get it? Obviously
TableView anotherView = new TableView();
won't work since em and utx will be null. So how do I get a new working instance with the injections performed?
Instance interface should do what you need:
Instance<TableView> tableViewInstance;
TableView anotherView = tableViewInstance.get();
But as stated in the comments, your view should not have/be aware of transactions and entity manager.

How does #RequestScoped work in JSF?

This bean controls editing a Player. When it's first loaded, it fetches a player from the database into the player property. The form displays fields correctly.
On submission, it seems that a player receives a new Player() object with values submitted on the form. Consequently, all player fields that are not on the submitted form are lost. One of those fields is player.playerId. Since the submission produces a player with null playerId, entity manager creates a new entity in the database.
I thought that request scope meant the PlayerEditBean will live and remember its state between GET and POST.
#Named
#RequestScoped
public class PlayerEditBean implements Serializable {
private static final long serialVersionUID = 1L;
#Inject
PlayerService playerService=null;
private Player player=null;
#PostConstruct
public void load() {
String strId=FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("id");
// load entity if edit
if(strId!=null) {
Long id = Long.valueOf(strId);
player=playerService.getPlayer(id);
}
if(player==null) {
player=new Player();
}
}
public void save() {
playerService.savePlayer(player);
}

Retrieving selectOneMenu complex object as selected item

I'm beginning with JSF (Mojarra 2.2 and Glassfish 4) and currently practicing with a web application which job is to store Clients and their Orders in DB.
When creating a new Order, one feature is to allow choosing an existing client from a JSF <h:selectOneMenu>. An Order entity stores a Client entity among other attributes...
I've followed BalusC's great answer about prepopulating a <h:selectOneMenu> from a DB (here), and have successfully populated mine from data stored in an eager ApplicationScoped ManagedBean, but I can't manage to retrieve the selected item in the backing bean as complex object. It is always null.
This is driving me mad and your help will be truly appreciated!
Here are the relevant code snippets:
#ManagedBean(eager = true)
#ApplicationScoped
public class Data implements Serializable {
private static final long serialVersionUID = 1L;
#EJB
private ClientDao clientDao;
private List<Client> clients;
#PostConstruct
private void init() {
clients = clientDao.lister();
}
public List<Client> getClients() {
return clients;
}
}
Order creation bean (note: 'commande' means order ;)
#ManagedBean
#RequestScoped
public class CreerCommandeBean implements Serializable {
private static final long serialVersionUID = 1L;
private Commande commande;
private String choixNouveauClient = "nouveauClient";
#EJB
private CommandeDao commandeDao;
public CreerCommandeBean() {
commande = new Commande();
}
public void inscrire() {
System.out.println("client : " + commande.getClient()); // prints **NULL**
// ... orderService to store in DB
}
... getters and setters
Client converter:
#FacesConverter(value = "clientConverter", forClass = Client.class)
public class ClientConverter implements Converter {
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null) {
return null;
}
Data data = context.getApplication().evaluateExpressionGet(context, "#{data}", Data.class);
for (Client c : data.getClients()) {
if (c.getId().toString().equals(value)) {
return c;
}
}
throw new ConverterException(new FacesMessage(String.format("Cannot convert %s to Client", value)));
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return (value instanceof Client) ? String.valueOf(((Client) value).getId()) : null;
}
}
Facelet excerpt:
<p:outputPanel id="gridContainerAncienClient">
<p:selectOneMenu value="#{creerCommandeBean.commande.client}"
rendered="#{creerCommandeBean.choixNouveauClient == 'ancienClient'}">
<f:converter converterId="clientConverter" />
<f:selectItems value="#{data.clients}" var="cli"
itemValue="#{cli}" itemLabel="#{cli.prenom} #{cli.nom}" />
</p:selectOneMenu>
</p:outputPanel>
CreerCommandeBean is #RequestScoped. That means it will live only for one request.
When you select a client to be assigned to #{creerCommandeBean.commande.client} you do this by a request. #{creerCommandeBean.commande.client} is now the selected client. Then the request is over, the bean gets destroyed and your "changes" are lost.
When you try to retrieve that data, you do that by a request again: A new instance of CreerCommandeBean is created and the constructor assigns the property commande with a new instance of Commande whose property client again is probably null.
Solution:
Use a broader scope. e.g. #ViewScoped which makes the bean "live" as long as you stay in the same view - no matter how many requests you make.
Tip:
Read BalusC's Post on Communication is JSF 2.0. Parts might be slightly different in JSF 2.2 but it's still a good and comprehensive introduction.
I got stuck with similar problem, only to realize that I forgot to implement equals() and hashCode() method in my Object. Client Class in this case.
I should blame myself for skipping the instructions in BalusC's blog.
"...Please note the Object#equals() implementation. This is very important for JSF. After conversion, it will compare the selected item against the items in the list. As the Object#equals() also require Object#hashCode(), this is implemented as well...."

JSF2.0: ManagedProperty Lifecycle?

I have a problem I don't understand: Behind any View I have a controller ManagedBean that is RequestScoped and a data ManagedBean, that holds the data for the view and is SessionScoped.
So there are two views, which are login with loginData and loginController and overview with overviewData and overviewController.
The functionality should be like that:
The User logs into the application (loginController method)
If Authentication is successfull, there is a redirect to overview.xhtml (again in loginController method)
Then the overviewData gets its data by the overviewController, which retrieves them from business logic layer
The overview.xhtml shows the retireved data
So, the point is that I want to fill overviewData out of loginController, right after login! (???or if possible right befor overview view is constructed, if possible???).
I tried it with managedProperties, but the one I initiate in loginController is a different object than the managedProperty in overviewController, although they have the same name! How is that possible.
Oh boy, I doubt you guys understand what I mean, so I need to post some code:
LoginController.java
...
#ManagedBean
#RequestScoped
public class LoginController {
#ManagedProperty(value = "#{overviewData}")
private OverviewData overviewData;
OverviewController overviewController;
public LoginController(){
overviewController = new OverviewController ();
}
String login() throws Exception {
UsernamePasswordToken token = new UsernamePasswordToken(loginData.getName(), loginData.getPw().trim());
try {
currentUser.login(token);
overviewController.fillProjects();
...
OverviewController.java
...
#ManagedBean
#RequestScoped
public class OverviewController {
#ManagedProperty(value = "#{overviewData}")
private OverviewData overviewData;
public void fillProjects(){
if(overviewData == null){
overviewData = new OverviewData();
}
overviewData.setProjects(projectService.getProjects()); //retrieves data from business logic
}
...
OverviewData.java
...
#ManagedBean(name = "overviewData")
#SessionScoped
public class OverviewData {
private List<ProjectDTO> projects; //that's what the view needs to display the overview
public void setProjects(List<ProjectDTO> projects) {
this.projects = projects;
}
...
I hope that helps to show my problem, if you don't understand it, pls ask in a comment..
Would be nice if you can help me :-)
Cheers...
You're creating beans yourself using new instead of letting JSF do the job.
overviewController = new OverviewController ();
and
overviewData = new OverviewData();
This is wrong. JSF won't utilize any beans which you've created yourself this way. Remove those lines and add another #ManagedProperty on overviewController inside LoginController (and make the property private).
#ManagedProperty(value="#{overviewController}")
private OverviewController overviewController;
JSF will create the bean itself and set it as managed property directly after parent bean's construction. You just have to access it the usual Java way (without the need for nullchecks).

Resources