JSF-Managed Beans and Design Patterns [duplicate] - jsf

This question already has answers here:
JSF Controller, Service and DAO
(2 answers)
Closed 6 years ago.
Lets assume, i have a student.xhtml form that has radiolists&dropdownmenu populated from its #ManagedBean Student(). As u know, in order to populate form from managedbean i need to have List<Object> fields in Student class. But i also want my Student class to be pure meaning it should have fields only related to itself, not the possible values it can get (i mean List<>). So i want to seperate my Student class from #ManagedBean. So i will have two classes at the end one of is pure Student class and StudentBean class which controls the view.
So my question is, is it good practice have two classes like below or i should go with one class? Two classes method duplicates fields so i don't know whether it affects performance to a bad extent.. What do you suggest?
Not wanted BeanClassWithStudent Pattern;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.faces.bean.*;
#ManagedBean
public class Student {
private String firstName;
private String lastName;
private String country;
private String favLanguage;
private List<String> countryList;
private List<String> favLanguageList;
#PostConstruct // generate DropDownList Combobox and radiobuttons From class fields
public void init() {
generateCountries();
generateFavLanguages();
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getFavLanguage() {
return favLanguage;
}
public void setFavLanguage(String favLanguage) {
this.favLanguage = favLanguage;
}
public List<String> getCountryList() {
return countryList;
}
public List<String> getFavLanguageList() {
return favLanguageList;
}
private void generateCountries(){
countryList = new ArrayList<>();
countryList.add("Turkey");
countryList.add("France");
countryList.add("Senegal");
countryList.add("USA");
}
private void generateFavLanguages(){
favLanguageList = new ArrayList<>();
favLanguageList.add("Java");
favLanguageList.add("Ruby");
favLanguageList.add("C++");
favLanguageList.add("Visual Basic");
}
}
My wanted seperate classes;
Student.class
public class Student {
private String firstName;
private String lastName;
private String country;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
Wanted StudentControllerBean;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.faces.bean.*;
#ManagedBean
public class StudentBean {
private String firstName;
private String lastName;
private String country;
private List<String> countryList;
private List<String> favLanguageList;
#PostConstruct // generate DropDownList Combobox and radiobuttons From class fields
public void init() {
generateCountries();
generateFavLanguages();
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public List<String> getCountryList() {
return countryList;
}
public List<String> getFavLanguageList() {
return favLanguageList;
}
private void generateCountries(){
countryList = new ArrayList<>();
countryList.add("Turkey");
countryList.add("France");
countryList.add("Senegal");
countryList.add("USA");
}
private void generateFavLanguages(){
favLanguageList = new ArrayList<>();
favLanguageList.add("Java");
favLanguageList.add("Ruby");
favLanguageList.add("C++");
favLanguageList.add("Visual Basic");
}
}

It is always better to maintain two separate Beans one for the presentation layer (#ManagedBean) and the other one (called as Business/Entiry Bean) for the Business tier (services layer) i.e., it is not a good idea to mix up both the presentation tier (Managed) beans with the Business beans rather you need to separate them like how you did.
The request flow between the J2EE tiers goes as follows:
HTML/JSP -> ManagedBean -> Service -> DAO -> Database
You need to convert the presentation bean data to the Business bean in the Action classes and then pass that to Business Bean Object to the Services layer. Service layer uses this Business Bean to interact with DAO classes which persist or do some transactions with the database.
This concept is applicable not only for JSF, but all other J2EE web frameworks (like Struts, Spring MVC, etc..).
You can find more details here on this concept.

Related

Convert List of Objects to List of Strings

I'm using Spring data jpa findAll() method. So it returns List of objects.
here is the entity.
#Entity
#Table(name = "country")
#Data
public class CountryEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "country_id")
private Long id;
#Column(name = "country_name")
private String name;
#OneToMany(mappedBy ="countryEntity")
private Collection<GovernmentEntity> governments;
}
and data jpa findAll() method is
List<CountryEntity> entities = countryRepo.findAll();
I want to get list of Country names as String WITHOUT USING loops or streams (performance issues).
I used streams and it works fine with javaFx ListView
#FxmlView("/address.fxml")
#Component
#RequiredArgsConstructor
public class HomeController implements Initializable {
private ObservableList<String> countriesNames;
#FXML
private ListView<String> countryListView;
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
List<CountryEntity> entities = countryRepo.findAll();
List <String> countryList = entities.stream().map(o-> Objects.toString(o.getName())).collect(Collectors.toList());
countriesNames = FXCollections.observableList(countryList);
countryListView.getItems().addAll(countriesNames);
}
}
Make your ListView a ListView<CountryEntity>, and use a cell factory to customize the display:
public class HomeController implements Initializable {
private ObservableList<String> countriesNames;
#FXML
private ListView<CountryEntity> countryListView;
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
List<CountryEntity> entities = countryRepo.findAll();
countryListView.getItems().addAll(entities);
countryListView.setCellFactory(lv -> new ListCell<CountryEntity>() {
#Override
protected void updateItem(CountryEntity country, boolean empty) {
super.updateItem(country, empty);
if (empty || country == null) {
setText("");
} else {
setText(country.getName()); // or however you want to display it
}
});
}
}
If you genuinely only want a list of country names, and don't want to retrieve a list of CountryEntitys and extract the names from them, then you need to define a method in your repository for the purpose:
public interface CountryEntityRepository extends JpaRepository<CountryEntity, Long> {
// existing methods...
#Query("select c.name from CountryEntity c")
List<String> findCountryNames() ;
}
And then of course just do
#FXML
private ListView<String> countryListView ;
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
countryListView.getItems().addAll(countryRepo.findCountryNames());
}
However, the first approach is almost certainly preferred. You will likely need the other data in the CountryEntity at some point.

How to set a list for addEntry with PostConstruct

Until now I add the things I want to have in my list over the getter which makes no sense and end in a mess up in my database, when adding a new entry.
My model until now:
#Data
#Entity
public class Telefonbuch {
/*#PostConstruct
public void init() {
//geschaeftsstellen = new ArrayList<String>();
geschaeftsstellen.add("Dortmund");
System.out.println("TEEEEEEEEEEEST");
geschaeftsstellen.add("Essen");
geschaeftsstellen.add("Stralsund");
geschaeftsstellen.add("Stuttgart");
geschaeftsstellen.add("Zürich");
geschaeftsstellen.add("Istanbul");
geschaeftsstellen.add("Köln");
geschaeftsstellen.add("Aachen");
geschaeftsstellen.add("Berlin");
}*/
public List<String> getGeschaeftsstellen() {
geschaeftsstellen = new ArrayList<String>();
geschaeftsstellen.add("Dortmund");
System.out.println("TEEEEEEEEEEEST");
geschaeftsstellen.add("Essen");
geschaeftsstellen.add("Stralsund");
geschaeftsstellen.add("Stuttgart");
geschaeftsstellen.add("Zürich");
geschaeftsstellen.add("Istanbul");
geschaeftsstellen.add("Köln");
geschaeftsstellen.add("Aachen");
geschaeftsstellen.add("Berlin");
//ArrayList<String> a = new ArrayList<String>();
//a.add("Test");
return geschaeftsstellen;
}
public void setGeschaeftsstellen(List<String> geschaeftsstellen) {
this.geschaeftsstellen = geschaeftsstellen;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column
private String vorname;
#Column
private String nachname;
#Column
private String telefonnummer;
#Column
private String handynummer;
#Column
private String geschaeftsstelle;
#Column
#ElementCollection
private List<String> geschaeftsstellen;
I just show the other variables to let you know what is in this. I know the #Column annotation for the List is not necessary. But it's a question for me. Should data like this be in a database or not? I will never use it again somewhere else. What is the right way to insert the data of the list for these selection buttons? When I just uncomment the PostConstruct it won't get called.
When I add #ManagedBean I get the error: DataModel must implement org.primefaces.model.SelectableDataModel when selection is enabled.
But in my .xhtml I already have selection="#{telefonbuchList.selectedEntry}" selectionMode="single" rowKey="#{telefonbuch.id}"
How do i know which import is the right one? javax.annotation or javax.faces.bean? Just with the annotation one it gets called.
Here can see where I use it. It's the tab "Neuer Eintrag" for a newEntry. The tab "Telefonbuch" is the list with a table for all entities.
Edit:
TelefonbuchListController on Request:
#Scope (value = "session")
#Component (value = "telefonbuchList")
#ELBeanName(value = "telefonbuchList")
#Join(path = "/", to = "/eintraege-liste.jsf")
public class TelefonbuchListController {
#Autowired
private TelefonbuchRepository telefonbuchRepository;
private List<Telefonbuch> eintraege;
#Deferred
#RequestAction
#IgnorePostback
public void loadData() {
eintraege = telefonbuchRepository.findAll();
}
public List<Telefonbuch> getEintraege() {
return eintraege;
}
private Telefonbuch selectedEntry;
public Telefonbuch getSelectedEntry() {
return selectedEntry;
}
public void setSelectedEntry(Telefonbuch selectedEntry) {
this.selectedEntry = selectedEntry;
}
public void deleteEntry() {
telefonbuchRepository.delete(selectedEntry);
eintraege.remove(selectedEntry);
selectedEntry = null;
}
I got your question that you are asking for a List getting filled with options usable with a selectOneRadio component - and why #PostConstruct does not work. #PostConstruct is ignored by JPA or other persistence frameworks - it's usable with CDI/Managed beans as far as I know.
Move it to your TelefonbuchList bean:
public class TelefonbuchListController {
#PostConstruct
public void init() {
geschaeftsstellen = new ArrayList<String>();
geschaeftsstellen.add("Dortmund");
geschaeftsstellen.add("Essen");
...
}
public List<String> getGeschaeftsstellen() {
return geschaeftsstellen;
}
}
You can use this list to fill your selectOneRadio component select items.

Send Object between #ViewScoped beans

I managed to create very simple example of sending Object between JSF pages:
First page:
#Named
#ViewScoped
public class Pricing
{
public Pricing()
{
int ww = 3;
PricingFormData obj = new PricingFormData(334, "Lalalala");
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("yourKey", obj);
}
Second page:
#Named
#ViewScoped
public class PricingCalculator implements Serializable
{
PricingFormData get;
public PricingCalculator()
{
get = (PricingFormData) FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("yourKey");
}
}
Custom Object:
public class PricingFormData
{
private int id;
private String name;
public PricingFormData(int id, String name)
{
this.id = id;
this.name = name;
}
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
}
This code works but I have several questions which I want to ask:
The code is working in View scope. What will happen if multiple users are clicking on the pages? Are these Objects are going to be mixed? Do I need to use some unique ID for Object key for example session ID. But here I don't have session.
What will happen if the Objects are too many(multiple users are working on the web site)? When the objects will be disposed?

backing bean in jsf scope

I am displaying a ProductList, which is made up of Product objects. The Product has attributes of type int, string, string, and int. The data is being pulled from the database and I build a ProductList. The data is in the database, I can see it, but when I display the table, the table shows 0's for the 2 int columns, and blanks in the String columns. Here is the ProductList:
#ManagedBean
public class ProductList {
private ArrayList<Product> allProducts;
public ProductList(){
allProducts = DatabaseConnector.getAllProducts();
}
public ArrayList<Product> getAllProducts(){
return allProducts;
}
public void setAllProducts(ArrayList<Product> allProducts){
this.allProducts = allProducts;
}
}
And here is the Product bean:
#ManagedBean
public class Product {
private int id;
private String productName;
private String description;
private int quantity;
public Product() {
}
public void setId(int id) {
this.id = id;
}
public void setProductName(String productName) {
this.productName = productName;
}
public void setDescription(String description) {
this.description = description;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public int getId() {
return id;
}
public String getProductName() {
return productName;
}
public String getDescription() {
return description;
}
public int getQuantity() {
return quantity;
}
}
Should I change the scope of the beans?
Add scope annotation to your bean , for example #RequestScoped or #ViewScoped
otherwise it will be the default scope which is #NoneScoped

Marshall object field as attribute

Here is what I have so far to marshall my POJO using JAXB :
#XmlRootElement
public class Local {
private Entity entity;
public void setEntity(Entity entity) {
this.entity = entity;
}
#XmlElement
public Entity getEntity() {
return entity;
}
}
and
#XmlRootElement
public class Entity {
private String name;
private String comment;
public void setName(String name){
this.name = name;
}
#XmlAttribute
public String getName(){
return this.name;
}
public void setComment...
#XmlAttribute
public void getComment...
}
With that, I get something like this:
<local>
<entity name="" comment=""></entity>
</local>
However, I would prefer to have the name attribute as an attribute of the local:
<local entityName="" entityComment=""></local>
Is the XmlJavaTypeAdapter a good way to begin with?
Thanks,
Alex
There are a couple of different options to handle this use case:
Option #1 - XmlAdapter (Any JAXB implementation)
You could use an XmlAdapter for this use case. This will work as long as only one attribute value comes from the Entity object:
EntityAdapter
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class EntityAdapter extends XmlAdapter<String, Entity>{
#Override
public String marshal(Entity entity) throws Exception {
if(null == entity) {
return null;
}
return entity.getName();
}
#Override
public Entity unmarshal(String name) throws Exception {
Entity entity = new Entity();
entity.setName(name);
return entity;
}
}
Local
The XmlAdapter is linked with the field/property using the #XmlJavaTypeAdapter annotation:
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement
public class Local {
private Entity entity;
public void setEntity(Entity entity) {
this.entity = entity;
}
#XmlAttribute
#XmlJavaTypeAdapter(EntityAdapter.class)
public Entity getEntity() {
return entity;
}
}
For More Information
http://blog.bdoughan.com/2010/07/xmladapter-jaxbs-secret-weapon.html
http://blog.bdoughan.com/2010/12/jaxb-and-immutable-objects.html
Option #2 - #XmlPath (EclipseLink JAXB (MOXy)
Alternatively if you are using EclipseLink JAXB (MOXy), the you could use the #XmlPath extension. This is useful with the Entity object corresponds to multiple XML attributes:
Local
Specifying the XPath "." indicated that the child contents will be written into the parent element
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.*;
#XmlRootElement
public class Local {
private Entity entity;
public void setEntity(Entity entity) {
this.entity = entity;
}
#XmlPath(".")
public Entity getEntity() {
return entity;
}
}
Entity
public class Entity {
private String name;
private String comment;
public void setName(String name){
this.name = name;
}
#XmlAttribute(name="entityName")
public String getName(){
return this.name;
}
public void setComment(String comment){
this.comment = comment;
}
#XmlAttribute(name="entityComment")
public String getComment(){
return this.comment;
}
}
For More Information
http://bdoughan.blogspot.com/2010/07/xpath-based-mapping.html
http://blog.bdoughan.com/2010/09/xpath-based-mapping-geocode-example.html
http://blog.bdoughan.com/2011/03/map-to-element-based-on-attribute-value.html
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html

Resources