设为首页 收藏本站
查看: 285|回复: 0

[经验分享] Full Web Application Tomcat JSF Primefaces JPA Hibernate – Part 1

[复制链接]

尚未签到

发表于 2017-2-7 09:06:13 | 显示全部楼层 |阅读模式
Full Web Application Tomcat JSF Primefaces JPA Hibernate – Part 1
We created this post that will show how to create a full web application using the following tools: Tomcat7, JSF2 (Facelets and Libraries) with Primefaces (with AutoComplete), JPA / Hibernate (with a relationship NxN), Login with Filter.If you want to see a full web application with JSF + EJB + JPA + JBoss 7 click here.
To run the code of this post you will need the artifacts bellow (all jar file you will find in the last page of this post):
■Eclipse Indigo
■Tomcat7
■Mojarra
■Primefaces
■Hibernate
■Driver Postgres
You can use the any database you want, you will need the specific driver of the database and to edit the URL connection at the persistence.xml.
At the end of this post you will find the source code with all needed libraries to download.
What you will see today in this post:
■Entity classes of the model. A relationship NxN (@ManyToMany), NamedQueries with JoinFetch, Enum as attribute.
■Generic DAO, application transaction methods, generic methods to populate query parameters.
■Façades an transactions, using the method findReferenceOnly, care using the entityManager.merge() method.
■Filters.
■ManagedBeans. How to inject a ManagedBean inside another ManagedBean, observations about @ViewScoped.
■JSFMessageUtil.
■Configurations file: log4j.properties, messages.properties.
■xhtml pages, Facelets.
■Primefaces AutoComplete, JSF Converter with “forClass”.
■Easiest way to use CSS/javascript/images with JSF.
■“web.xml” configurations.
■Increasing the security of your application.
■Running the application.
The application that you will find in here will have a Dog and Person CRUD (Create, Read, Update, and Delete); only the ADMIN will have access to the Dog CRUD. Before you run this application you should create a database named “JSFCrudDB”.
Model classes
The following classes are the model classes and they should be in the “com.model” package:

package com.model;
public enum Role {
ADMIN, USER;
}



package com.model;
import java.io.Serializable;
import javax.persistence.*;
@Entity
@Table(name = 'USERS')
@NamedQuery(name = 'User.findUserByEmail', query = 'select u from User u where u.email = :email')
public class User implements Serializable {
private static final long serialVersionUID = 1L;
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;
@Enumerated(EnumType.STRING)
private Role role;
// get and set
public boolean isAdmin() {
return Role.ADMIN.equals(role);
}
public boolean isUser() {
return Role.USER.equals(role);
}
@Override
public int hashCode() {
return getId();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof User) {
User user = (User) obj;
return user.getId() == id;
}
return false;
}
}



package com.model;
import java.io.Serializable;
import java.util.List;
import javax.persistence.*;
@Entity
@NamedQuery(name = 'Person.findUserByIdWithDogs', query = 'select p from Person p left join fetch p.dogs where p.id = :personId')
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
public static final String FIND_USER_BY_ID_WITH_DOGS = 'Person.findUserByIdWithDogs';
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private int age;
private String name;
@ManyToMany
private List<Dog> dogs;
// get and set
@Override
public int hashCode() {
return id;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Person) {
Person person = (Person) obj;
return person.getId() == id;
}
return false;
}
}



package com.model;
import java.io.Serializable;
import java.util.List;
import javax.persistence.*;
@Entity
public class Dog implements Serializable{
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private int age;
private String name;
@ManyToMany(mappedBy='dogs')
private List<Person> persons;
// get and set
@Override
public int hashCode() {
return id;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Dog) {
Dog dog = (Dog) obj;
return dog.getId() == id;
}
return false;
}
@Override
public String toString() {
return name;
}
}


About the above code:
■The Person class has the namedQuery “Person.findUserByIdWithDogs”. This query will eagerly load the Dog list of the Person class. If we try to access the Dog list without a transaction and this query a LazyInitializationException will happen. Other solution to this situation would be the OpenSessionInView pattern; this pattern may generate the N+1 queries effect. If you want to know more about this exception click here.
■It is very easy to map an Enum with JPA/Hibernate. As you can see there is an option with the @Enumerated annotation that will set the database table field as String. If you want to read more about this annotation and see how to map an Enum as ORDINAL click here.
■Always override the equals/hashCode methods. These methods are often invoked by several frameworks. A lot of Primefaces “bugs” were solved by implementing these methods.
You will need to create the “persistence.xml” file inside the “src/META-INF” folder:

<?xml version='1.0' encoding='UTF-8'?>
<persistence version='2.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_2_0.xsd'>
<persistence-unit name='JSFCrudPU' transaction-type='RESOURCE_LOCAL'>
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<properties>
<property name='javax.persistence.jdbc.driver' value='org.postgresql.Driver' />
<property name='javax.persistence.jdbc.url' value='jdbc:postgresql://localhost:5432/JSFCrudDB' />
<property name='javax.persistence.jdbc.user' value='postgres' />
<property name='javax.persistence.jdbc.password' value='postgres' />
<property name='hibernate.dialect' value='org.hibernate.dialect.PostgreSQLDialect' />
<property name='hibernate.connection.shutdown' value='true' />
<property name='hibernate.hbm2ddl.auto' value='update' />
<property name='hibernate.show_sql' value='false' />
<property name='hibernate.format_sql' value='false'/>
</properties>
</persistence-unit>
</persistence>


About the above code:
■The “hibernate.hbm2ddl.auto” property with the value “update” indicates to the JPA to update, whenever necessary, the database according the model classes. It is not a good practice to leave this option as “update” or with any other value that may update the production/customer database. It is always good to use the “validate” option in the production environment and to create and execute the sql script manually.
■The hibernate.show_sql and the hibernate.format_sql properties are used to display the query in the console. In the file log4j.properties (page 08) there is an option that will allow displaying the query parameters values.
DAO
In the package “com.dao” you will need to create the following classes:

package com.dao;
import java.io.Serializable;
import java.util.*;
import java.util.Map.*;
import javax.persistence.*;
import javax.persistence.criteria.CriteriaQuery;
abstract class GenericDAO<T> implements Serializable {
private static final long serialVersionUID = 1L;
private static final EntityManagerFactory emf = Persistence.createEntityManagerFactory('JSFCrudPU');
private EntityManager em;
private Class<T> entityClass;
public void beginTransaction() {
em = emf.createEntityManager();
em.getTransaction().begin();
}
public void commit() {
em.getTransaction().commit();
}
public void rollback() {
em.getTransaction().rollback();
}
public void closeTransaction() {
em.close();
}
public void commitAndCloseTransaction() {
commit();
closeTransaction();
}
public void flush() {
em.flush();
}
public void joinTransaction() {
em = emf.createEntityManager();
em.joinTransaction();
}
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);
}
public T findReferenceOnly(int entityID) {
return em.getReference(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
// query.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 (NoResultException e) {
System.out.println('No result found for named query: ' + namedQuery);
} 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());
}
}
}



package com.dao;
import java.util.*;
import com.model.Person;
public class PersonDAO extends GenericDAO<Person> {
private static final long serialVersionUID = 1L;
public PersonDAO() {
super(Person.class);
}
public Person findPersonWithAllDogs(int personId) {
Map<String, Object> parameters = new HashMap<String, Object>();
parameters.put('personId', personId);
return super.findOneResult(Person.FIND_USER_BY_ID_WITH_DOGS, parameters);
}
}



package com.dao;
import java.util.*;
import com.model.User;
public class UserDAO extends GenericDAO<User> {
private static final long serialVersionUID = 1L;
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);
}
}



package com.dao;
import com.model.Dog;
public class DogDAO extends GenericDAO<Dog> {
private static final long serialVersionUID = 1L;
public DogDAO() {
super(Dog.class);
}
}


About the above code:
■The DAO classes has methods to allow the Façades to control the transaction. This pattern is being applied to avoid the OpenSessionInView pattern; the OpenSessionInView pattern may cause the N+1 effect of queries. With the transaction controlled by the developer it is easier to understand the transaction flow, but we have a more verbose code. If you want to know more about the OpenSessionInView and the N+1 effect you can click here.
■You will find a method named “populateQueryParameters” in the GenericDAO class. This method will populate dynamically all the parameters of a query if needed. The classes PersonDAO and UserDAO have a sample code of how to invoke this method.
Façades
In the package “com.facade” you will need to create the classes bellow:

package com.facade;
import com.dao.UserDAO;
import com.model.User;
public class UserFacade {
private UserDAO userDAO = new UserDAO();
public User isValidLogin(String email, String password) {
userDAO.beginTransaction();
User user = userDAO.findUserByEmail(email);
if (user == null || !user.getPassword().equals(password)) {
return null;
}
return user;
}
}



package com.facade;
import java.io.Serializable;
import java.util.List;
import com.dao.DogDAO;
import com.model.Dog;
public class DogFacade implements Serializable{
private static final long serialVersionUID = 1L;
private DogDAO dogDAO = new DogDAO();
public void createDog(Dog dog) {
dogDAO.beginTransaction();
dogDAO.save(dog);
dogDAO.commitAndCloseTransaction();
}
public void updateDog(Dog dog) {
dogDAO.beginTransaction();
Dog persistedDog = dogDAO.find(dog.getId());
persistedDog.setAge(dog.getAge());
persistedDog.setName(dog.getName());
dogDAO.update(persistedDog);
dogDAO.commitAndCloseTransaction();
}
public Dog findDog(int dogId) {
dogDAO.beginTransaction();
Dog dog = dogDAO.find(dogId);
dogDAO.closeTransaction();
return dog;
}
public List<Dog> listAll() {
dogDAO.beginTransaction();
List<Dog> result = dogDAO.findAll();
dogDAO.closeTransaction();
return result;
}
public void deleteDog(Dog dog) {
dogDAO.beginTransaction();
Dog persistedDog = dogDAO.findReferenceOnly(dog.getId());
dogDAO.delete(persistedDog);
dogDAO.commitAndCloseTransaction();
}
}



package com.facade;
import java.io.Serializable;
import java.util.List;
import com.dao.DogDAO;
import com.dao.PersonDAO;
import com.model.Dog;
import com.model.Person;
public class PersonFacade implements Serializable {
private static final long serialVersionUID = 1L;
private PersonDAO personDAO = new PersonDAO();
private DogDAO dogDAO = new DogDAO();
public void createPerson(Person person) {
personDAO.beginTransaction();
personDAO.save(person);
personDAO.commitAndCloseTransaction();
}
public void updatePerson(Person person) {
personDAO.beginTransaction();
Person persistedPerson = personDAO.find(person.getId());
persistedPerson.setName(person.getName());
persistedPerson.setAge(person.getAge());
personDAO.commitAndCloseTransaction();
}
public void deletePerson(Person person){
personDAO.beginTransaction();
Person persistedPersonWithIdOnly = personDAO.findReferenceOnly(person.getId());
personDAO.delete(persistedPersonWithIdOnly);
personDAO.commitAndCloseTransaction();
}
public Person findPerson(int personId) {
personDAO.beginTransaction();
Person person = personDAO.find(personId);
personDAO.closeTransaction();
return person;
}
public List<Person> listAll() {
personDAO.beginTransaction();
List<Person> result = personDAO.findAll();
personDAO.closeTransaction();
return result;
}
public Person findPersonWithAllDogs(int personId) {
personDAO.beginTransaction();
Person person = personDAO.findPersonWithAllDogs(personId);
personDAO.closeTransaction();
return person;
}
public void addDogToPerson(int dogId, int personId) {
personDAO.beginTransaction();
dogDAO.joinTransaction();
Dog dog = dogDAO.find(dogId);
Person person = personDAO.find(personId);
person.getDogs().add(dog);
dog.getPerson().add(person);
personDAO.commitAndCloseTransaction();
}
public void removeDogFromPerson(int dogId, int personId) {
personDAO.beginTransaction();
dogDAO.joinTransaction();
Dog dog = dogDAO.find(dogId);
Person person = personDAO.find(personId);
person.getDogs().remove(dog);
dog.getPerson().remove(person);
personDAO.commitAndCloseTransaction();
}
}


About the above code:
■Every transaction is controlled by the developer. The developer has the responsibility of never forgetting an opened transaction.
■The class PersonFacade uses a method named “findReferenceOnly“. This method has a better performance than the “find” method with operations that involves only operations where only the entity id is needed. A query will be triggered in the database to get only the ID of that entity. If any other attribute is invoked a new query will be fired in the database to bring that information. According the book “Pro JPA 2: Mastering the Java™ Persistence API” this method could also be used to edit items in a list. e.g. In the method Person.addDoAgToPerson the “find” method could be replace for the “findReferenceOnly“
■Both methods ” Person.addDogToPerson” and “ Person.removeDogFromPerson” invoke the method “joinTransaction” in the DogDAO class. The EntityManager in the DogDAO will use the same transaction started at the PersonDAO.
Cares about the EntityManager.merge()

It would be easier just invoke the method like this: “entityManager.merge(dog)” instead of querying for the persisted object and updating the data. If you invoke the merge method in a file received from the view you could damage the integrity of your database data. The developer must take care with this merge method; if a lazy relationship is not loaded and this relationship is null in the merged entity the JPA/Hibernate will erase this relationship in the database. e.g. If the developer invokes the method entityManager.merge(dog) and dog.getPersons() == null the JPA/Hibernate will delete all relationship between them in the database.
The best practice is to find the object in the database than edit it.
In this project the object were loaded from the database and it is in the detached state; for this specific situation where you find the object detached and the lazy relationship is not null the JPA will not erase the database data relationship. If the object were manually created and the relationship has the value List<Person> == null the JPA would have deleted the database data.
Filter
In the “com.filter” package you will need to create the classes bellow:

package com.filter;
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
public class AbstractFilter {
public AbstractFilter() {
super();
}
protected void doLogin(ServletRequest request, ServletResponse response, HttpServletRequest req) throws ServletException, IOException {
RequestDispatcher rd = req.getRequestDispatcher('/pages/public/login.xhtml');
rd.forward(request, response);
}
protected void accessDenied(ServletRequest request, ServletResponse response, HttpServletRequest req) throws ServletException, IOException {
RequestDispatcher rd = req.getRequestDispatcher('/pages/public/accessDenied.xhtml');
rd.forward(request, response);
}
}



package com.filter;
import java.io.IOException;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import com.model.User;
/**
* Servlet Filter implementation class UserCheckFilter
*/
public class LoginCheckFilter extends AbstractFilter implements Filter {
private static List<String> allowedURIs;
/**
* @see Filter#init(FilterConfig)
*/
public void init(FilterConfig fConfig) throws ServletException {
if(allowedURIs == null){
allowedURIs = new ArrayList<String>();
allowedURIs.add(fConfig.getInitParameter('loginActionURI'));
allowedURIs.add('/JSFCrudApp/javax.faces.resource/main.css.xhtml');
allowedURIs.add('/JSFCrudApp/javax.faces.resource/theme.css.xhtml');
allowedURIs.add('/JSFCrudApp/javax.faces.resource/primefaces.js.xhtml');
allowedURIs.add('/JSFCrudApp/javax.faces.resource/primefaces.css.xhtml');
allowedURIs.add('/JSFCrudApp/javax.faces.resource/jquery/jquery.js.xhtml');
allowedURIs.add('/JSFCrudApp/javax.faces.resource/messages/messages.png.xhtml');
allowedURIs.add('/JSFCrudApp/javax.faces.resource/images/ui-icons_2e83ff_256x240.png.xhtml');
allowedURIs.add('/JSFCrudApp/javax.faces.resource/images/ui-icons_38667f_256x240.png.xhtml');
}
}
/**
* @see Filter#destroy()
*/
public void destroy() {
}
/**
* @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
*/
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpSession session = req.getSession();
if (session.isNew()) {
doLogin(request, response, req);
return;
}
User user = (User) session.getAttribute('user');
if (user == null && !allowedURIs.contains(req.getRequestURI())) {
System.out.println(req.getRequestURI());
doLogin(request, response, req);
return;
}
chain.doFilter(request, response);
}
}



package com.filter;
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import com.model.User;
public class AdminPagesFilter extends AbstractFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
User user = (User) req.getSession(true).getAttribute('user');
if (!user.isAdmin()) {
accessDenied(request, response, req);
return;
}
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig arg0) throws ServletException {
}
}



package com.filter;
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import com.model.User;
public class DefaultUserPagesFilter extends AbstractFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
User user = (User) req.getSession(true).getAttribute('user');
if(!user.isUser() && !user.isAdmin()){
accessDenied(request, response, req);
return;
}
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig arg0) throws ServletException {
}
}


About the above code:
■The classes that implements the Filter interface are responsible for the Authorization and Authentication of the user. At the page 13 will have more information about why do we need to check if the user is logged in and its role at every request.
■The method “fConfig.getInitParameter(“loginActionURI”);” that is invoked inside the class LoginCheckFilter will load a property from the “web.xml” file. The “loginActionURI” property has a value to indicate the login page to the Filter; the Filter will use this property to allow every user (logged or not) to access that specific page (you will see the property value in the page 12). This property value is added to a list of URLs that are allowed to be called without the need of a logged user. It is a good practice to have this list of allowed URLs in the “web.xml” file, but for commodity only one were added in the “web.xml” in this post to display how to do it.
■Each filter watches over a directory that an user may access. Other approach would be to have a file with all allowed URLs and its allowed roles, e.g. “JSFCrud/protected/manager;admin-manager”.
from:http://www.javacodegeeks.com/2012/07/full-web-application-tomcat-jsf.html

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.iyunv.com/thread-338569-1-1.html 上篇帖子: IntelliJ IDEA 14.x 与 Tomcat 集成,创建并运行Java Web项目 下篇帖子: Tomcat 6.0 + Eclipse_JEE_Galileo 配置一个热部署的工程
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表