一、项目概述
1、Gilead项目
使用Gilead可以省去entity类和mode类之间的转换工作,在GWT客户端直接使用实体类。
Gilead旨在实现把持久性实体简便地传送到JVM环境外。
Gilead的全称是Generic Light Entity Adapter,之前曾叫做Hibernate4GWT,随着开发团队开始支持其他一些框架,项目于是修改成当前的名字。
创建Gilead项目的主要目的是减轻人们把持久性实体传递到其他系统或者其他技术前端时遇到的痛苦。为什么这个过程那么棘手呢?通常是由于持久化框架在编译期或者运行期修改类的定义,添加某些所需信息以减少持久化实体时的麻烦。如此一来,当这些修改了的对象传送到JVM外面以后,就会发生一些奇怪的事情,比如说接收方甚至可能会抛出“NoClassDefFoundError”、“ClassCastException”之类的错误。
针对这种情况,Gilead采取了“克隆-合并”(clone-merge)策略。对象从服务器发送出来的时候,本身会被克隆,克隆操作会根据持久性实体创建普通实例,这个实例既不包含代理,也不包含带持久化特性的集合。当对象返回给服务器时,Gilead可以以下面三种“模式”中的任一种来合并返回的对象。
无状态:实体类必须继承自LightEntity或者实现ILightEntity接口,这样持久化信息才会被保存在克隆出来的对象上面(服务器上不保留相关信息)。
有状态:持久化信息是保存在HTTP会话里面。
动态代理:这也是一种无状态模式,尚处试验阶段。适用于无法修改已有遗留代码来满足无状态模式的情况。
2、GWT-SL项目
GWT Server Library(GWT-SL)是一个旨在简化GWT应用软件和Spring框架集成的项目,它通过允许将POJO作为RPC服务发布的方法帮助实现GWT RPC服务与Spring的集成。允许你直接从GWT客户端调用Spring服务,GileadRPCServiceExporter类将Gilead和GWT-SL整合在一起,提供一个完整的GWT-Spring-Hibernate堆栈。有了GWT-SL这后,服务实现类就不需要继承PersistentRemoteService类了。
二、项目源码详解
1、在web.xml文件中增加以下配置信息
<context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 增加一个Spring控制器,对RPC请求进行统一处理 --> <servlet> <servlet-name>gilead</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>gilead</servlet-name> <url-pattern>/rpc/*</url-pattern> </servlet-mapping>
2、增加一个名为gilead-servlet的Spring控制器配置文件,其内容最终如下:
<bean class="org.gwtwidgets.server.spring.GWTHandler"/> <bean class="com.cjm.server.test.HibernateDomainServiceImpl" parent="abstractService"/>
GWTHandler继承AbstractUrlHandlerMapping类,这是GWT-SL项目增加的控制器映射类,它能将所有RPC请求根据URL映射到对应的RPC服务类。GWTHandler类将所有实现RemoteService接口并且用GWTRequestMapping注解的类导出为一个RPC服务,RPC服务通过由GWTRequestMapping注解类指定的URL进行映射。
3、applicationContext.xml配置文件的内容如下:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" /> <property name="url" value="jdbc:oracle:thin:@localhost:1521:CRM" /> <property name="username" value="cjm" /> <property name="password" value="cjm" /> </bean> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="useTransactionAwareDataSource" value="true"/> <property name="mappingDirectoryLocations"> <list> <value>WEB-INF/hbm/</value> </list> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.Oracle9iDialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.format_sql">true</prop> </props> </property> </bean> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <aop:config> <aop:pointcut id="service" expression="execution(* com.cjm.core.service..*.*(..))"/> <aop:advisor pointcut-ref="service" advice-ref="txAdvice"/> </aop:config> <tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="add*" propagation="REQUIRED" rollback-for="RuntimeException"/> <tx:method name="save*" propagation="REQUIRED" rollback-for="RuntimeException"/> <tx:method name="insert*" propagation="REQUIRED" rollback-for="RuntimeException"/> <tx:method name="update*" propagation="REQUIRED" rollback-for="RuntimeException"/> <tx:method name="delete*" propagation="REQUIRED" rollback-for="RuntimeException"/> <tx:method name="batchAdd*" propagation="REQUIRED" rollback-for="RuntimeException"/> <tx:method name="batchDelete*" propagation="REQUIRED" rollback-for="RuntimeException"/> <tx:method name="*" read-only="true"/> </tx:attributes> </tx:advice> <!-- base bean --> <bean id="baseDao" class="com.cjm.core.dao.impl.BaseDaoImpl"> <property name="sessionFactory" ref="sessionFactory"/> </bean> <bean id="baseService" class="com.cjm.core.service.BaseService"> <property name="baseDao" ref="baseDao"/> </bean> <bean id="abstractService" class="com.cjm.core.service.BaseService" abstract="true"> <property name="baseDao" ref="baseDao"/> </bean>
4、hibernate映射文件
<hibernate-mapping> <class name="com.cjm.domain.BranchInfo" table="BRANCH_INFO"> <id name="id" column="id"> <generator class="native" /> </id> <property name="code"/> <property name="name"/> <property name="state"/> </class> </hibernate-mapping> <hibernate-mapping> <class name="com.cjm.domain.SecDepartment" table="DEPARTMENT_INFO"> <id name="id" column="id"> <generator class="native" /> </id> <property name="code"/> <property name="name"/> <many-to-one name="branchInfo" column="BRANCH_ID" lazy="false" cascade="none"/> </class> </hibernate-mapping> <hibernate-mapping> <class name="com.cjm.domain.SecUser" table="USER_INFO"> <id name="id" column="id"> <generator class="native" /> </id> <property name="loginid" type="string" > <column name="LOGINID"/> </property> <property name="userName" type="string" > <column name="USER_NAME"/> </property> <many-to-one name="secDepartment" column="DEPARTMENT_ID" lazy="false" cascade="none"/> <many-to-one name="branchInfo" column="BRANCH_ID" lazy="false" cascade="none"/> </class> </hibernate-mapping>
5、关键类的源码
@GWTRequestMapping("/domain")
public interface HibernateDomainService extends RemoteService{
public static class Util {
private static final String SERVICE_URI = "../rpc/domain";
public static HibernateDomainServiceAsync getInstance() {
HibernateDomainServiceAsync instance = (HibernateDomainServiceAsync)GWT.
create(HibernateDomainService.class);
ServiceDefTarget target = (ServiceDefTarget)instance;
target.setServiceEntryPoint(GWT.getModuleBaseURL() + SERVICE_URI);
return instance;
}
}
public List<BranchInfo> queryBranchs();
public SecUser getUser(int id);
public void saveUser(SecUser secUser);
}
public interface HibernateDomainServiceAsync {
void queryBranchs(AsyncCallback<List<BranchInfo>> callback);
void getUser(int id, AsyncCallback<SecUser> callback);
void saveUser(SecUser secUser, AsyncCallback<Object> callback);
}
public class HibernateDomainServiceImpl extends BaseService implements HibernateDomainService{
@Override
public List<BranchInfo> queryBranchs() {
List<BranchInfo> list = find("from BranchInfo a where a.state='1');
return list;
}
@Override
public SecUser getUser(int id) {
try{
SecUser secUser = (SecUser)get(SecUser.class, new Long(id));
return secUser;
}catch(Exception ex){
ex.printStackTrace();
return null;
}
}
@Override
public void saveUser(SecUser newUser) {
SecUser oldUser = (SecUser)get(SecUser.class, newUser.getId());
if(oldUser!=null){
oldUser.setUserName(newUser.getUserName());
update(oldUser);
}
}
}
6、GWT模块
GWT模块定义文件内容类似如下:
<module> <inherits name="com.google.gwt.user.User"/> <inherits name="com.google.gwt.user.theme.standard.Standard"/> <inherits name="net.sf.gilead.Gilead4Gwt"/> <!-- 需要继承Gilead4Gwt模块 --> <entry-point class="com.cjm.client.Index"/> <source path="client"/> <source path="domain"/> </module>
RPC调用源码:
HibernateDomainServiceAsync service = HibernateDomainService.Util.getInstance();
service.saveUser(newUser, new AsyncCallback<Object>() {
public void onSuccess(Object obj) {
MessageBox.alert("用户名更新成功!");
}
public void onFailure(Throwable caught) {
MessageBox.alert(caught.getMessage());
}
});
service.getUser(Integer.parseInt(txtUserId.getValueAsString()), new AsyncCallback<SecUser>() {
public void onSuccess(SecUser secUser) {
txtUserName.setValue(secUser.getUserName());
cboBranch.setValue(secUser.getBranchInfo().getId().toString());
}
public void onFailure(Throwable caught) {
MessageBox.alert(caught.getMessage());
}
});