Dorado 7 : 02. 实做CRUD(SEFC)

下面我们先做一个单表的CRUD,单表的CRUD比较简单,实际开发的时候不会涉及到立体数据模型的概念,但是我们通过这个范例可以将前面单表数据模型中的相关概念,如:DataType, Dataset等有一个使用上的感觉。便于继续后面知识点的深入了解,查看这个单表维护的基本界面,连接地址:

 http://bsdn.org/projects/dorado7/deploy/sample-center/com.bstek.dorado.sample.data.SimpleCRUD.d

截图如下:

这是一个产品列表的维护界面,属于典型的CRUD界面。基本功能有:

  • 单击添加新增按钮;
  • 单击删除按钮删除当前选中行的记录;
  • 单击Grid中的某一行记录,直接在Grid中编辑;
  • 编辑完成后通过Save按钮完成记录提交保存的工作;
  • 还有本页面有翻页标签,可以进行数据翻页;

我们查看一下sample-center中该页面对应视图的定义,打com.bstek.dorado.sample.data目录下的SimpleCRUD.view.xml:

其中我们注意到,本例中我们用到了model节点(之前的HelloWorld和AJAX范例中都未使用到),本例因为要定义数据模型,所以我们用到了model节点,在model节点中我们定义了一个DataType(ProductType),DataType我们在立体数据模型中提到过,它的目的是为了描述数据实体的各个属性的校验规则、数据类型、显示格式等等。在这个DataType中我们定义了其parent属性,表示这个DataType是Bean类型,另外我们设定其matchType属性为:com.bstek.dorado.sample.entity.Product,表示Bean的类型为Product。

我们注意到前面浏览器中Grid有多个列,但是在这个View视图中我们只定义了一个productName的PropertyDef对象,这是因为我们默认设置了其autoCreatePropertyDefs属性为true,表示允许DataType自动根据相关配置(如Product)自动的创建属性,事实上我们也可以在这个DataType上单击右键,通过其GeneratePropertyDef功能自动创建相关的PropertyDefs:

但在一般情况下,如果不需要通过PropertyDef对象对Bean的属性进行特别设定,我们就可以不用创建,只要保证autoCreatePropertyDefs属性为true就可以,在本例中我们设置了它的required属性为true,确保该属性的非空特性:

在页面上我们可以看到设置了required为true之后的productName在Grid的列中就可以看到一个小箭头图标,另外我们如果在Grid中将当前列的任何一个值清空都会出现一个红色的警告图标,该图标就是告诉操作人员该列是非空的:

我们再来看一下刚才那个Bean的定义,Product.java:

package com.bstek.dorado.sample.entity;

import java.io.Serializable;
...
import javax.persistence.Table;

@Entity
@Table(name = "PRODUCTS")
public class Product implements Serializable {
	private static final long serialVersionUID = -6197184284268376113L;

	private long id;
	private Category category;
	private Long categoryId;
	private String productName;
	private String quantityPerUnit;
	private float unitPrice;
	private int unitsInStock;
	private int unitsOnOrder;
	private int reorderLevel;
	private boolean discontinued;

	@Id
	@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_ID")
	@SequenceGenerator(name = "SEQ_ID", sequenceName = "SEQ_ID")
	public long getId() {
		return id;
	}

	public void setId(long id) {
		this.id = id;
	}

可以看出这个Bean的定义并没有什么提别,其中用到的一些annotation的写法都是Hibernate本身提供的,其中并没有Dorado相关的东西。

定义好DataType之后,我们就还需要定义一个DataSet,就是我们所说的数据集,它用来封装页面的数据,这个数据集中提供了一个重要的属性:dataProvider,如下图:

dataProvider是Dorado中为DataSet提供数据的一个对象,我们看它的值是一个典型的服务定位表达式,这个服务定位表达式之前我们学习过,我们很容易就可以知道它对应到simpleCRUD的getAll方法,我们找到这个方法,com.bstek.dorado.sample.data下SimpleCRUD中的getAll方法:

@DataProvider
public Collection<Product> getAll() {
	return productDao.getAll();
}

这个方法非常简单,就是通过一个DAO获取所有的产品对象,其中DAO的实现完全与Dorado无关,它完全取决与我们将来系统的设计。范例的DAO只做了一个简单的工作,就是通过Hibernate获取一个产品列表。这个getAll方法将最终获得的产品列表直接作为方法调用的返回参数。有了这个数据之后DataSet怎么解析这些数据呢?我们再来看DataSet中的dataType属性,它的值为:"[ProductType]"。这表示它认为自身是一个以ProductType结构为数据实体的一个集合。我们可以IDE中单击这个属性,打开其属性编辑器:

其中的base节点下是Dorado默认提供的一些全局的DataType:Boolean,boolean, Short, shorg,Int,int等。另外这儿我们看到的ProductType就是当前View中私有的DataType。另外在选择的时候需要注意objectType属性的设定:

我们可以选择默认的Default也可以选用Collection这种聚合的方式,如果选用聚合,则向导生成的表达式为:"[ProductType]",否则就是:"ProductType"。差别就在与是否有中括号,在本例中我们采用中扩号,其原因是因为前面我们从Java的getAll方法返回的是一个Collection这种聚合类的数据,如果getAll返回的是单个Bean,则此处的DataType属性就可以为没有中括号的:"ProductType"。

现在我们已经定义好了数据集,以及数据集的数据来源和数据来源的结构的定义。接下来事情就比较简单了,无非就是在页面上定义一个Grid,并将它与数据集关联上,便于展示其中的Product数据。我们查看DataGrid的dataSet属性配置:

这样就完成了DataGrid与数据集的绑定。由于目前这个DataSet中的数据结构比较简单,就是一个Collection结构的二维表,Grid不需要做特别多的设定就可以直接显示其中的数据了。另外我们也注意到虽然浏览器中这个Grid中有很多列,但是在View设计器中这个Grid中却只有一个列,这是因为我们设置了这个Grid的autoCreateColumn属性为true,之前我们设置过DataType的autoCreatePropertyDefs为true,这样DataType可以根据绑定的POJO自动的创建PropertyDef,之后Grid就可以根据绑定的数据集中的DataType自动的创建列对象。

上面我们描述了数据展现和数据提取的工作的大概开发过程,最终界面上所作的各种修改还是要提交的服务器端的,这个提交动作与数据提取的动作基本类似,容易理解,我们来看View中存在一个UpdateAction对象,这个对象就是负责数据提交工作的。其中的updateItem中我们通过dataSet属性定义了要提交的数据集:

另外在UpdateAction中类似Provider的处理方式,定义了其中的dataResolver:"simpleCRUD#saveAll",这也是一个服务表达式,我们找到相关的Java代码:

@DataResolver
@Transactional
public void saveAll(Collection<Product> products) {
	productDao.persistEntities(products);
}

下面为属性设置图:

该方法接受客户端提交上来的Product的集合,并调用Dao完成一个持久化的动作。定义好UpdateAction之后,我们只要将工具栏上的按钮与这个Action绑定就可以:

这样当我们单击这个保存按钮的时候,就可以自动的将当前的数据提交回数据库了。

基于数据模型的编程

在实做单表CRUD的范例中我们初步接触了Dorado基于数据模型开发的一系列对象:

  • DataSet:数据集
  • DataType:数据类型(通常用于描述以数据实体及其中各个属性)
  • DataProvider:用于为Dorado提供数据(即将业务逻辑层中的数据导入到Dorado中)
  • DataResolver:与DataProvider的作用相反,用于将Dorado中的数据交还给业务逻辑层。

其中DataProvider与DataResolver是刚刚新接触的。

下面我们通过一张图来查看这四中对象在数据环路中的作用:

这张图我们在前面已经看到过好多次了,只不过将上面的四种对象放在其中而已。其中虚线框是Dorado的作用范围,其中DataProvider与DataResolver相对来说功能较为明确。

  • DataProvider是用于提供数据的,其目的是将外部与Dorao对接的部分,将数据转换为Dorado所需要的数据;
  • DataResolver的作用是将Dorado中的数据交给外面的业务系统;
  • DataType用来描述整个Dorado作用域中的数据,不仅仅是ViewObject,它在整个环路中都起作用,这样我们拿到一个数据之后,就可以通过DataType知道其中是一个什么样结构的数据,其中包含哪些属性,有哪些子对象等等;
  • DataSet就是一堆数据的集合,有一个ID,便于其他数据感知控件与其绑定;

如果我们将这些概念与自己较为熟悉的数据库的各种术语做一个类比,就可以找到如下的概念翻译:

DB

Dorado7

Record

Entity

Table

EntityList

DataBase

DataSet

DBSChema

DataType

SQL

DataPath

其中DataPath,由于SimpleCURD较为简单,尚未涉及。我们知道由于现在的DataSet是一颗树形的结构,当我们想做一个较为复杂的数据绑定的时候,DataPath就可以告诉你如何从DataSet中抽取数据,其作用就类似于SQL,我们将在后面的文档中再做描述。