——结合使用Webwork2和Hibernate简化系统结构、提高开发效率
引子
如果狭隘的理解,很多时候,一个运行于B/S平台的业务系统的工作是接受用户从浏览器端输入的信息,经过处理后存储到数据库,或者,从数据库提取数据处理并格式化后传回浏览器显示给用户。然而,让应用设计者很沮丧的是:浏览器功能很弱,只能提交复杂度极低的信息,如携带着文本信息的表单(form);主流的数据库是关系数据库,并不能直接存储面向对象的领域模型数据;而应用系统往往是用面向对象的方式来设计、开发的。从只能传递简单数据类型的form,到面向对象的系统,再到关系数据库,都要跨越恼人的系统边界,也就是,我们都要对同一数据进行多次形态转换才能实现系统功能。为了解决这个问题,系统设计先辈提出了很多解决方案,如:用FormBean封装从浏览器传递过来的简单类型数据(也可能进行一些常见类形状换,但说到底,还是简单类型的堆砌,并不是领域模型domain model),用VO(不同的设计叫法不同,但思想是一样的)来对关系数据库做tables—JavaBeans的一对一映射等。
这种方式对浏览器的Form和关系数据库的Table进行了面向对象的封装,从某种意义上讲,起到了一个适配器的作用,在一定程度上隐藏了浏览器的form和数据库的table的底层信息,简化了系统设计,并在相当长的一段时期内取得了很好的效果。但是,随着业务系统需求复杂度的增加,这种解决方案显示出了诸多缺陷:
由于在系统中引入FormBean、VO这两类与domain object无关的对象,分散了开发人员对领域对象本身的关注,增加了系统的复杂度,降低了系统的运行效率,并对系统的耦合度、复用等带来负面的影响。可以认为,这已不再是最好的、最有效的解决方案,我们有必要对这种处理方式进行改进。
入题
近年,已经出现了很多成熟的解决方案:
OGNL(http://www.ognl.org)等设置、获取Java Object Properties的语言的出现解决了浏览器等UI系统同核心业务模块交互效果不佳的问题,并且,很多前端控制器(Front controller)很好的实现了OGNL,如WebWork2等;Hibernate(http://www.hibernate.org)等O/R Mapping的出现,解决了从对象系统到关系数据库之间的不协调。
于是,在开发中,我们不必被迫在跨越逻辑层次时在不同的JavaBeans之间进行转换。
实现
建立详细细致的业务模型,如:Employee
package info.powersoft.sample;
import java.util.Date;
/**
* Description:
* Copyright: Copyright (c) 2005
* @author guangzong.xu
* @since 2005-3-17
* @version 1.0
*/
/**
* @author guangzong.xu
*
* TODO To change the template for this generated type comment go to
* Window - Preferences - Java - Code Style - Code Templates
*/
public class Employee {
private Long id;
private String name;
private Date dateOfBirth;
private Department department;
/**
* @return Returns the dateOfBirth.
*/
public Date getDateOfBirth() {
return dateOfBirth;
}
/**
* @param dateOfBirth The dateOfBirth to set.
*/
public void setDateOfBirth(Date dateOfBirth) {
this.dateOfBirth = dateOfBirth;
}
/**
* @return Returns the department.
*/
public Department getDepartment() {
return department;
}
/**
* @param department The department to set.
*/
public void setDepartment(Department department) {
this.department = department;
}
/**
* @return Returns the id.
*/
public Long getId() {
return id;
}
/**
* @param id The id to set.
*/
public void setId(Long id) {
this.id = id;
}
/**
* @return Returns the name.
*/
public String getName() {
return name;
}
/**
* @param name The name to set.
*/
public void setName(String name) {
this.name = name;
}
}
Department
package info.powersoft.sample;
/**
* Description:
* Copyright: Copyright (c) 2005
* @author guangzong.xu
* @since 2005-3-17
* @version 1.0
*/
/**
* @author guangzong.xu
*
* TODO To change the template for this generated type comment go to
* Window - Preferences - Java - Code Style - Code Templates
*/
public class Department {
private Long id;
private String name;
private Department superDepartment;
/**
* @return Returns the id.
*/
public Long getId() {
return id;
}
/**
* @param id The id to set.
*/
public void setId(Long id) {
this.id = id;
}
/**
* @return Returns the name.
*/
public String getName() {
return name;
}
/**
* @param name The name to set.
*/
public void setName(String name) {
this.name = name;
}
/**
* @return Returns the superDepartment.
*/
public Department getSuperDepartment() {
return superDepartment;
}
/**
* @param superDepartment The superDepartment to set.
*/
public void setSuperDepartment(Department superDepartment) {
this.superDepartment = superDepartment;
}
}
DAO
package info.powersoft.sample;
/**
* Description:
* Copyright: Copyright (c) 2005
* @author guangzong.xu
* @since 2005-3-17
* @version 1.0
*/
/**
* @author guangzong.xu
*
* TODO To change the template for this generated type comment go to
* Window - Preferences - Java - Code Style - Code Templates
*/
public interface EmployeeDAO {
Long insertEmployee(Employee employee);
}
使用Webwork2作为前端控制器(front controller),并在相应的action中把该业务模型作为一个变量,设置相应的getter、setter,如
package info.powersoft.sample;
/**
* Description:
* Copyright: Copyright (c) 2005
* @author guangzong.xu
* @since 2005-3-17
* @version 1.0
*/
/**
* @author guangzong.xu
*
* TODO To change the template for this generated type comment go to
* Window - Preferences - Java - Code Style - Code Templates
*/
public class EmployeeAction {
private EmployeeDAO employeeDAO;
private Employee employee;
/**
* @return Returns the employeeDAO.
*/
public EmployeeDAO getEmployeeDAO() {
return employeeDAO;
}
/**
* @param employeeDAO The employeeDAO to set.
*/
public void setEmployeeDAO(EmployeeDAO employeeDAO) {
this.employeeDAO = employeeDAO;
}
/**
* @return Returns the employee.
*/
public Employee getEmployee() {
return employee;
}
/**
* @param employee The employee to set.
*/
public void setEmployee(Employee employee) {
this.employee = employee;
}
public String execute() throws Exception {
employeeDAO.insertEmployee(employee);
return "success";
}
}
其中,EmployeeDAO是用于将域对象存储到数据库中的DAO,特定实现可以用Hibernate,这个大家都很熟悉,不在此赘述。
发送到浏览器中的网页为
我们可以注意到网页中,文本输入框(input type="text")的名字(name)为类似于这样的东西:“employee.department.id”,经Webwork2的interceptor的拦截,自动把这些form中的field中的字段拼装成我们所需要的employee对象,供我们方便的调用,省去了在FormBean到Domain Object间的转换。同样的道理,我们也可以把这些域对象送到视图层去格式化显示(这其中要用一些支持特定功能的表示技术,如jsp2.0、freemarker等)
完了的话
至此,我们已经完成从浏览器接受输入到存储进数据库,在这过程当中,一直是同一个域对象(Domain Object)承载着应用所需要的信息,没有被迫进行数据转换,代码相对简洁,结构相对明了,同时,执行效率也较传统方式(FormBean->Domain Object->VO)有所提高。
当然,本文只是从梗概上介绍了这种实现方式,所提供的代码并不能直接运行。如果你在体验的过程中有好的想法,或者遇到问题,欢迎与我交流[email protected]