Статьи

Полное веб-приложение Tomcat JSF Primefaces JPA Hibernate — Часть 2

ManagedBeans

Этот пост продолжается с первой части этого урока.

В пакете «com.mb» вам нужно будет создать классы ниже:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.mb;
 
import org.primefaces.context.RequestContext;
 
import com.util.JSFMessageUtil;
 
public class AbstractMB {
 private static final String KEEP_DIALOG_OPENED = 'KEEP_DIALOG_OPENED';
 
 public AbstractMB() {
  super();
 }
 
 protected void displayErrorMessageToUser(String message) {
  JSFMessageUtil messageUtil = new JSFMessageUtil();
  messageUtil.sendErrorMessageToUser(message);
 }
 
 protected void displayInfoMessageToUser(String message) {
  JSFMessageUtil messageUtil = new JSFMessageUtil();
  messageUtil.sendInfoMessageToUser(message);
 }
 
 protected void closeDialog(){
  getRequestContext().addCallbackParam(KEEP_DIALOG_OPENED, false);
 }
 
 protected void keepDialogOpen(){
  getRequestContext().addCallbackParam(KEEP_DIALOG_OPENED, true);
 }
 
 protected RequestContext getRequestContext(){
  return RequestContext.getCurrentInstance();
 }
}
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
package com.mb;
 
import java.io.Serializable;
import java.util.List;
 
import javax.faces.bean.*;
 
import com.facade.DogFacade;
import com.model.Dog;
 
@ViewScoped
@ManagedBean
public class DogMB extends AbstractMB implements Serializable {
 private static final long serialVersionUID = 1L;
 
 private Dog dog;
 private List<Dog> dogs;
 private DogFacade dogFacade;
 
 public DogFacade getDogFacade() {
  if (dogFacade == null) {
   dogFacade = new DogFacade();
  }
 
  return dogFacade;
 }
 
 public Dog getDog() {
  if (dog == null) {
   dog = new Dog();
  }
 
  return dog;
 }
 
 public void setDog(Dog dog) {
  this.dog = dog;
 }
 
 public void createDog() {
  try {
   getDogFacade().createDog(dog);
   closeDialog();
   displayInfoMessageToUser('Created With Sucess');
   loadDogs();
   resetDog();
  } catch (Exception e) {
   keepDialogOpen();
   displayErrorMessageToUser('Ops, we could not create. Try again later');
   e.printStackTrace();
  }
 }
 
 public void updateDog() {
  try {
   getDogFacade().updateDog(dog);
   closeDialog();
   displayInfoMessageToUser('Updated With Sucess');
   loadDogs();
   resetDog();
  } catch (Exception e) {
   keepDialogOpen();
   displayErrorMessageToUser('Ops, we could not create. Try again later');
   e.printStackTrace();
  }
 }
 
 public void deleteDog() {
  try {
   getDogFacade().deleteDog(dog);
   closeDialog();
   displayInfoMessageToUser('Deleted With Sucess');
   loadDogs();
   resetDog();
  } catch (Exception e) {
   keepDialogOpen();
   displayErrorMessageToUser('Ops, we could not create. Try again later');
   e.printStackTrace();
  }
 }
 
 public List<Dog> getAllDogs() {
  if (dogs == null) {
   loadDogs();
  }
 
  return dogs;
 }
 
 private void loadDogs() {
  dogs = getDogFacade().listAll();
 }
 
 public void resetDog() {
  dog = new Dog();
 }
}
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.mb;
 
import java.io.Serializable;
 
import javax.faces.bean.*;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;
 
import com.model.User;
 
@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;
 
 public boolean isAdmin() {
  return user.isAdmin();
 }
 
 public boolean isDefaultUser() {
  return user.isUser();
 }
 
 public String logOut() {
  getRequest().getSession().invalidate();
  return '/pages/public/login.xhtml';
 }
 
 private HttpServletRequest getRequest() {
  return (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
 }
 
 public User getUser() {
  return user;
 }
 
 public void setUser(User user) {
  this.user = user;
 }
}
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
package com.mb;
 
import java.io.Serializable;
import java.util.*;
 
import javax.faces.bean.*;
 
import com.facade.*;
import com.model.*;
import com.sun.faces.context.flash.ELFlash;
 
@ViewScoped
@ManagedBean
public class PersonMB extends AbstractMB implements Serializable {
 private static final long serialVersionUID = 1L;
 
 private static final String SELECTED_PERSON = 'selectedPerson';
 
 private Dog dog;
 private Person person;
 private Person personWithDogs;
 private Person personWithDogsForDetail;
 
 private List<Dog> allDogs;
 private List<Person> persons;
 
 private DogFacade dogFacade;
 private PersonFacade personFacade;
 
 public void createPerson() {
  try {
   getPersonFacade().createPerson(person);
   closeDialog();
   displayInfoMessageToUser('Created With Sucess');
   loadPersons();
   resetPerson();
  } catch (Exception e) {
   keepDialogOpen();
   displayErrorMessageToUser('Ops, we could not create. Try again later');
   e.printStackTrace();
  }
 }
 
 public void updatePerson() {
  try {
   getPersonFacade().updatePerson(person);
   closeDialog();
   displayInfoMessageToUser('Updated With Sucess');
   loadPersons();
   resetPerson();
  } catch (Exception e) {
   keepDialogOpen();
   displayErrorMessageToUser('Ops, we could not create. Try again later');
   e.printStackTrace();
  }
 }
 
 public void deletePerson() {
  try {
   getPersonFacade().deletePerson(person);
   closeDialog();
   displayInfoMessageToUser('Deleted With Sucess');
   loadPersons();
   resetPerson();
  } catch (Exception e) {
   keepDialogOpen();
   displayErrorMessageToUser('Ops, we could not create. Try again later');
   e.printStackTrace();
  }
 }
 
 public void addDogToPerson() {
  try {
   getPersonFacade().addDogToPerson(dog.getId(), personWithDogs.getId());
   closeDialog();
   displayInfoMessageToUser('Added With Sucess');
   reloadPersonWithDogs();
   resetDog();
  } catch (Exception e) {
   keepDialogOpen();
   displayErrorMessageToUser('Ops, we could not create. Try again later');
   e.printStackTrace();
  }
 }
 
 public void removeDogFromPerson() {
  try {
   getPersonFacade().removeDogFromPerson(dog.getId(), personWithDogs.getId());
   closeDialog();
   displayInfoMessageToUser('Removed With Sucess');
   reloadPersonWithDogs();
   resetDog();
  } catch (Exception e) {
   keepDialogOpen();
   displayErrorMessageToUser('Ops, we could not create. Try again later');
   e.printStackTrace();
  }
 }
 
 public Person getPersonWithDogs() {
  if (personWithDogs == null) {
   if (person == null) {
    person = (Person) ELFlash.getFlash().get(SELECTED_PERSON);
   }
 
   personWithDogs = getPersonFacade().findPersonWithAllDogs(person.getId());
  }
 
  return personWithDogs;
 }
 
 public void setPersonWithDogsForDetail(Person person) {
  personWithDogsForDetail = getPersonFacade().findPersonWithAllDogs(person.getId());
 }
 
 public Person getPersonWithDogsForDetail() {
  if (personWithDogsForDetail == null) {
   personWithDogsForDetail = new Person();
   personWithDogsForDetail.setDogs(new ArrayList<Dog>());
  }
 
  return personWithDogsForDetail;
 }
 
 public void resetPersonWithDogsForDetail(){
  personWithDogsForDetail = new Person();
 }
 
 public String editPersonDogs() {
  ELFlash.getFlash().put(SELECTED_PERSON, person);
  return '/pages/protected/defaultUser/personDogs/personDogs.xhtml';
 }
 
 public List<Dog> complete(String name) {
  List<Dog> queryResult = new ArrayList<Dog>();
 
  if (allDogs == null) {
   dogFacade = new DogFacade();
   allDogs = dogFacade.listAll();
  }
 
  allDogs.removeAll(personWithDogs.getDogs());
 
  for (Dog dog : allDogs) {
   if (dog.getName().toLowerCase().contains(name.toLowerCase())) {
    queryResult.add(dog);
   }
  }
 
  return queryResult;
 }
 
 public PersonFacade getPersonFacade() {
  if (personFacade == null) {
   personFacade = new PersonFacade();
  }
 
  return personFacade;
 }
 
 public Person getPerson() {
  if (person == null) {
   person = new Person();
  }
 
  return person;
 }
 
 public void setPerson(Person person) {
  this.person = person;
 }
 
 public List<Person> getAllPersons() {
  if (persons == null) {
   loadPersons();
  }
 
  return persons;
 }
 
 private void loadPersons() {
  persons = getPersonFacade().listAll();
 }
 
 public void resetPerson() {
  person = new Person();
 }
 
 public Dog getDog() {
  if (dog == null) {
   dog = new Dog();
  }
 
  return dog;
 }
 
 public void setDog(Dog dog) {
  this.dog = dog;
 }
 
 public void resetDog() {
  dog = new Dog();
 }
 
 private void reloadPersonWithDogs() {
  personWithDogs = getPersonFacade().findPersonWithAllDogs(person.getId());
 }
}
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package com.mb;
 
import javax.faces.bean.*;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;
 
import com.facade.UserFacade;
import com.model.User;
 
@RequestScoped
@ManagedBean
public class LoginMB extends AbstractMB {
 @ManagedProperty(value = UserMB.INJECTION_NAME)
 private UserMB userMB;
 
 private String email;
 private String password;
 
 public String getEmail() {
  return email;
 }
 
 public void setEmail(String email) {
  this.email = email;
 }
 
 public String getPassword() {
  return password;
 }
 
 public void setPassword(String password) {
  this.password = password;
 }
 
 public String login() {
  UserFacade userFacade = new UserFacade();
 
  User user = userFacade.isValidLogin(email, password);
 
  if(user != null){
   userMB.setUser(user);
   FacesContext context = FacesContext.getCurrentInstance();
   HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest();
   request.getSession().setAttribute('user', user);
   return '/pages/protected/index.xhtml';
  }
 
  displayErrorMessageToUser('Check your email/password');
 
  return null;
 }
 
 public void setUserMB(UserMB userMB) {
  this.userMB = userMB;
 }
}

О приведенном выше коде:

  • Все ManagedBeans отвечают только за действия VIEW; ManagedBeans должен нести ответственность за обработку единственного результата бизнес-методов. В ManagedBeans нет бизнес-правил. Некоторые бизнес-действия в слое представления очень легко выполнить, но это не очень хорошая практика.
  • Класс LoginMB использует другой ManagedBean (UserMB), который был введен внутри него. Чтобы внедрить ManagedBean в другой ManagedBean, вы должны сделать следующее:
    • Использует @ManagedProperty поверх введенного ManagedBean
    • Создайте метод set для свойства, например « loginMB.setUserMB (…) »
  • Класс PersonMB может получать действия по рефакторингу, потому что он слишком большой. PersonMB делает так, чтобы новичкам легче было понять код быстрее.

Наблюдения за @ViewScoped

В управляемых компонентах с аннотацией @ViewScoped вы увидите некоторые методы перезагрузки и сброса. Оба метода необходимы для сброса состояния объектов; например, объект-собака будет хранить значения из представления после выполнения метода (сохраняться в базе данных, отображать значения в диалоговом окне). Если пользователь откроет диалоговое окно de create и успешно создаст собаку, этот объект dog будет содержать все значения, пока пользователь остается на той же странице. Если пользователь снова откроет диалог создания, все данные последней записанной собаки будут отображаться там. Вот почему у нас есть методы сброса.

Если вы обновляете объект в базе данных, объект в пользовательском представлении также должен получать это обновление, объекты ManagedBean должны получать эти новые данные. Если вы обновили имя собаки в базе данных, список собак также должен получить эту обновленную собаку. Вы можете запросить эти новые данные в базе данных или просто обновить значения управляемого компонента.

Разработчик должен знать:

  • Перезагрузите данные управляемого компонента, опрашивающие базу данных (методы перезагрузки) : если запущенный запрос для перезагрузки объекта ManagedBean содержит огромное количество данных, его запрос может повлиять на производительность приложения. Разработчик может использовать данные с отложенной загрузкой. Нажмите здесь, чтобы узнать больше о Lazy Datatable .
  • Перезагрузите обновленный объект непосредственно в управляемом bean-компоненте, не запрашивая базу данных : представьте, что user1 обновляет имя dog1 в базе данных и одновременно user2 обновляет возраст dog2. Пользователь 1 увидит старые данные о собаке 2, которые могут вызвать проблему целостности базы данных, если пользователь 1 обновит собаку 2. Решением этого подхода может быть поле версии в таблице базы данных. Перед обновлением это поле будет проверено. Если поле версии не содержит того же значения, которое найдено в базе данных, может возникнуть исключение. При таком подходе, если пользователь1 обновляет dog2, значение версии не будет таким же.

JSFMessageUtil

В пакете «com.util» создайте класс ниже:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.util;
 
import javax.faces.application.FacesMessage;
import javax.faces.application.FacesMessage.Severity;
import javax.faces.context.FacesContext;
 
public class JSFMessageUtil {
 public void sendInfoMessageToUser(String message) {
  FacesMessage facesMessage = createMessage(FacesMessage.SEVERITY_INFO, message);
  addMessageToJsfContext(facesMessage);
 }
 
 public void sendErrorMessageToUser(String message) {
  FacesMessage facesMessage = createMessage(FacesMessage.SEVERITY_WARN, message);
  addMessageToJsfContext(facesMessage);
 }
 
 private FacesMessage createMessage(Severity severity, String mensagemErro) {
  return new FacesMessage(severity, mensagemErro, mensagemErro);
 }
 
 private void addMessageToJsfContext(FacesMessage facesMessage) {
  FacesContext.getCurrentInstance().addMessage(null, facesMessage);
 }
}

Этот класс будет обрабатывать все сообщения JSF, которые будут отображаться пользователю. Этот класс поможет нашим ManagedBeans потерять связь между классами.

Также неплохо создать класс для обработки действий диалогов.

Файл настроек

В исходной папке «src» создайте следующие файлы:

« Log4.properties »

01
02
03
04
05
06
07
08
09
10
11
12
13
14
# Direct log messages to stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
 
# Root logger option
log4j.rootLogger=ERROR, stdout
 
# Hibernate logging options (INFO only shows startup messages)
log4j.logger.org.hibernate=ERROR
 
# Log JDBC bind parameter runtime arguments
#log4j.logger.org.hibernate.type=TRACE

« Messages.properties »

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#Actions
welcomeMessage=Hello! Show me the best soccer team logo ever
update=Update
create=Create
delete=Delete
cancel=Cancel
detail=Detail
logIn=Log In
add=Add
remove=Remove
ok=Ok
logOut= Log Out
javax.faces.component.UIInput.REQUIRED={0}: is empty. Please, provide some value
javax.faces.validator.LengthValidator.MINIMUM={1}: Length is less than allowable minimum of u2018u2019{0}u2019u2019
noRecords=No data to display
deleteRecord=Do you want do delete the record
 
#Login / Roles Validations
loginHello=Login to access secure pages
loginUserName=Username
loginPassword=Password
logout=Log Out
loginWelcomeMessage=Welcome
accessDeniedHeader=Wow, our ninja cat found you!
accessDeniedText=Sorry but you can not access that page. If you try again, that ninja cat gonna kick you harder! >= )
accessDeniedButton=You got-me, take me out. =/
 
#Person
person=Person
personPlural=Persons
personName=Name
personAge=Age
personDogs=These dogs belongs to
personAddDogTo=Add the selected Dog To
personRemoveDogFrom=Remove the selected Dog from
personEditDogs=Edit Dogs
 
#Dog
dog=Dog
dogPlural=Dogs
dogName=Name
dogAge=Age

Посмотрите на « lo4j.properties » в строке # log4j.logger.org.hibernate.type = TRACE . Если вы хотите увидеть созданный запрос в Hibernate, вам нужно отредактировать другие конфигурации файла от ERROR до DEBUG и удалить # из строки выше.

Вы сможете увидеть выполненный запрос Hibernate и его параметры.

xhtml Страницы, Facelets

Внутри папки WebContent вы найдете следующие файлы:

Давайте посмотрим, как применить Facelets к проекту. Создайте файлы ниже внутри папки «/ WebContent / pages / protected / templates /»:

« Left.xhtml »

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN'
 
 
<h:body>
 <ui:composition>
  <h:form>
   <p:commandButton styleClass='menuButton' icon='ui-icon-arrowstop-1-e' rendered='#{userMB.admin or userMB.defaultUser}' action='/pages/protected/defaultUser/defaultUserIndex.xhtml' value='#{bundle.personPlural}' ajax='false' immediate='true' />
   <br />
   <p:commandButton styleClass='menuButton' icon='ui-icon-arrowstop-1-e' rendered='#{userMB.admin}' action='/pages/protected/admin/adminIndex.xhtml' value='#{bundle.dogPlural}' ajax='false' immediate='true' />
   <br />
  </h:form>
 </ui:composition>
</h:body>
</html>

« Master.xhtml »

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?xml version='1.0' encoding='UTF-8' ?> 
<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN'
 
 
<h:head>
 <title>CrudJSF</title>
 <h:outputStylesheet library='css' name='main.css' />
 <meta http-equiv='Content-Type' content='text/html; charset=UTF-8' />
</h:head>
 
<h:body>
 <f:view contentType='text/html; charset=UTF-8' encoding='UTF-8' >
  <div id='divTop' style='vertical-align: middle;'>
   <ui:insert name='divTop'>
    <ui:include src='top.xhtml' />
   </ui:insert>
  </div>
 
  <div id='divLeft'>
   <ui:insert name='divLeft'>
    <ui:include src='left.xhtml' />
   </ui:insert>
  </div>
 
  <div id='divMain'>
   <p:growl id='messageGrowl' />
   <ui:insert name='divMain' />
  </div>
 
  <h:outputScript library='javascript' name='jscodes.js' />
 </f:view>
</h:body>
</html>

«Top.xhtml»

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN'
 
      xmlns:ui='http://java.sun.com/jsf/facelets'
      xmlns:h='http://java.sun.com/jsf/html'
      xmlns:p='http://primefaces.org/ui'>
   <h:body>  
     <ui:composition>
      <div id='topMessage'>
       <h1>
        <h:form>
         #{bundle.loginWelcomeMessage}: #{userMB.user.name} | <p:commandButton value='#{bundle.logOut}' action='#{userMB.logOut()}' ajax='false' style='font-size: 20px;' />
        </h:form>
       </h1>
      </div>
     </ui:composition>  
   </h:body>
</html>

Приведенный выше код будет основой для всех xhtml-страниц приложения. Очень важно применить шаблон Facelets для повторного использования кода xhtml. Ниже вы можете увидеть, как применять Facelets на странице xhtml, обратите внимание, что разработчику просто нужно перезаписать нужный раздел:

01
02
03
04
05
06
07
08
09
10
11
12
<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>
<h:body>
 <ui:composition template='/pages/protected/templates/master.xhtml'>
  <ui:define name='divMain'>
   #{bundle.welcomeMessage} :<br/>
   <h:graphicImage library='images' name='logoReal.png' />
  </ui:define>
 </ui:composition>
</h:body>
</html>

Обратите внимание, что только « divMain » был перезаписан, а остальные разделы остались прежними. Это самое большое преимущество Facelets, вам не нужно использовать все области сайта на каждой странице.

Продолжайте в третьей части .

Ссылка: полноценное веб-приложение с Tomcat JSF Primefaces JPA Hibernate от нашего партнера JCG Хеберта Коэльо в блоге uaiHebert .