最佳Excel导入实践(二)

相关链接
最佳Excel导入实践(一)
最佳Excel导入实践(二)
最佳Excel导入实践(三)
最佳Excel导入实践(四)
最佳Excel导入实践(五)


  我们先以销售订单为例看一下实际的业务场景:通常在常用的业务系统中都会有单据的保存操作,如销售订单的保存方法可能如下:OrderService.Save(Order order)。而Excel导入实际上也是Save功能,只是录入方式不同而已,一个从普通UI,一个从Excel,所以在本导入方案中会重用此Save方法。另外,导入存在很多必要的检查(如数据类型,值范围等)。
  为了能满足系统中所有的导入功能和体现面向对象的设计原则,引入如下导入规则类:

 

  本例是以表头表体的业务场景来讲解的(如一张订单中可能有多个商品的分录),如果只是单一表的结构要比这个简单。
  其中规则包含了常用的检查以及保存时要执行的方法等信息。规则类的创建是根据以下配置文件(当然也可以来源于数据库):

 

<ImportConfig id="order">
  
<!--本设计是讨论一种比较复杂的,包含表头表体的导入(如一张订单中可能包含多个商品的分录)。-->
  
<!--Excel中的记录将被映射成的实体对象名(表头)-->
  
<HeadClass>BusinessRule.Order</HeadClass>
  
<!--表头中对表体实体引用的属性名。如果不是表头表体的结构,可以删除此结点-->
  
<EntryProperty>OrderEntries</EntryProperty>
  
<!--Excel中的记录将被映射成的实体对象名(表体)。如果不是表头表体的结构,可以删除此节点-->
  
<EntryClass>BusinessRule.OrderEntry</EntryClass>
  
<!--保存方法所在的类名-->
  
<MethodClass>BusinessRule.OrderService</MethodClass>
  
<!--对象构造完成之后调用的保存方法名-->
  
<SaveMethod>Save</SaveMethod>
  
<!--调用保存方法之前可能会有一些业务逻辑上的检查,此为检查的方法名,如果没有可以删除此节点-->
  
<ExValidateMethod>ExValidate</ExValidateMethod>
  
  

  
<!--以下为Excel中各列的信息配置-->
  
<!--
  IsEntry:是否表体字段。默认为0,不是表体字段;
  IsPrimaryKey:是否唯一字段。因一个订单头可能对应多个订单体,即Excel中的多条记录对应一张订单。导入时将根据IsPrimaryKey=1的字段值来确定哪些行属于一张订单。
  Property:实体类中对应的属性名。
  ColumnName:Excel中对应的列头名。
  Requried:是否必录项。默认0,不必录。
  DataType:生成引入模板时将根据此生成对应列的有效性验证。如果是引用类型(DataType=ref),则会根据RefConfig生成下拉列表及保存时进行相应的转换(如将客户名称转换为客户ID,因为数据库中存的为ID,Excel中显示的为名称)。
  RefConfig:引用类型的配置信息。引用类型一般需要转换后保存,故配置项依次为 转换后的字段;转换时查找的表;转换时用到的比较字段;过滤条件。
  DefValue:默认值。支持多种默认值:$F{XXX}表示取系统级的变量,当然系统中要首先存在这些变量;$E{a.b}表示取其它对象的相关属性;$C{XXX}表示默认值为常量;$S{XXX}表示通过SQL取默认值等。
  Min:最小值。
  Max:最大值。
  MaxLen:最大长度。
  Comment:生成引入模板时列头的批注。
  
-->
  
<!--表头字段-->
  
<Column IsPrimaryKey="1" Property="OrderNO" ColumnName="订单编号" Required="1" DataType="string" Comment="编号只能由字母或数字组成。" MaxLen="40"></Column>
  
<Column Property="OrderDate" ColumnName="订单日期" Required="1" DataType="date" DefValue="$F{SysDate}" Comment="日期格式为YYYY-MM-DD,如果不录入则取系统当前日期。"></Column>
  
<Column Property="CustomerID" ColumnName="客户名称" Required="1" DataType="ref" RefConfig="CustomerID;t_Customer;CustomerName;IsUsed=1"></Column>

  
<!--表体字段-->
  
<Column IsEntry="1" Property="InvID" ColumnName="商品名称" Required="1" DataType="ref" DefValue="InvID;t_Inventory;InvName;"></Column>
  
<Column IsEntry="1" Property="Qty" ColumnName="数量" Required="1" DataType="decimal" ></Column>
  
<Column IsEntry="1" Property="Price" ColumnName="单价" Required="1" DataType="decimal" DefValue="$E{InvID.StandardPrice}"></Column>
  
<Column IsEntry="1" Property="Discount" ColumnName="折扣率(%)" DataType="decimal" Min="0" Max="100" DefValue="$C{0}" Comment="请输入0-100之前的数字。"></Column>

  
<!--制单人不用在导入模板中显示,但数据库中要求有值,所以没有设置ColumnName属性,只设置了默认值-->
  
<Column Property="CreateUser" DefValue="$F{UserNO}" ></Column>
</ImportConfig>

  各节点的说明已经在注释中做了详细的说明。注意,为了代码的可读性更好,将其中的RefConfig节点转换成了对象中的RefField,RefTable,RefConvertField和RefFilter属性。导入时执行转换时将执行类似如下SQL语句:

SELECT top 1 RefField FROM RefTable WHERE RefConvertField='XXX' AND RefFilter

 

下一篇我们将讲Excel导入模板的生成。

posted @ 2009-10-24 10:57 atao.xiang 阅读(...) 评论(...) 编辑 收藏

公告