Dorado 5 : 2.详解运行流程 (T1)

本节通过按步骤详细解释Marmot的core项目,带领读者一步步从配置数据源开始看清Marmot框架的配置方法和运行机制。

示例演示说明

在前面完成Marmot的core项目导入配置之后,启动Server服务(本例是Tomcat应用服务器)。通过【Browse】打开employee.jsp页面,点击【Query】执行查询查看效果。

图18-08
本例中的数据持久层采用iBatis实现,关于iBatis框架的相关使用请参考相关资料。现在我们就从点击了【Query】之后开始认识Marmot之旅。

配置数据源

毋庸置疑,我们首先要做的还是配置数据源,此处数据源的配置主要为iBatis服务。在core\web\WEB-INF\configs目录下的jdbc.properties文件中,配置数据库连接属性。

jdbc.url=jdbc\:hsqldb\:file:D\:
marmot/sample/data/marmot-sample 
jdbc.username=sa 
jdbc.password= 
jdbc.driverClassName=org.hsqldb.jdbcDriver



图18-09

配置iBatis和DAO

  • iBatis在Spring中的配置

Spring中提供了iBatis框架集成的接口,使得我们不需要单独去创建iBatis配置文件,这一切在Spring中配置即可。在同样的configs目录中,由base-context.xml负责配置。

 <?xml version="1.0" encoding="UTF-8"?> 
 <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" 
 "http://www.springframework.org/dtd/spring-beans.dtd"> 
 <beans> 
 <!-- DataSource --> 
 <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> 
 <property name="locations"> 
 <list> 
 <value>WEB-INF/configs/jdbc.properties</value> <!-- 加载JDBC配置文件 --> 
 </list> 
 </property> 
 </bean> 
 <!-- 配置dataSource数据源对象 --> 
 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> 
 <property name="driverClassName" value="${jdbc.driverClassName}" /> 
 <property name="url" value="${jdbc.url}" /> 
 <property name="username" value="${jdbc.username}" /> 
 <property name="password" value="${jdbc.password}" /> 
 </bean> 
 
 <!-- Transaction 配置事务--> 
 <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
 <property name="dataSource"> 
 <ref bean="dataSource" /> 
 </property> 
 </bean> 
 <bean id="txProxyTemplate" abstract="true" 
 class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> 
 <property name="proxyTargetClass"> 
 <value>true</value> 
 </property> 
 <property name="transactionManager"> 
 <ref bean="transactionManager" /> 
 </property> 
 <property name="transactionAttributes"> 
 <props> 
 <prop key="get*">PROPAGATION_REQUIRED,readOnly,ISOLATION_DEFAULT,-Exception</prop> 
 <prop key="query*">PROPAGATION_REQUIRED,readOnly,ISOLATION_DEFAULT,-Exception</prop> 
 <prop key="calc*">PROPAGATION_REQUIRED,readOnly,ISOLATION_DEFAULT,-Exception</prop> 
 <prop key="find*">PROPAGATION_REQUIRED,readOnly,ISOLATION_DEFAULT,-Exception</prop> 
 <prop key="search*">PROPAGATION_REQUIRED,readOnly,ISOLATION_DEFAULT,-Exception</prop> 
 <prop key="*">PROPAGATION_SUPPORTS,ISOLATION_DEFAULT,-Exception</prop> 
 </props> 
 </property> 
 </bean> 
 
 <!-- Hibernate 配置Hibernate持久层--> 
 <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 
 <property name="dataSource"> 
 <ref local="dataSource" /> 
 </property> 
 <property name="mappingResources"> 
 <list> 
 <value>sample/dao/hibernate/Branch.hbm.xml</value> 
 <value>sample/dao/hibernate/Dept.hbm.xml</value> 
 <value>sample/dao/hibernate/Employee.hbm.xml</value> 
 </list> 
 </property> 
 <property name="hibernateProperties"> 
 <props> 
 <prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop> 
 <prop key="hibernate.show_sql">true</prop> 
 </props> 
 </property> 
 </bean> 
 
 <!-- iBatis 配置iBatis持久层--> 
 <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> 
 <property name="configLocation"> 
 <value>classpath:/sample/dao/ibatis/sqlmap-config.xml</value> 
 </property> 
 <property name="dataSource"> 
 <ref bean="dataSource" /> 
 </property> 
 </bean> 
 </beans> 

base-context.xml文件中配置信息

图18-10

配置说明图01

    1. 在base-context.xml中配置dataSource,引用jdbc.properties中的配置信息(图①)。
    2. 配置声明式事务(图②)。

 

  • 配置DAO

在core项目中,configs目录下的ibatis-dao-context.xml中配置Spring中的sqlMapDAOTemplatet及执行数据访问的DAO。

 <?xml version="1.0" encoding="UTF-8"?> 
 <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" 
 "http://www.springframework.org/dtd/spring-beans.dtd"> 
 <beans> 
 <bean id="sqlMapDAOTemplate" abstract="true" class="org.springframework.orm.ibatis.support.SqlMapClientDaoSupport"> 
 <property name="sqlMapClient"> 
 <ref bean="sqlMapClient"/> 
 </property> 
 </bean> 
 <bean id="branchDAO" class="sample.dao.ibatis.BranchDAOImpl" parent="sqlMapDAOTemplate"/> 
 <bean id="deptDAO" class="sample.dao.ibatis.DeptDAOImpl" parent="sqlMapDAOTemplate"/> 
 <bean id="employeeDAO" class="sample.dao.ibatis.EmployeeDAOImpl" parent="sqlMapDAOTemplate"/> 
 </beans> 

ibatis-dao-context.xml文件中配置信息

图18-11
在bo-context.xml中配置事务代理。

 <?xml version="1.0" encoding="UTF-8"?> 
 <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" 
 "http://www.springframework.org/dtd/spring-beans.dtd"> 
 <beans> 
 <bean id="branchManager" parent="txProxyTemplate"> 
 <property name="target"> 
 <bean class="sample.bo.BranchManager"> 
 <property name="branchDAO"> 
 <ref bean="branchDAO"/> 
 </property> 
 </bean> 
 </property> 
 </bean> 
 <bean id="deptManager" parent="txProxyTemplate"> 
 <property name="target"> 
 <bean class="sample.bo.DeptManager"> 
 <property name="deptDAO"> 
 <ref bean="deptDAO"/> 
 </property> 
 </bean> 
 </property> 
 </bean> 
 <bean id="employeeManager" parent="txProxyTemplate"> 
 <property name="target"> 
 <bean class="sample.bo.EmployeeManager"> 
 <property name="employeeDAO"> 
 <ref bean="employeeDAO"/> 
 </property> 
 </bean> 
 </property> 
 </bean> 
 </beans> 

bo-context.xml中配置信息

图18-12

配置说明图02

    1. 在ibatis-dao-context.xml中配置sqlMapDAOTemplate和DAO(图中③所示)。
    2. 配置声明式事务代理DAO(图中④⑤)。

 

MarmotDataset和dataProvider

在dorado的Dataset数据集中,MarmotDataset属于Marmot框架在ViewModel中的专属数据集对象。打开core项目中视图模型Employee。步骤:【core】—>【src】—>【sample】—>【dorado】—>【Employee】。

图18-13
MarmotDataset与其他Dataset不同之处在于它在数据加载的时候,并不需要调用doLoadData方法,也不需要在ViewModel的实现类或Dataset的监听器中控制加载。而是通过MarmotDataset的dataProvider属性的配置,实现了数据加载的外部化,经由DAO返回的数据加载至MarmotDataset中并最终通过dorado的数据展现控件进行页面展现,这些原本分离的业务处理通过Spring的IOC机制可以全部被"装配"在一起。

  • dataProvider属性

dataProvider属性是MarmotDataset特有的属性。该属性指定了MarmotDataset执行数据加载的时候需要的JavaBean对象在Spring配置中的BeanID。

  • method属性

MarmotDataset中的method属性指定了调用的数据加载方法名。当多个MarmotDataset的dataProvider属性值指定的BeanID相同时(即都是同一个JavaBean处理数据的加载),不同的MarmotDataset可以通过method属性的不同执行各自的数据加载方法,即通过method属性实现数据加载的方法分派。这使得只需要一个JavaBean就可以实现多个MarmotDataset的数据加载,管理更简洁方便。

  • ObjectClazz属性

该属性指定了MarmotDataset数据集的类型,属性值通常是一个POJO。

MarmotUpdateCommand和dataResolver

MarmotUpdateCommand和MarmotRPCCommand两个命令控件类似于UpdateCommand和RPCCommand命令控件,不同之处在于前者是Marmot的专属命令控件。在视图模型Employee中查看MarmotUpdateCommand命令控件。

图18-14
MarmotUpdateCommand命令中其他的属性与UpdateCommand类似,而其中的resolver属性概念类似于MarmotDataset的dataProvider属性。

  • resolver属性

该属性指定了MarmotUpdateCommand执行提交由DatasetInfo中指定的MarmotDataset时需要的JavaBean对象在Spring配置中的BeanID。

  • method属性

该属性指定了处理由客户端发起的AJAX请求的JavaBean中的方法名。当resolver属性值相同而method值不同时,不论是MarmotUpdateCommand命令提交了Dataset数据集对象还是MarmotRPCComamnd命名的远程调用,利用method属性实现方法分派可以使得这些请求被同一个JavaBean中不同的方法处理。默认情况下,如果此项为空,则会调用execute方法。

使用Spring配置关联

Marmot中的数据处理,例如第一次数据加载、不同条件下的查询结果集加载、修改数据后的提交保存等等,需要编写与之对象的业务逻辑处理代码。在本例中,我们讲解的逻辑处理代码有两部分。打开EmployeeDataProvider.java文件查看数据加载的处理代码。步骤:【core】—>【src】—>【sample】—>【view】—>【EmployeeDataProvider.java】。

package sample.view;
import java.util.Map;
import org.apache.commons.lang.BooleanUtils;
import org.marmot.view.DataSet;
import sample.bo.EmployeeManager;
import sample.bo.Page;
import sample.model.Employee;
public class EmployeeDataProvider {
    private EmployeeManager employeeManager;
    public void setEmployeeManager(EmployeeManager employeeManager) {
        this.employeeManager = employeeManager;
    }
    public EmployeeManager getEmployeeManager() {
        return employeeManager;
    }
    public void getEmployee(DataSet dataSet) throws Exception {
        Map parameters = (Map) dataSet.getParameters();
        String id = (String) (parameters).get("id");
        Employee employee = employeeManager.getEmployee(id);
        if (employee != null) {
            dataSet.addRecord(employee);
        }
    }
    public void getEmployeesByDept(DataSet dataSet) throws Exception {
        Map parameters = (Map) dataSet.getParameters();
        String deptId = (String) (parameters).get("deptId");
        dataSet.addRecords(employeeManager.getEmployeesByDept(deptId));
    }
    public void query(DataSet dataSet) throws Exception {
        /** 整理查询条件 */
        Map parameters = (Map) dataSet.getParameters();
        String value;
        value = (String) parameters.get("sex");
        if (value != null && value.length() > 0) {
            parameters.put("sex", BooleanUtils.toBooleanObject(value));
        } else {
            parameters.remove("sex");
        }
        value = (String) parameters.get("married");
        if (value != null && value.length() > 0) {
            parameters.put("married", BooleanUtils.toBooleanObject(value));
        } else {
            parameters.remove("married");
        }
        /** 调用业务逻辑 */
        Page page = employeeManager.getEmployees((Map) dataSet.getParameters(),
                dataSet.getPageIndex(), dataSet.getPageSize());
        /** 添加数据 */
        dataSet.addRecords(page.getList());
        /** 指定总记录数 */
        dataSet.setRecordCount(page.getTotalCount());
    }
}

EmployeeDataProvider.java中的数据加载代码

图18-15
同样在【view】目录下打开EmployeeDataResolver.java文件查看提交Dataset数据集处理的代码。

package sample.view;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.marmot.view.DataResolver;
import org.marmot.view.DataSet;
import sample.bo.EmployeeManager;
import sample.model.Employee;
public class EmployeeDataResolver implements DataResolver {
    private EmployeeManager employeeManager;
    public void setEmployeeManager(EmployeeManager employeeManager) {
        this.employeeManager = employeeManager;
    }
    public EmployeeManager getEmployeeManager() {
        return employeeManager;
    }
    public Object execute(Map dataSetMap, Object parameter) throws Exception {
        return saveAll(dataSetMap, parameter);
    }
    public Object saveAll(Map dataSetMap, Object parameter) throws Exception {
        DataSet datasetEmployee = (DataSet) dataSetMap.get("datasetEmployee");
        Collection employees;
        employees = datasetEmployee.getRecords(DataSet.DELETED);
        for (Iterator iter = employees.iterator(); iter.hasNext();) {
            Employee employee = (Employee) iter.next();
            employeeManager.delete(employee);
        }
        employees = datasetEmployee.getRecords(DataSet.MODIFIED);
        for (Iterator iter = employees.iterator(); iter.hasNext();) {
            Employee employee = (Employee) iter.next();
            employeeManager.update(employee);
        }
        employees = datasetEmployee.getRecords(DataSet.NEW);
        for (Iterator iter = employees.iterator(); iter.hasNext();) {
            Employee employee = (Employee) iter.next();
            employeeManager.create(employee);
        }
        return null;
    }
    public Object deleteSelected(Map dataSetMap, Object parameter)
            throws Exception {
        DataSet datasetEmployee = (DataSet) dataSetMap.get("datasetEmployee");
        int count = 0;
        Collection employees = datasetEmployee.getRecords();
        for (Iterator iter = employees.iterator(); iter.hasNext();) {
            Employee employee = (Employee) iter.next();
            employeeManager.delete(employee);
            datasetEmployee.setRecordState(employee, DataSet.DELETED);
            count++;
        }
        Map outParameters = new HashMap();
        outParameters.put("$message", count + " employee(s) deleted!");
        return outParameters;
    }
    public Object raiseSalary(Map dataSetMap, Object parameter)
            throws Exception {
        DataSet datasetEmployee = (DataSet) dataSetMap.get("datasetEmployee");
        Employee employee = (Employee) datasetEmployee.getRecords().iterator()
                .next();
        float originalSalary = employee.getSalary();
        float newSalary = originalSalary + 500;
        employee.setSalary(newSalary);
        employeeManager.update(employee);
        Map outParameters = new HashMap();
        outParameters.put("$message", employee.getName()
                + "'s salary raised from " + originalSalary + " to "
                + newSalary + " !");
        return outParameters;
    }
}

EmployeeDataResolver.java中的数据处理代码

图18-16
在configs目录下的view-context.xml文件中定义了ViewModel中的MarmotDataset和MarmotUpdateCommand与其对应的JavaBean相关联的信息。

 <?xml version="1.0" encoding="UTF-8"?> 
 <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" 
 "http://www.springframework.org/dtd/spring-beans.dtd"> 
 <beans> 
 <bean id="branchDataProvider" class="sample.view.BranchDataProvider"> 
 <property name="branchManager"> 
 <ref bean="branchManager" /> 
 </property> 
 </bean> 
 <bean id="deptDataProvider" class="sample.view.DeptDataProvider"> 
 <property name="deptManager"> 
 <ref bean="deptManager" /> 
 </property> 
 </bean> 
 <bean id="employeeDataProvider" class="sample.view.EmployeeDataProvider"> 
 <property name="employeeManager"> 
 <ref bean="employeeManager" /> 
 </property> 
 </bean> 
 <bean id="employeeDataResolver" parent="txProxyTemplate"> 
 <property name="target"> 
 <bean class="sample.view.EmployeeDataResolver"> 
 <property name="employeeManager"> 
 <ref bean="employeeManager" /> 
 </property> 
 </bean> 
 </property> 
 </bean> 
 <bean id="testRPCResolver" class="sample.view.TestRPCResolver" /> 
 </beans> 

view-context.xml中的配置信息

图18-17

配置说明图03

  1. MarmotUpdateCommand的DatasetInfo指定了需要提交的MarmotDataset。可以新建多个DatasetInfo实现一次提交多个MarmotDataset(图①)。
  2. MarmotDataset的dataProvider属性指定了需要处理数据加载的Java类的BeanID。在view-cnotext.xml中配置(图②)。
  3. MarmotDataset的method属性指定了处理数据加载的Java类中实际进行处理的方法名(图③)。
  4. MarmotUpdateCommand的resolver属性指定了需要处理被提交的数据集对象的Java类的BeanID。在view-context.xml中配置(图④)。
  5. MarmotupdateCommand的method属性指定了处理被提交的数据集对象的Java类中实际进行处理的方法名。默认属性值为空时执行execute方法(图⑤)。
  6. 处理数据加载的EmployeeDataProvider类和处理被提交的数据集对象的EmployeeDataResolver类在view-context.xml中被装配(图⑥⑦)。

 

配置web.xml

在web.xml中还需要配置前面所提到的那些xml配置文件,实现应用服务器启动时加载,打开web.xml配置文件查看配置信息。 步骤:【core】—>【web】—>【WEB-INF】—>【web.xml】。

 <?xml version="1.0" encoding="UTF-8"?> 
 <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4"> 
 <!-- Spring Configures --> 
 <context-param> 
 <param-name>contextConfigLocation</param-name> 
 <param-value> 
 /WEB-INF/configs/base-context.xml, 
 /WEB-INF/configs/marmot-base-context.xml, 
 /WEB-INF/configs/ibatis-dao-context.xml, 
 <!-- 
 用此行配置替换上一行可进行hibernate的测试 
 /WEB-INF/configs/hibernate-dao-context.xml, 
 
 --> 
 
 /WEB-INF/configs/bo-context.xml, 
 /WEB-INF/configs/view-context.xml, 
 </param-value> </context-param> 
 
 <!-- Dorado Filter --> 
 <filter> 
 <filter-name>GZIPEncoder</filter-name> 
 <filter-class>com.bstek.dorado.core.GZIPEncodeFilter</filter-class> 
 </filter> 
 <filter> 
 <filter-name>dorado-filter</filter-name> 
 <filter-class>com.bstek.dorado.core.DoradoFilter</filter-class> 
 </filter> 
 <filter-mapping> 
 <filter-name>GZIPEncoder</filter-name> 
 <url-pattern>*.jsp</url-pattern> 
 </filter-mapping> 
 <filter-mapping> 
 <filter-name>dorado-filter</filter-name> 
 <url-pattern>/*</url-pattern> 
 </filter-mapping> 
 
 <!-- Spring Loader --> 
 <listener> 
 <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 
 </listener> 
 
 <!-- Dorado Serlvet --> 
 <servlet> 
 <servlet-name>dorado-servlet</servlet-name> 
 <servlet-class>com.bstek.dorado.core.DoradoServlet</servlet-class> 
 <load-on-startup>3</load-on-startup> 
 </servlet> 
 <servlet-mapping> 
 <servlet-name>dorado-servlet</servlet-name> 
 <url-pattern>*.d</url-pattern> 
 </servlet-mapping> 
 </web-app> 



图18-18