В конце этого поста вы найдете исходный код для скачивания. Вы можете использовать его как хотите. Просто перейдите на последнюю страницу и выполните загрузку. \ о /
Если вы скачали код и ничего не поняли, в этом посте я объясню каждую деталь, найденную в коде. Просто прочитайте тему в этом посте, который вы хотите.
Я перечислю ниже технологии, которые я буду использовать в этом посте:
- JSF 2.0 Mojarra — с ManagedBeans в качестве RequestScope и SessionScope.
- Интернационализация сообщений — файл, который будет содержать все сообщения нашей системы; вам будет легче переводить страницы.
- CSS-файл по умолчанию, который будет импортирован как библиотека.
- EJB 3 — Наши DAO и Фасады будут @Stateless.
- Универсальный DAO — универсальный DAO, в котором будут использоваться действия CRUD для облегчения нашей жизни.
- JPA 2 — Для отображения наших классов в БД
- JAAS — для контроля входа и доступа пользователей к страницам.
- MVC — я буду использовать этот шаблон с небольшими изменениями.
- Postgres как база данных, но я покажу, как настроить приложение для MySQL.
Я не буду использовать TDD-JUnit для тестирования наших View / Model / Classes, но по следующей ссылке вы можете увидеть методику использования JUnit для тестирования ваших ManagedBeans: JUnit с HSQLDB, JPA и Hibernate .
Инструменты, которые мы будем использовать:
- Eclipse Indigo — http://www.eclipse.org/downloads/packages/eclipse-ide-java-ee-developers/indigor
- Я буду использовать JBoss 7 (некоторые читатели этого блога просили меня об этом) — http://download.jboss.org/jbossas/7.0/jboss-as-7.0.2.Final/jboss-as-7.0.2.Final .zip версия: JBoss 7 Все (НЕ сертифицирован Java EE6) .
- Инструменты Indigo JBoss (версия Milestone) — https://www.jboss.org/tools/download/dev.html, если вы не знаете, как установить Инструменты JBoss, в этом посте я покажу, как ( Проверка регистрации пользователя с помощью JAAS). и JSF ). Помните, что в этом посте я покажу, как установить другую версию Eclipse, но разница заключается в URL; используйте этот URL вместо URL, использованного в ссылке выше: http://download.jboss.org/jbosstools/updates/development/indigo/
- Я буду использовать базу данных Postgres, но вы можете использовать любую базу данных; Вам потребуется только загрузить базу данных и драйвер JDBC. Здесь вы можете загрузить последнюю версию Postgres JDBC: http://jdbc.postgresql.org/download.html ; последняя версия на данный момент — 4: http://jdbc.postgresql.org/download/postgresql-9.1-901.jdbc4.jar .
Этот пост будет иметь несколько страниц; Эта первая страница просто для того, чтобы показать технические детали сегодняшнего поста.
Я не буду кодировать интерфейс моей модели / DAO, просто чтобы сэкономить место. Помните, что вы всегда должны кодировать интерфейсы ( Design Pattern — Strategy ) .
Прежде чем продолжить, убедитесь, что вы установили JBoss Tools и JBoss 7 именно в таком порядке.
Бизнес модель
Давайте создадим проект EJB, который будет поддерживать наш системный бизнес.
Нажмите на кнопку « Готово ».
Давайте создадим классы User и Dog, которые будут внутри пакета «com». Это будет иметь код ниже:
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
|
package com.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.NamedQuery; import javax.persistence.Table; @Entity @Table (name = 'USERS' ) @NamedQuery (name= 'User.findUserByEmail' , query= 'select u from User u where u.email = :email' ) public class User { public static final String FIND_BY_EMAIL = 'User.findUserByEmail' ; @Id @GeneratedValue (strategy=GenerationType.AUTO) private int id; @Column (unique = true ) private String email; private String password; private String name; private String role; public int getId() { return id; } public void setId( int id) { this .id = id; } 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 getName() { return name; } public void setName(String name) { this .name = name; } public String getRole() { return role; } public void setRole(String role) { this .role = role; } @Override public int hashCode() { return getId(); } @Override public boolean equals(Object obj) { if (obj instanceof User){ User user = (User) obj; return user.getEmail().equals(getEmail()); } return false ; } } |
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
|
package com.model; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table (name = 'DOGS' ) public class Dog { @Id @GeneratedValue (strategy = GenerationType.AUTO) private int id; private String name; private double weight; 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; } public double getWeight() { return weight; } public void setWeight( double weight) { this .weight = weight; } @Override public int hashCode() { return getId(); } @Override public boolean equals(Object obj) { if (obj instanceof Dog){ Dog dog = (Dog) obj; return dog.getId() == getId(); } return false ; } } |
О коде выше:
- У класса User есть поле с именем «role», в котором будет храниться уровень роли пользователя. Я создал одно поле, и мы оставим все данные в одной таблице, чтобы их было легче понять. Если вы хотите получить более подробную информацию о JAAS, вы можете написать в этом посте: Проверка входа пользователя с помощью JAAS и JSF .
- Я позволю JPA обрабатывать таблицы Id поколений. Если вы хотите изменить способ создания идентификатора, вы можете проверить эти сообщения, чтобы увидеть, как это сделать: JPA SequenceGenerator , JPA TableGenerator — Простой ключ Primay .
- Электронная почта будет уникальной; это будет идентификатор входа в систему.
- Обратите внимание, что класс, который должен быть объявлен как Entity, нуждается только в следующих аннотациях: «@Entity» и «@Id». Класс не должен реализовывать интерфейс Serializable.
Бизнес — ДАО
Я буду использовать общий DAO для основных операций CRUD, а другие два DAO: один для пользователя и другой для собаки. Понять его использование будет очень легко:
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
|
package com.dao; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.Query; import javax.persistence.criteria.CriteriaQuery; public abstract class GenericDAO<T> { private final static String UNIT_NAME = 'CrudPU' ; @PersistenceContext (unitName = UNIT_NAME) private EntityManager em; private Class<T> entityClass; public GenericDAO(Class<T> entityClass) { this .entityClass = entityClass; } public void save(T entity) { em.persist(entity); } public void delete(T entity) { T entityToBeRemoved = em.merge(entity); em.remove(entityToBeRemoved); } public T update(T entity) { return em.merge(entity); } public T find( int entityID) { return em.find(entityClass, entityID); } // Using the unchecked because JPA does not have a // em.getCriteriaBuilder().createQuery()<T> method @SuppressWarnings ({ 'unchecked' , 'rawtypes' }) public List<T> findAll() { CriteriaQuery cq = em.getCriteriaBuilder().createQuery(); cq.select(cq.from(entityClass)); return em.createQuery(cq).getResultList(); } // Using the unchecked because JPA does not have a // ery.getSingleResult()<T> method @SuppressWarnings ( 'unchecked' ) protected T findOneResult(String namedQuery, Map<String, Object> parameters) { T result = null ; try { Query query = em.createNamedQuery(namedQuery); // Method that will populate parameters if they are passed not null and empty if (parameters != null && !parameters.isEmpty()) { populateQueryParameters(query, parameters); } result = (T) query.getSingleResult(); } catch (Exception e) { System.out.println( 'Error while running query: ' + e.getMessage()); e.printStackTrace(); } return result; } private void populateQueryParameters(Query query, Map<String, Object> parameters) { for (Entry<String, Object> entry : parameters.entrySet()) { query.setParameter(entry.getKey(), entry.getValue()); } } } |
01
02
03
04
05
06
07
08
09
10
11
12
13
|
package com.dao; import javax.ejb.Stateless; import com.model.Dog; @Stateless public class DogDAO extends GenericDAO<Dog> { public DogDAO() { super (Dog. class ); } } |
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
package com.dao; import java.util.HashMap; import java.util.Map; import javax.ejb.Stateless; import com.model.User; @Stateless public class UserDAO extends GenericDAO<User> { public UserDAO() { super (User. class ); } public User findUserByEmail(String email){ Map<String, Object> parameters = new HashMap<String, Object>(); parameters.put( 'email' , email); return super .findOneResult(User.FIND_BY_EMAIL, parameters); } } |
О коде выше:
- Я скрыл некоторые предупреждения, потому что код JPA еще не «понимает» дженерики.
- Метод findOneResult имеет защищенный доступ только для предотвращения внешнего доступа из других классов; этот метод требует логики для заполнения параметров, как мы видим в UserDAO.
- Класс GenericDAO имеет полные методы CRUD плюс метод, который возвращает один объект с заданным NamedQuery.
- У класса UserDAO есть метод (findUserByEmail), который принадлежит только классу; но он имеет все методы CRUD по наследству. С этим шаблоном DAO мы получили более гибкий код.
- В DogDAO нет никакого метода, а есть только методы CRUD; Вы можете реализовать любой метод в классе без проблем.
- Вместо того, чтобы использовать метод для «сохранения» и другой для «обновления» ваших объектов, вы можете использовать один метод «entityManager.merge ()». У вас будет тот же результат, но вам нужно будет обратить внимание на параметры каскада.
- Я не использовал интерфейсы, потому что EJB 3.1 позволяет нам иметь Локальные Сессионные Компоненты без Состояния без интерфейса. Если вы используете более старую версию EJB, вам необходимо реализовать интерфейс (как реализовать EJB с интерфейсами, мы увидим в этом посте на странице «Фасады»). Я не буду разрабатывать интерфейсы моей DAO / Model только для экономии места в этом посте. Помните: «Всегда программируйте на интерфейс» ( Design Pattern — Strategy ).
- Если вы используете JBoss 4.2, вы можете использовать аннотации org.jboss.annotation.ejb.LocalBinding или org.jboss.annotation.ejb.RemoteBinding; в этой аннотации вы можете написать имя, которое будет отображено и передано EJB.
Бизнес — Фасады
Я создам Фасады, которые станут «мостом» между View и DAO. На Фасаде мы оставим все бизнес-правила, оставив в DAO только функции / транзакции «базы данных», например, CRUD и запросы.
Давайте посмотрим, какими будут наши классы UserFacade и DogFacade:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
package com.facade; import java.util.List; import javax.ejb.Local; import com.model.Dog; @Local public interface DogFacade { public abstract void save(Dog dog); public abstract Dog update(Dog dog); public abstract void delete(Dog dog); public abstract Dog find( int entityID); public abstract List<Dog> findAll(); } |
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
|
package com.facade; import java.util.List; import javax.ejb.EJB; import javax.ejb.Stateless; import com.dao.DogDAO; import com.model.Dog; @Stateless public class DogFacadeImp implements DogFacade { @EJB private DogDAO dogDAO; @Override public void save(Dog dog) { isDogWithAllData(dog); dogDAO.save(dog); } @Override public Dog update(Dog dog) { isDogWithAllData(dog); return dogDAO.update(dog); } @Override public void delete(Dog dog) { dogDAO.delete(dog); } @Override public Dog find( int entityID) { return dogDAO.find(entityID); } @Override public List<Dog> findAll() { return dogDAO.findAll(); } private void isDogWithAllData(Dog dog){ boolean hasError = false ; if (dog == null ){ hasError = true ; } if (dog.getName() == null || '' .equals(dog.getName().trim())){ hasError = true ; } if (dog.getWeight() <= 0 ){ hasError = true ; } if (hasError){ throw new IllegalArgumentException( 'The dog is missing data. Check the name and weight, they should have value.' ); } } } |
01
02
03
04
05
06
07
08
09
10
|
package com.facade; import javax.ejb.Local; import com.model.User; @Local public interface UserFacade { public User findUserByEmail(String email); } |
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
package com.facade; import javax.ejb.EJB; import javax.ejb.Stateless; import com.dao.UserDAO; import com.model.User; @Stateless public class UserFacadeImp implements UserFacade { @EJB private UserDAO userDAO; public User findUserByEmail(String email) { return userDAO.findUserByEmail(email); } } |
О коде выше:
- Класс DogFacadeImp выполняет всю работу по защите DogDAO от представления. Он может иметь бизнес-правила и избегать доступа к базе данных, если некоторые данные отсутствуют или любое другое бизнес-правило нарушено.
- У UserFacade есть только один метод, потому что в этом посте у нас не будет User crud; bean-компонент будет искать пользователя только в том случае, если представление не находит его в HttpSession.
- Если вы используете JBoss 4.2, вы можете использовать аннотации org.jboss.annotation.ejb.LocalBinding или org.jboss.annotation.ejb.RemoteBinding; в этой аннотации вы можете написать имя, которое будет отображено и передано EJB.
- Я делаю проверку в DogFacadeImp, чтобы убедиться, что у собаки есть только действительные данные. Помните, что каждый может отправить вам неверные данные, и проверка вашего представления может работать не так, как вы ожидаете. Данные вашего заявления очень важны, и двойная проверка всегда стоит.
Интерфейсы аннотированы @Local, но я помню, что эта аннотация не является обязательной. Если вы не напишите аннотацию @Local, ваш сервер будет считать, что ваш EJB является локальным по умолчанию.
Бизнес — Источник данных (по модулям)
Нам также нужно настроить наш источник данных.
Сначала я попытался создать источник данных, следуя этому руководству http://community.jboss.org/wiki/JBossAS7-DatasourceConfigurationForPostgresql (я следую учебным пособиям, как и любой другой человек), но после развертывания с использованием нескольких ошибок началось EJB и JBoss не смогли найти банку Postgres.
Если вы используете JBoss 6 или любую другую версию, вам не нужно создавать модуль; просто поместите файл в папку «default / lib». Если у вас есть какие-либо сомнения по поводу настройки источника данных, я покажу, как это сделать, в JBoss 6 или другой версии под ним, здесь: Проверка входа пользователя с помощью JAAS и JSF .
Давайте создадим модуль Postgres. Внутри вашего JBoss 7 создайте каталог: « YOUR_JBOSS / modules / org / postgresql / main ». Скопируйте банку в созданный каталог и создайте файл с именем «module.xml»; скопируйте код ниже внутри файла «module.xml»:
1
2
3
4
5
6
7
|
<? xml version = '1.0' encoding = 'UTF-8' ?> < module xmlns = 'urn:jboss:module:1.0' name = 'org.postgresql' > < resources > < resource-root path = 'postgresql-9.1-901.jdbc4.jar' /> </ resources > < dependencies >< module name = 'javax.api' /></ dependencies > </ module > |
Обратите внимание, что внутри файла «module.xml» написано имя файла jar, имя должно совпадать с именем файла jar Postgres.
Для создания модуля MySQL создайте следующую папку « YOUR_JBOSS / modules / com / mysql / main » . Скопируйте банку в созданный каталог и создайте файл с именем «module.xml»; скопируйте код ниже внутри файла «module.xml»:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
<? xml version = '1.0' encoding = 'UTF-8' ?> <!-- ~ JBoss copyrights --> < module xmlns = 'urn:jboss:module:1.0' name = 'com.mysql' > < resources > < resource-root path = 'mysql-connector-java-5.1.15.jar' /> </ resources > < dependencies > < module name = 'javax.api' /> </ dependencies > </ module > |
Давайте отредактируем файл « YOUR_JBOSS / standalone / configuration / standalone.xml ». Внутри ключа «<источники данных>» добавьте код ниже:
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
|
< datasources > <!-- Add this config: begin --> < datasource jndi-name = 'CrudDS' pool-name = 'CrudDS_Pool' enabled = 'true' jta = 'true' use-java-context = 'true' use-ccm = 'true' > < connection-url >jdbc:postgresql://localhost:5432/CrudDB</ connection-url > < driver-class >org.postgresql.Driver</ driver-class > < driver >postgresql-jdbc4</ driver > < pool > < min-pool-size >2</ min-pool-size > < max-pool-size >20</ max-pool-size > < prefill >true</ prefill > < use-strict-min >false</ use-strict-min > < flush-strategy >FailingConnectionOnly</ flush-strategy > </ pool > < security > < user-name >postgres</ user-name > < password >postgres</ password > </ security > < validation > < check-valid-connection-sql >SELECT 1</ check-valid-connection-sql > < validate-on-match >false</ validate-on-match > < background-validation >false</ background-validation > < use-fast-fail >false</ use-fast-fail > </ validation > </ datasource > <!-- Add this config: end --> < drivers > <!-- Add this config: begin --> < driver name = 'postgresql-jdbc4' module = 'org.postgresql' /> <!-- Add this config: end --> </ drivers > |
Чтобы настроить источник данных с MySQL, посмотрите здесь:
http://community.jboss.org/wiki/DataSourceConfigurationInAS7
Бизнес — XML-конфигурации
Давайте посмотрим, каким будет наш файл persistence.xml (этот файл должен находиться внутри папки src / META-INF):
01
02
03
04
05
06
07
08
09
10
|
<? xml version = '1.0' encoding = 'UTF-8' ?> < persistence version = '1.0' xmlns = 'http://java.sun.com/xml/ns/persistence' xmlns:xsi = 'http://www.w3.org/2001/XMLSchema-instance' xsi:schemaLocation = 'http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd' > < persistence-unit name = 'CrudPU' transaction-type = 'JTA' > < provider >org.hibernate.ejb.HibernatePersistence</ provider > < jta-data-source >java:/CrudDS</ jta-data-source > < properties > < property name = 'hibernate.hbm2ddl.auto' value = 'update' /> </ properties > </ persistence-unit > </ persistence > |
Мы получили очень простой код; он просто указывает на источник данных и возможность генерировать все таблицы базы данных с помощью «update».
Добавьте проект EJB в JBoss.
Создайте базу данных в Postgres и запустите JBoss; после того, как вы запустили его, JPA создаст таблицы.
Проверьте изображение ниже, чтобы увидеть таблицы / последовательности, которые JPA создал для нас.
Создайте файл с именем «jboss-web.xml» в папке WEB-INF и напишите в нем приведенный ниже код:
1
2
3
4
5
6
7
8
9
|
<? xml version = '1.0' encoding = 'UTF-8' ?> < jboss-web > <!-- URL to access the web module --> < context-root >CrudJSF</ context-root > <!-- Realm that will be used --> < security-domain >java:/jaas/CrudJSFRealm</ security-domain > </ jboss-web > |
В приведенном выше файле мы настроили Царство, которое будет использовать наше приложение. Давайте вставим пользователей в базу данных, которая будет выполнять вход с помощью JAAS (если вы хотите увидеть более подробную информацию о JAAS, вы можете увидеть это здесь: Проверка входа пользователя с помощью JAAS и JSF ). Я не буду вдаваться в подробности JAAS, потому что вы можете найти каждый шаг, описанный в предыдущей ссылке.
Смотрите на изображении ниже данные, которые я вставил вручную в базу данных (вы должны сделать то же самое):
Перейдите ко второй части урока.
Ссылка: полное веб-приложение JSF EJB JPA JAAS от нашего партнера JCG Хеберта Коэльо в блоге uaiHebert .