记得是去年Java EE 5发布初期时,就兴致昂然的读了document,并且按照相关的refrence的说明,对jsf、ejb3.0、jpa等进行了应用,当时也是我自己先动手试验,部署时那个顺利啊。但是当我真正运用时,通过我打出来监控日志,发现jsf初始时对数据表进行的查询方式和消耗,使我完全回退了jsf的应用,当时在公司得出结论:
1、JSF成熟以后再用;
2、EJB3.0由于jboss和Sun Application Server的不统一,并且其他商用级服务器并不支持因此。暂缓使用;
3、唯一我们可以考虑采纳的是JPA,但是公司技术人员对原有的SSH的配置方式已经非常熟悉,没有必要为了“缩短开发周期”这个理论优点来改我们“已经实现好了的框架和系统”。
以上三点结论呈述后,当时使公司已有的一个使用JSF项目(那位项目经理力推JSF,并且能力值得赞许)也退出JSF的项目,使用公司成熟的Struts 1.2+Spring 1.2+Hibernate3.0。
这一次与上次相同了,我研究了几天了吧,搭建了Struts 2.0+Spring 2.0+Hibernate 3.3的应用,其中Hibernate 3.3应用的是JPA,Spring的结合方式也是使用JpaTransactionManager,具体的一些配置文件罗列如下:
- 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="studioUnit" transaction-type="RESOURCE_LOCAL">
- <provider>org.hibernate.ejb.HibernatePersistenceprovider>
- <jta-data-source>java:comp/env/jdbc/oraclejta-data-source>
- <class>com.****.****.entity.WapConfigclass>
- <properties>
- <property name="hibernate.dialect"
- value="org.hibernate.dialect.Oracle9Dialect" />
- <property name="hibernate.show_sql" value="true" />
- <property name="hibernate.format_sql" value="true" />
- properties>
- persistence-unit>
- persistence>
以下是spring 2.0的配置:
- 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:aop="http://www.springframework.org/schema/aop"
- xmlns:tx="http://www.springframework.org/schema/tx"
- xsi:schemaLocation="
- http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
- http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
- http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
- <bean
- class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
- <bean id="entityManagerFactory"
- class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
- <property name="loadTimeWeaver">
- <bean
- class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver" />
- property>
- bean>
- <bean id="transactionManager"
- class="org.springframework.orm.jpa.JpaTransactionManager">
- <property name="entityManagerFactory"
- ref="entityManagerFactory" />
- bean>
- <tx:annotation-driven transaction-manager="transactionManager" />
- <bean id="wapConfigDao"
- class="com.***.***.dao.impl.JpaWapConfigDAOImpl">
- <property name="entityManagerFactory">
- <ref bean="entityManagerFactory" />
- property>
- bean>
- <bean id="wapConfigAction" scope="prototype"
- class="com.***.***.action.WapConfigAction">
- <constructor-arg ref="wapConfigDao" />
- bean>
- beans>
以下是struts.xml文件:
- xml version="1.0" encoding="UTF-8" ?>
- "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
- "http://struts.apache.org/dtds/struts-2.0.dtd">
- <struts>
- <constant name="struts.devMode" value="true" />
- <constant name="struts.objectFactory" value="spring" />
- <package name="default" extends="struts-default">
- <action name="list" method="execute" class="wapConfigAction">
- <result>/list.jspresult>
- <result name="input">/list.jspresult>
- action>
- <action name="remove" class="wapConfigAction" method="remove">
- <result>/list.jspresult>
- <result name="input">/list.jspresult>
- action>
- <action name="save" class="wapConfigAction" method="save">
- <result>/list.jspresult>
- <result name="input">/list.jspresult>
- action>
- package>
- struts>
以下是tiles.xml 文件
- xml version="1.0" encoding="GBK" ?>
- "-//Apache Software Foundation//DTD Tiles Configuration 2.0//EN"
- "http://struts.apache.org/dtds/tiles-config_2_0.dtd">
- <tiles-definitions>
- name="showcase.index" template="/tiles/layout.jsp">
- <put-attribute name="title" value="测试用标题"/>
- <put-attribute name="header" value="/tiles/header.jsp"/>
- <put-attribute name="body" value="/tiles/body.jsp"/>
- definition>
- <definition name="showcase.freemarker" template="/tiles/layout.jsp">
- <put-attribute name="title" value="Tiles/Freemarker Showcase"/>
- <put-attribute name="header" value="/tiles/header.jsp"/>
- <put-attribute name="body" value="/tiles/body.ftl"/>
- definition-->
- tiles-definitions>
以下是通用的一段dao代码的片段,采用的是spring的jpatemplate,所以没有声明PersistenceContext:
- package com.***.common.spring.impl;
- import java.util.List;
- import org.apache.log4j.Logger;
- import org.springframework.orm.jpa.support.JpaDaoSupport;
- import org.springframework.transaction.annotation.Transactional;
- import com.**.common.jpa.BaseEntity;
- import com.**.common.spring.BaseDao;
- @Transactional
- public class JpaDaoImpl extends JpaDaoSupport implements BaseDao {
- private Logger log = Logger.getLogger(getClass());
- // private EntityManager entityManager ;
- // @PersistenceContext
- // public void setEntityManager(EntityManager em) {
- // this.entityManager = em;
- // }
- // public EntityManager getEntityManager() {
- // return entityManager;
- // }
- private Class claz;
- public JpaDaoImpl(Class z){
- super();
- claz = z;
- }
- public List find(String sql) {
- return getJpaTemplate().find(sql); }
- public void saveOrUpdate(BaseEntity obj){
- if ( obj.id == null )
- // getEntityManager().persist(obj);
- getJpaTemplate().persist(obj);
- else
- // getEntityManager().merge(obj);
- getJpaTemplate().merge(obj);
- }
- public void remove(BaseEntity obj) {
- if (obj != null)
- // getEntityManager().remove(obj);
- getJpaTemplate().remove(obj);
- }
- /**
- * 采用本地删除方法快速删除某个对象,但是需要指定物理表名.这种方法相较于获得ID再利用ID去: Person person = find(id);
- * 出来对象来再remove(perseon)的方式会更有效率.
- *
- * @param id
- * 要删除对象的标识符
- * @param table
- * 物理表名
- */
- public void remove(Long id, String table) {
- getJpaTemplate().getEntityManager().createNativeQuery(
- "DELETE FROM " + table + " WHERE ID=" + id).executeUpdate();
- }
- public BaseEntity get(Long id) {
- return (BaseEntity)getJpaTemplate().find(claz, id);
- }
- }
WapConfig这个实体的主要声明代码如下:
- // Fields
- @Id
- @Column(name = "ID", unique = true, nullable = false, insertable = true, updatable = true, precision = 6, scale = 0)
- private Long id;
- @Column(name = "KEY", unique = false, nullable = false, insertable = true, updatable = true, length = 100)
- private String key;
- @Column(name = "VALUE", unique = false, nullable = true, insertable = true, updatable = true, length = 100)
- private String value;
- @Column(name = "MEM", unique = false, nullable = true, insertable = true, updatable = true, length = 500)
- private String mem;
写出来时,是平淡无奇,可以说以上这样的配置与代码是我完整的配置,直到最后tiles的配置,能够正常跑了——注意,是在JPA不读数据的情况下能正常跑的。
说能跑,也不是那么顺利的能跑的,碰到很多问题,都通过官方文档和在网络搜寻同行人员的经验,得到了解决,单说这最后一个变态的情况,当我代码读JPA实体时,如调用上面的代码:
- List wapConfigs = dao.find(" FROM WapConfig";
本想可以得出一个结果集,即使是空值,但实际结果是能过控制台得知,发到Oracle的查询语句变成了这样的:
- SELECT __wapConfig0.ID as ID,
- __wapConfig0.__amber_0.ID as __amber_0.ID,
- __wapConfig0.__amber_0.KEY as __amber_0.KEY,
- __wapConfig0.__amber_0.VALUE as __amber_0.VALUE,
- __wapConfig0.__amber_0.MEM as __amber_0.MEM,
- __wapConfig0.KEY as KEY,
- __wapConfig0.VALUE as VALUE,
- __wapConfig0.MEM as MEM
- FROM
- WapConfig __wapConfig0
注意看,我的实体根本是没有__amber_0.**这样的字段的,说不清这个字段究竟是哪里冒出来的。兼于前面坎坎坷坷的碰到很多问题,我尝试解决未果后,思考的问题就不再局限在技术层面了。
首先Struts 2单说他的taglig、OGNL、Action、Spring支持的改进,Hibernate JPA的支持,Spring对JPA的支持,都是非常的诱人的,特别对我这种技术型的人员,有很强的攻克他的欲望。就这个各个组件扔出来的思路和方向对我们来说都是很有利的,我相信各个组件成熟后,我将来一定采用这个更优秀的框架。
但是现在,我大致发现一些我认为存在的问题:
1、Struts2对tiles的支持目前我认为做得没有struts1好,而这对于团队开发、技术与美工的协作来说,是相当受重视的优点的一部份;
3、Struts 2.0.6GA的说明文档不够齐全,也就是说他还可能存在完善的方面;
2、Hibernate对JPA支持方面不是很稳定;至少从已知问题来说,这个结论可能是由于我不继续深究下去而得出的错误结论。
就目前来看,我认为最稳定的部份还是spring2。
脱开技术层面来看问题,公司400人左右都习惯于现有的的研发平台的使用方法,如果新框架存在这样那样的对“配置依赖性相当脆弱”的情况,新技术的采用会对各方面不可避免的带来新的培训成本和高技术风险,并且更为关键的是现有人员都对SSH烂熟于胸了。这个熟练应该暂时可以抵消Struts2和Hibernate JPA的优势,至少在目前新框架不稳定的情况下。
我的结论是:新技术不急着采用,我们不急着做新技术的急先锋,也没必要做第一个吃螃蟹的人。可以等社会上已经出相关书籍,已经有相关的培训课程安排后我们再同步推出更新的SSH2框架,那时,我们现有的技术人员本身有机会在工作之外通过书籍、实践获得新知识,我们再安排新框架的培训,相信会让现有技术人员低成本较快过渡到新框架去。再者,社会上可以招聘到对新技术方案了解的人。