写之前的感受
刘岩
先说好的,文章后再说缺点。
JSF+Spring+JPA以我个人看来,应该说是Struts2+Spring+Hibernate的替代解决方案。
引入JPA去取代或者说包装或者说是升级Hibernate是为了符合JAVA EE的规范,达到ORM统一的结果。下次项目用EJB也好、用TOPLINK也好、用ibatis也罢,我们的开发人员代价会很小。在目前很多项目来说,引入此规范,能更好的代码复用,持久层和控制层相当于进一步的解耦合了,你只需要开启项目之前好好地建立领域模型。
那么用JSF取代Struts2或者更早一点的Struts1又是为何!答案依然是,它是规范。是符合某些标准的规范。不是说Struts2不好,这个因项目而定才行。我个人还是比较推崇apache的Struts2的,当然了,apache的JSF实现和JSF组件也是相当地杠杠的!!!!JSF更贴近事件比较多的web前端处理机制。想想我们一般要接收和处理页面的各种事件是用什么:js函数+ajax处理是吧(Flex我没有接触过,不知道它是怎么样的处理前后台交互的)。JSF可以采用事件监听机制,控件(JSF里面叫做控件)被触发了某些时间,直接优雅的和POJO进行了交互,配合ajax4jsf,处理大量的控件的事件是很简洁方便的。
言归正传,开始。。。
1. 首先利用IDE建立一个Web项目
2. 加入相应的jar包,注意:所有的开源框架都是加入jar包的时候最麻烦,尤其加入了Spring,搞不好就他妈的启动服务的时候报错。我已经将相应需要的jar包放到了csdn上面共享了,网址是http://download.csdn.net/source/2416493。大家可以去下载。
3. web项目加入JSF支持
修改web.xml,加入如下内容
<listener> <listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class> listener>
<context-param> <param-name>javax.faces.CONFIG_FILESparam-name> <param-value>/WEB-INF/faces-config.xmlparam-value> context-param>
<context-param> <param-name>contextConfigLocationparam-name> <param-value>/WEB-INF/classes/applicationContext.xmlparam-value> context-param>
<context-param> <param-name>javax.faces.STATE_SAVING_METHODparam-name> <param-value>clientparam-value> context-param>
加入facelets 框架支持--> <context-param> <param-name>facelets.LIBRARIESparam-name> <param-value> /WEB-INF/facelets/tags/arcmind.taglib.xml;/WEB-INF/tomahawk.taglib.xml param-value> context-param>
<context-param> <description>指定facelets支持的后缀description> <param-name>javax.faces.DEFAULT_SUFFIXparam-name> <param-value>.xhtmlparam-value> context-param>
<filter> <display-name>Ajax4jsf Filterdisplay-name> <filter-name>ajax4jsffilter-name> <filter-class>org.ajax4jsf.Filterfilter-class> filter> <filter-mapping> <filter-name>ajax4jsffilter-name> <servlet-name>Faces Servletservlet-name> <dispatcher>REQUESTdispatcher> <dispatcher>FORWARDdispatcher> <dispatcher>INCLUDEdispatcher> filter-mapping>
支持--> <servlet> <servlet-name>Faces Servletservlet-name> <servlet-class>javax.faces.webapp.FacesServletservlet-class> <load-on-startup>1load-on-startup> servlet> <servlet-mapping> <servlet-name>Faces Servletservlet-name> <url-pattern>*.facesurl-pattern> servlet-mapping>
组件支持--> <filter> <description>为正确使用tomahawk组件包而配置description> <filter-name>extensionsFilterfilter-name> <filter-class>org.apache.myfaces.webapp.filter.ExtensionsFilterfilter-class> <init-param> <param-name>uploadMaxFileSizeparam-name> <param-value>10mparam-value> init-param> <init-param> <param-name>uploadThresholdSizeparam-name> <param-value>100param-value> init-param> filter>
<filter-mapping> <filter-name>extensionsFilterfilter-name> <url-pattern>*.facesurl-pattern> filter-mapping> <filter-mapping> <filter-name>extensionsFilterfilter-name> <url-pattern>/faces/*url-pattern> filter-mapping> |
在WEB-INF下面加入jsf_core.tld、html_basic.tld和tomahawk.taglib.xml文件、新建一个faces-config.xml文件,内容配置如下:
<application> <locale-config> <default-locale>zh_CNdefault-locale> locale-config> <view-handler>com.sun.facelets.FaceletViewHandlerview-handler> application>
与Spring集成--> <application> <variable-resolver> org.springframework.web.jsf.DelegatingVariableResolver variable-resolver> application> |
再加入facelets框架的标签支持包,整个环境包大致如下图所示:
4. 上面的配置好了,JSF和Spring就集成起来了。下面我们加入JPA支持,并且将JPA和Spring集成起来。
首先加入persistence.xml文件,内容如下:
xml version="1.0" encoding="UTF-8"?> <persistence 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" version="1.0">
<persistence-unit name="JSJDemoPU" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.ejb.HibernatePersistenceprovider> <class>test.vo.Testclass> <properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.format_sql" value="true" /> <property name="hibernate.hbm2ddl.auto" value="update" />
properties> persistence-unit>
persistence>
|
之后建立一个值对象,代码如下:
package mangerTeam.vo;
import java.io.Serializable; import java.util.Set;
import javax.persistence.Basic; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.Lob; import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; import javax.persistence.Table;
/** * 运动员实体 * * @author 刘岩 */ @Entity @Table(name = "player") public class PlayersVO implements Serializable {
@Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id", unique = true, nullable = false) private Integer id;
@Column(name = "name") private String name;
@ManyToOne(optional=true, fetch = FetchType.LAZY) @JoinColumn(name = "teamId") private TeamVO teamVO;
@ManyToOne(fetch = FetchType.LAZY) @JoinColumn(nullable=false,name = "nationalityId") private NationalityVO nationalityVO;
@Lob @Basic(fetch = FetchType.LAZY) @Column(name = "pic") private byte[] pic;
@Lob @Basic(fetch = FetchType.LAZY) @Column(name = "mess") private String mess;
@ManyToMany(cascade = CascadeType.REFRESH, fetch = FetchType.LAZY) @JoinTable(name = "plays_position", joinColumns = { @JoinColumn(name = "playerId") }, inverseJoinColumns = { @JoinColumn(name = "positionId") }) private Set
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Set return positions; }
public void setPositions(Set this.positions = positions; }
public TeamVO getTeamVO() { return teamVO; }
public void setTeamVO(TeamVO teamVO) { this.teamVO = teamVO; }
public NationalityVO getNationalityVO() { return nationalityVO; }
public void setNationalityVO(NationalityVO nationalityVO) { this.nationalityVO = nationalityVO; }
public byte[] getPic() { return pic; }
public void setPic(byte[] pic) { this.pic = pic; }
public String getMess() { return mess; }
public void setMess(String mess) { this.mess = mess; }
} |
之后写DAO,无论你是用IDE生成的DAO还是自己写的DAO,代码大致如下:
package mangerTeam.dao;
import java.util.HashMap; import java.util.List;
import javax.persistence.EntityManager; import javax.persistence.PersistenceException; import javax.persistence.Query;
import mangerTeam.vo.PlayersVO;
import org.springframework.context.ApplicationContext; import org.springframework.orm.jpa.JpaCallback; import org.springframework.orm.jpa.support.JpaDaoSupport; import org.springframework.transaction.annotation.Transactional;
@Transactional public class PlayerDAO extends JpaDaoSupport implements IPlayerDAO { // property constants public static final String NAME = "name"; public static final String MESS = "mess"; public static final String PIC = "pic";
public void save(PlayersVO entity) { logger.info("saving Player instance"); try { getJpaTemplate().persist(entity); logger.info("save successful"); } catch (RuntimeException re) { logger.error("save failed", re); throw re; } }
public void delete(PlayersVO entity) { logger.info("deleting Player instance"); try { entity = getJpaTemplate().getReference(PlayersVO.class, entity.getId()); getJpaTemplate().remove(entity); logger.info("delete successful"); } catch (RuntimeException re) { logger.error("delete failed", re); throw re; } }
public PlayersVO update(PlayersVO entity) { logger.info("updating Player instance"); try { PlayersVO result = getJpaTemplate().merge(entity); logger.info("update successful"); return result; } catch (RuntimeException re) { logger.error("update failed", re); throw re; } }
public PlayersVO findById(Integer id) { logger.info("finding Player instance with id: " + id); try { PlayersVO instance = getJpaTemplate().find(PlayersVO.class, id); return instance; } catch (RuntimeException re) { logger.error("find failed", re); throw re; } }
@SuppressWarnings("unchecked") public List final Object value, final int... rowStartIdxAndCount) { logger.info("finding Player instance with property: " + propertyName + ", value: " + value); try { final String queryString = "select model from PlayersVO model where model." + propertyName + "= :propertyValue"; return getJpaTemplate().executeFind(new JpaCallback() { public Object doInJpa(EntityManager em) throws PersistenceException { Query query = em.createQuery(queryString); query.setParameter("propertyValue", value); if (rowStartIdxAndCount != null && rowStartIdxAndCount.length > 0) {
// 有分页 int rowStartIdx = Math.max(0, rowStartIdxAndCount[0]);
// 开始页 if (rowStartIdx > 0) { query.setFirstResult(rowStartIdx); }
// 一页最大记录数目 if (rowStartIdxAndCount.length > 1) { int rowCount = Math.max(0, rowStartIdxAndCount[1]); if (rowCount > 0) { query.setMaxResults(rowCount); } } } return query.getResultList(); } }); } catch (RuntimeException re) { logger.error("find by property name failed", re); throw re; } }
public List return findByProperty(NAME, name, rowStartIdxAndCount); }
public List return findByProperty(MESS, mess, rowStartIdxAndCount); }
public List return findByProperty(PIC, pic, rowStartIdxAndCount); }
public List logger.info("finding all Player instances"); try { final String queryString = "select model from PlayersVO model"; return getJpaTemplate().executeFind(new JpaCallback() { public Object doInJpa(EntityManager em) throws PersistenceException { Query query = em.createQuery(queryString); if (rowStartIdxAndCount != null && rowStartIdxAndCount.length > 0) { int rowStartIdx = Math.max(0, rowStartIdxAndCount[0]); if (rowStartIdx > 0) { query.setFirstResult(rowStartIdx); }
if (rowStartIdxAndCount.length > 1) { int rowCount = Math.max(0, rowStartIdxAndCount[1]); if (rowCount > 0) { query.setMaxResults(rowCount); } } } return query.getResultList(); } }); } catch (RuntimeException re) { logger.error("find all failed", re); throw re; } }
/** * 按条件的复杂查询 * * @param con * @param rowStartIdxAndCount * @return */ @SuppressWarnings("unchecked") public List final String con, final int... rowStartIdxAndCount) {
try { String queryString = "select model from PlayersVO model WHERE 1=1 ";
if (con != null && !"".equalsIgnoreCase(con)) { queryString = queryString + con; }
final String jpql = queryString;
return getJpaTemplate().executeFind(new JpaCallback() { public Object doInJpa(EntityManager em) throws PersistenceException {
Query query = em.createQuery(jpql);
for (int i = 0; i < conMap.size(); i++) { String key = "option" + i; query.setParameter(key, conMap.get(key)); }
if (rowStartIdxAndCount != null && rowStartIdxAndCount.length > 0) { int rowStartIdx = Math.max(0, rowStartIdxAndCount[0]); if (rowStartIdx > 0) { query.setFirstResult(rowStartIdx); }
if (rowStartIdxAndCount.length > 1) { int rowCount = Math.max(0, rowStartIdxAndCount[1]); if (rowCount > 0) { query.setMaxResults(rowCount); } } } return query.getResultList(); } }); } catch (RuntimeException re) { logger.error("find all failed", re); throw re; }
}
/** * 用JPQL查询计算记录总数 * @return */ public Long countNumJPQL(){ logger.info("finding all Team instances"); try { final String queryString = "select COUNT(model) from PlayersVO model"; return (Long)getJpaTemplate().executeFind(new JpaCallback() { public Object doInJpa(EntityManager em) throws PersistenceException { Query query = em.createQuery(queryString);
return query.getResultList(); } }).get(0); } catch (RuntimeException re) { logger.error("find all failed", re); throw re; } }
public static IPlayerDAO getFromApplicationContext(ApplicationContext ctx) { return (IPlayerDAO) ctx.getBean("PlayerDAO"); } } |
注意:之所以加入注解:@Transactional,是为了AOP进行事务处理。
配置Spring文件applicationContext.xml,内容如下:
xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd" xmlns:tx="http://www.springframework.org/schema/tx" default-autowire="byName">
<bean id="realPoolDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="org.gjt.mm.mysql.Driver" /> <property name="url" value="jdbc:mysql://127.0.0.1:3306/jsjdemo?useUnicode=true&characterEncoding=UTF-8" /> <property name="username" value="root" /> <property name="password" value="root" />
<property name="maxIdle" value="20" /> <property name="maxWait" value="1000" /> <property name="defaultAutoCommit" value="false" /> <property name="removeAbandoned" value="true" /> <property name="removeAbandonedTimeout" value="120" />
bean>
<bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="org.gjt.mm.mysql.Driver" /> <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/jsjdemo?useUnicode=true&characterEncoding=UTF-8" />
<property name="acquireIncrement" value="5" />
<property name="maxPoolSize" value="20" />
<property name="minPoolSize" value="5" />
<property name="user" value="root" />
<property name="password" value="root">property> bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="persistenceUnitName" value="JSJDemoPU" /> <property name="dataSource" ref="c3p0DataSource" /> bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource"> <ref bean="c3p0DataSource" /> property> bean>
<bean id="testDAO" class="test.dao.TestDAO"> <property name="entityManagerFactory" ref="entityManagerFactory" /> bean>
<bean id="TeamDAO" class="mangerTeam.dao.TeamDAO"> <property name="entityManagerFactory" ref="entityManagerFactory" /> bean> <bean id="PositionDAO" class="mangerTeam.dao.PositionDAO"> <property name="entityManagerFactory" ref="entityManagerFactory" /> bean> <bean id="PlayerDAO" class="mangerTeam.dao.PlayerDAO"> <property name="entityManagerFactory" ref="entityManagerFactory" /> bean> <bean id="NationalityDAO" class="mangerTeam.dao.NationalityDAO"> <property name="entityManagerFactory" ref="entityManagerFactory" /> bean> beans> |
5. 之后修改一下faces-config.xml里面的内容。
加入如下内容
<managed-bean> <managed-bean-name>playersVOmanaged-bean-name> <managed-bean-class> mangerTeam.vo.PlayersVO managed-bean-class> <managed-bean-scope>requestmanaged-bean-scope> managed-bean>
<managed-bean> <managed-bean-name>playerActionmanaged-bean-name> <managed-bean-class> mangerTeam.action.PlayerAction managed-bean-class> <managed-bean-scope>requestmanaged-bean-scope>
<managed-property> <property-name>playerDAOproperty-name> <value>#{PlayerDAO}value> managed-property> <managed-property> <property-name>playersVOproperty-name> <value>#{playersVO}value> managed-property> managed-bean> |
这样Spring的DAO就注入了该Action(我还是喜欢叫它Action),至于该Action的代码由于过长,就不在此列出,很简单。无非就是注入DAO,setter和getter呗。
到此JSF+Spring+JPA的环境算是集成起来了,项目代码结构图如下:
JPA的缺点:
必须使用在JDK1.5以上的环境,否则只能望JPA而兴叹。
因为它是标准,还在不断的更新中,所以还是不太成熟,不敢保证之后会有很大的变动。
和Hibernate一样,对于多个表(对象)的联合查询,效率比纯JDBC的执行sql要慢很多。
JSF的缺点:
调试不是很方便。
无论是sun的实现还是apache的实现,都存在比较多的bug。
组件技术不如js开源框架灵活,有点鸡肋的感觉。