您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Model Center   Code  
会员   
   
 
     
   
 
 订阅
应用UML进行数据库建模
 
译者:火龙果Luca
   次浏览      
 2021-8-24 
 
编辑推荐:
在本文中主要讨论了为软件系统提供对象持久性的其中一种方案——在纯关系数据库之上构建面向对象的类模型。
来自于sparx官网,由火龙果软件Luca译、推荐。

简介

当谈到为软件系统提供可靠、灵活和高效的对象持久性时,当今的设计人员和架构师面临着许多选择。 从技术角度来看,选择通常是在纯面向对象、对象-关系混合、纯关系和基于开放或专有文件格式的定制解决方案(例如: XML, OLE结构化存储)。 从供应商的角度来看,Oracle、IBM、Microsoft、POET和其他公司提供了类似但往往不兼容的解决方案。

本文只讨论其中的一种选择,即在纯关系数据库之上构建面向对象的类模型。并不是说这是唯一的、最好的或最简单的解决方案,但从实用主义的角度来看,它是最常见的解决方案之一,并且有可能被误用。

首先,我们将快速浏览一下我们试图连接的两个设计领域:首先是UML中表示的面向对象的类模型,其次是关系数据库模型。

对于每个领域,我们只关注会影响任务的主要特性。 然后,我们将讨论从类模型映射到数据库模型所涉及的技术和问题,包括对象持久性、对象行为、对象之间的关系和对象标识。 最后,我们将回顾UML数据概要文件(由Rational Software提出)。一些面向对象设计,UML和关系数据库建模的相似性也会被提及。

UML中的类模型是用来表示软件系统逻辑结构的主要工件。 它捕获模型域中的数据需求和对象的行为。 发现和细化该模型的技术超出了本文的范围,因此我们假设存在一个设计良好的类模型,它需要映射到关系数据库。

类模型

类是UML中的基本逻辑实体。 它定义了一个结构单元的数据和行为。 类是在运行时从中创建实例或对象的模板或模型。 当我们在UML中开发逻辑模型(如结构层次)时,我们明确地处理类。 当我们处理动态图时,比如序列图和协作图,我们处理的是类的对象或实例以及它们在运行时的交互。 数据隐藏或封装的原理是基于效果的本地化。 类有它负责的内部数据元素。 访问这些数据元素应该通过类的公开行为或接口。 遵循这一原则会产生更易于维护的代码。

行为

使用为类定义的操作在类模型中捕获行为。 操作可以是外部可见的(公共的),对子节点可见的(受保护的)或隐藏的(私有的)。 通过将隐藏数据与公开可访问的接口以及隐藏或受保护的数据操作结合起来,类设计人员可以创建支持而不是阻碍更改的高度可维护的结构单元。

关系和身份

关联是两个类之间的关系,表明关系的至少一方知道并以某种方式使用或操作另一方。 这种关系可能是功能性的(为我做些什么),也可能是结构性的(为我做些什么)。 对于本文来说,最有趣的是结构关系:例如,Address类可能与Person类相关联。 将此关系映射到关系数据空间需要一些注意。

聚合是一种关联形式,它意味着一类对象在另一类对象中的集合。 组合是一种更强的聚合形式,它意味着一个对象实际上是由其他对象组成的。 与关联关系一样,这意味着一个复杂的类属性,需要在映射到关系域的过程中仔细考虑。 虽然类代表可以从中创建许多对象实例的模板或模型,但对象在运行时需要某种方法来标识自身,以便关联的对象可以对正确的对象实例进行操作。 在像c++这样的编程语言中,对象指针可以被传递和保存以允许对象访问唯一的对象实例。 但是,对象通常会被销毁,并要求重新创建它,就像它在最后一个活动实例期间一样。 这些对象需要一种存储机制来保存它们的内部状态和关联,并根据需要检索该状态。

继承为类模型提供了一种方法,可以将公共行为分解到泛化类中,然后这些泛化类充当一个公共主题的许多变体的祖先。 继承是管理重用和复杂性的一种方法。 正如我们将看到的,关系模型没有继承的直接对应物,这为将对象模型映射到关系框架的数据建模者创造了一个困境。 在运行时从一个对象导航到另一个对象是基于绝对引用的。 一个对象有某种形式的链接(指针或唯一的对象ID),用它来定位或重新创建所需的对象。

关系模型

关系数据模型已经存在很多年了,并且在提供性能和灵活性方面有可靠的记录。 它本质上是基于集合的,以“表”为基本单位,“表”由一组一个或多个“列”组成,每一列都包含一个数据元素。

表和列:关系表是一个或多个列的集合,每个列在表结构中有唯一的名称。 每一列都被定义为具有特定的基本数据类型,如数字、文本或二进制数据。 表定义是创建表行的模板,每一行都是可能的表实例的一个实例。 关系模型只提供公共数据访问模型。 所有数据都同样公开并向任何进程开放,以便对其进行更新、查询或操作。 信息隐藏是未知的。

行为

与表相关联的行为通常基于应用于该实体的业务或逻辑规则。 约束可以以惟一性需求的形式应用于列,也可以以关系完整性约束的形式应用于其他表/行、允许值和数据类型。

触发器提供一些可以与实体关联的附加行为。 通常这用于在更新、插入和删除之前或之后强制数据完整性。 数据库存储过程提供了一种方法,可以通过用于构造功能单元(脚本)的专有语言扩展来扩展数据库功能。 这些功能过程并不直接映射到实体,也与实体没有逻辑关系。 关系数据集的导航基于行遍历和表连接。 SQL是用于从表集中选择行和定位实例的主要语言。

关系和身份

表的主键为特定的行提供唯一的标识值。 我们感兴趣的主键有两种:第一种是有意义的键,由在业务域中具有意义的数据列组成;第二种是抽象的唯一标识符,比如计数器值,它没有业务意义,但唯一标识一行。 我们将在后面讨论这一点以及有意义的键的含义。 一个表可能包含映射到另一个表的主键的列。 表之间的这种关系定义了一个外键,并暗示了两个表之间的结构关系或关联。

总结

从上面的概述我们可以看到,对象模型是基于具有状态(属性/数据)和行为的离散实体,通常只能通过类的公共接口访问封装的数据。 关系模型平等地公开所有数据,但对通过触发器、索引和约束将行为与数据元素关联的支持有限。 通过使用唯一的对象标识符和已建立的对象关系(类似于网络数据模型)从一个对象移动到另一个对象,您可以导航到对象模型中的不同信息。 在关系模型中,通过使用通用搜索条件的SQL连接和过滤结果集来查找行。 对象模型中的标识要么是运行时引用,要么是持久唯一ID(称为OID)。 在关系世界中,主键定义了整个数据空间中数据集的惟一性。

在对象模型中,我们有一组丰富的关系:继承、聚合、关联、组合、依赖等。 在关系模型中,我们实际上只能使用外键指定关系。 在研究了这两个感兴趣的领域并比较了它们的一些重要特性之后,我们将简要地离题,看看UML中表示关系数据模型的表示法。

UML数据模型配置文件

数据模型配置文件是一个 UML 扩展,用于支持在 UML 中对关系数据库进行建模。它包括针对表、数据库架构、表键、触发器和约束等内容的自定义扩展。虽然这不是一个被批准的扩展,但它仍然说明了一种在 UML 中建模关系数据库的可能技术。

表和列

UML 数据配置文件中的表是一个具有 «Table» 构造型的类,如图所示,右上角有一个表图标。数据库列被建模为 «Table» 类的属性。

例如,上图显示了与 Customer 表关联的一些属性。在这个例子中,一个对象 id 被定义为主键,还有另外两个列,Name 和 Address。注意,上面的示例根据本机 DBMS 数据类型定义了列类型。

行为

到目前为止,我们只定义了表的逻辑(静态)结构;此外,我们应该描述与列相关的行为,包括索引、键、触发器、过程等。行为表示为构造型操作。

下图显示了上面的表格,其中包含一个主键约束和索引,它们都被定义为构造型操作:

注意,列“OID”上的PK标志定义了逻辑主键,而构造型操作“«PK»idx_customer00”定义了与主键实现相关联的约束和行为(即主键的行为)。

在我们的例子中,我们现在可以定义额外的行为,如触发器、约束和存储过程,如下所示:

这个例子说明了以下可能的行为:

1.主键约束(PK);

2.外键约束(FK);  

3.索引约束(index);  

4.一个触发器(触发);  

5.唯一性约束(Unique);

6.有效性检查(检查)。

使用上面提供的表示法,可以在DBMS级别上对复杂的数据结构和行为建模。 除此之外,UML还提供了表示逻辑实体之间关系的符号。

关系

UML数据建模概要文件将关系定义为两个表之间的任何类型的依赖关系。 它被表示为一个构造型关联,包括一组主键和外键。 数据配置文件还要求一个关系总是包含父键和子键,父键定义一个主键,子键实现一个基于全部或部分父主键的外键。 如果子外键包含父主键的所有元素,则称为“标识”关系,如果只包含主键的一些元素,则称为“非标识”关系。 关系可以包括基数约束,并使用名为关联角色的相关PK - FK对进行建模。 下面的插图说明了使用UML的这种关系建模。

物理模型

UML还提供了一些机制来表示数据库的整体物理结构、它的内容和部署位置。 要在UML中表示物理数据库,请使用如下图所示的构造型组件:

组件表示模型中离散的和可部署的实体。 在物理模型中,组件可以映射到硬件的物理部分(UML中的“节点”)。 要在数据库中表示模式,请在包上使用«schema»构造型。 一个表可以放置在一个«schema»中,以建立它在数据库中的范围和位置。

从类模型到关系模型的映射

在描述了两个感兴趣的领域和要使用的符号之后,我们现在可以将注意力转向如何从一个领域映射或转换到另一个领域。 下面的策略和顺序是为了暗示而不是禁止-调整步骤和程序以适应你的个人需求和环境。

1. 模型类

首先,我们假设我们正在从创建的类模型中设计一个新的关系数据库模式。 这显然是最简单的方向,因为模型仍在我们的控制之下,我们可以将关系数据模型优化为类模型。 在现实世界中,您可能需要在遗留数据模型之上构建类模型——这是一种更困难的情况,也带来了自身的挑战。 目前的讨论将集中在第一种情况。 至少,类模型应该捕获元素之间的关联、继承和聚合。

2. 识别持久对象

在构建了类模型之后,我们需要将它划分为需要持久性的元素和不需要持久性的元素。 例如,如果我们使用模型-视图-控制器设计模式来设计应用程序,那么只有模型部分中的类需要持久状态。

3. 假设每个持久化类映射到一个关系表

这是一个相当大的假设,但在大多数情况下是可行的(暂时不考虑继承问题)。 在最简单的模型中,逻辑模型中的类可以全部或部分映射到关系表。 它的逻辑扩展是单个对象(或类的实例)映射到单个表行。

4. 选择继承策略

继承可能是面向对象模型中最有问题的关系和逻辑构造,需要转换为关系模型。关系空间本质上是扁平的,每个实体在其自身中都是完整的,而对象模型通常非常深,具有完善的类层次结构。深层类模型可能具有多层继承的属性和行为,从而在运行时生成最终的、功能齐全的对象。有三种基本方法可以处理继承到关系模型的转换:

a.每个类层次结构都有一个对应的表,其中包含所有元素的所有继承属性——因此,这个表是层次结构中每个类的联合。 例如,Person、Parent、Child和Grandchild可能都形成一个单一的类层次结构,每个类中的元素将出现在相同的关系表中;

b.层次结构中的每个类都有一个对应的表,其中只包含该类可访问的属性(包括继承的属性)。 例如,如果Child仅从Person继承,那么表将只包含Person和Child的元素;

c.类层次结构中的每代都有一个表,其中只包含该代的实际属性。 例如,Child将映射到一个只有Child属性的表

每种方法都有不同的情况,但我建议第三种选择,这是最简单、最容易维护和更不容易出错的方法。 第一种选择在运行时提供最佳性能,而第二种选择是第一种和最后一种之间的折衷。 第一个选项将层次结构扁平化,并在一个表中定位所有属性—便于对层次结构中的任何类进行更新和检索,但难以进行身份验证和维护。 与一行关联的业务规则很难实现,因为每一行都可能被实例化为层次结构中的任何对象。 列之间的依赖关系可能会变得相当复杂。 此外,当从表中添加、删除或修改列时,对层次结构中的任何类的更新都可能影响层次结构中的每个其他类。

第二种选择是一种折衷方案,它提供了更好的封装并消除了空列。 但是,对父类的更改可能需要在许多子表中复制。 更糟糕的是,两个或多个子类中的父数据可能被冗余存储在许多表中; 如果父节点的属性被修改,那么定位相关的子节点和更新受影响的行就需要付出相当大的努力。

第三个选项更准确地反映对象模型,层次结构中的每个类都映射到它自己的独立表。 对父母或孩子的更新被定位在正确的空间。 维护也相对容易,因为实体的任何修改也仅限于单个关系表。 缺点是需要在运行时重新构造层次结构,以精确地重新创建子类的状态。 Child对象可能需要一个Person成员变量来表示它们的模型血统。 由于两者都需要加载,因此需要两个数据库调用来初始化一个对象。 随着层次结构的深化,随着代的增多,初始化或更新单个对象所需的数据库调用的数量也会增加。

理解将继承映射到关系模型时出现的问题是很重要的,这样您就可以决定哪个解决方案适合您。

5. 为每个类添加一个唯一的对象标识符

在关系和对象世界中,都需要惟一地标识一个对象或实体。 在对象模型中,运行时的非持久对象通常通过直接引用或对象的指针来标识。 一旦创建了一个对象,我们就可以通过它的运行时标识来引用它。 但是,如果我们将对象写入存储,问题是如何根据需要检索完全相同的实例。 最方便的方法是定义OID(对象标识符),它保证在感兴趣的名称空间中是惟一的。 这可能在类、包或系统级别,取决于实际需求。

系统级OID的一个例子可能是用微软的'guidgen'工具创建的GUID(全局唯一标识符); 如。 {a1a68e8e cd92 - 420 b - bda7 - 118 f847b71eb}。 类级别的OID可以使用简单的数字来实现(例如。 32位计数器)。 如果一个对象保存了对其他对象的引用,它可以使用其他对象的OID。 然后可以合理有效地从存储中加载完整的运行时场景。 关于上面的OID值,有一点很重要,那就是它们除了简单的标识之外没有任何内在意义。 它们只是逻辑指针,仅此而已。 在关系模型中,情况通常是非常不同的。

关系模型中的标识通常使用主键实现。 主键是表中的一组列,它们一起唯一地标识一行。 例如,名称和地址可以唯一地标识一个“客户”。 当其他实体(如“Salesperson”)引用“Customer”时,它们实现基于“Customer”主键的外键。 对于我们来说,这种方法的问题是在标识符中嵌入业务信息(如客户名称和地址)的影响。 假设有三四个表都有基于客户主键的外键,并且一个系统更改需要更改客户主键(例如包括“客户类型”)。 修改“customer”表和外键相关的实体所需的工作是相当大的。

另一方面,如果将OID实现为主键,并形成其他表的外键,则更改的范围仅限于主表,因此更改的影响会小得多。 此外,在实践中,基于业务数据的主键可能会发生更改。 例如,客户可能会更改地址或姓名。 在这种情况下,更改必须正确地传播到所有其他相关实体,更不用说更改作为主键一部分的信息的困难了。

无论其他信息发生什么变化,OID总是指同一个实体。 在上面的示例中,客户可以更改名称或地址,而相关的表不需要更改。 当将对象模型映射到关系表时,使用OID的主键而不是与业务相关的主键实现绝对标识通常更方便。 OID作为主键和外键的方法通常会为对象提供更好的加载和更新时间,并减少维护工作。 在实践中,与业务相关的主键可能被替换为:

  • 有关列的惟一约束或索引;
  • 嵌入在类行为中的业务规则;
  • 1和2的组合。
  • 同样,使用有意义的键或OID的决定将取决于正在开发的系统的确切需求。

    6. 将属性映射到列

    通常,我们将类的简单数据属性映射到关系表中的列。 例如,文本和数字字段可以分别表示一个人的姓名和年龄。 这种类型的直接映射应该没有问题—只需在供应商的关系模型中选择适当的数据类型来承载类属性。

    对于复杂属性(例如,属性是其他对象)使用下面详细介绍的方法来处理关联和聚合。

    7. 将关联映射到外键

    更复杂的类属性(例如, 那些代表其他类的类),通常被建模为关联。关联是对象之间的结构关系。例如,Person可能住在Address。 虽然可以将其建模为具有City、Street和Zip属性的Person,但在对象和关系世界中,我们倾向于将该信息构造为一个单独的实体,即Address。 在对象域中,地址表示唯一的物理对象,可能具有唯一的OID。 在关系中,地址可以是address表中的一行,其他实体具有address主键的外键。

    因此,在这两种模型中,都有将地址信息移动到单独实体中的趋势。 这有助于避免冗余数据并提高可维护性。 因此,对于类模型中的每个关联,考虑创建一个从子表到父表的外键。

    8. 地图聚合和组合

    聚合和组合关系类似于关联关系和映射到主-外键对相关的表。 然而,有几点需要记住。 普通聚合(弱形式)模型的关系,如Person驻留在一个或多个Addresses。 在这个例子中,可以有多个person居住在同一个地址,如果person不再存在,与他们相关联的Addresses仍然存在。 这个示例与关系术语中的多对多关系类似,通常实现为一个单独的表,其中包含从一个表到另一个表的主键的映射。

    弱形式聚合的第二个例子是一个实体拥有另一个实体的使用或独占所有权。 例如,一个Person实体聚合一组共享。 这意味着一个Person可能与一个Share表中的零个或多个共享相关联,但每个Share可能与零个或一个Person相关联。 如果该人士不再存在,股份将成为无人持有或转让给另一人士。 在关系世界中,这可以实现为每个Share都有一个“所有者”列,存储一个Person ID(或OID)。

    然而,强形式的聚合具有与之相关的重要完整性约束。 组合,意味着一个实体是由部分组成的,而这些部分与整体有依赖关系。 例如,一个人可能有身份证明文件,如护照、出生证明、驾照等。 一个Person实体可以由一组此类识别文件组成。 如果Person从系统中删除,那么标识文档也必须删除,因为它们映射到唯一的个人。

    如果我们暂时忽略OID问题,弱聚合可以使用中间表(对于多对多情况)或使用聚合类/表中的外键(一对多情况)实现。 在多对多关系的情况下,如果父节点被删除,那么该实体的中间表中的条目也必须被删除。 在一对多关系的情况下,如果父节点被删除,外键条目(即。 '所有者')必须清除。

    在组合的情况下,外键的使用是必须的,添加了一个约束,即删除父组件时也必须删除该部件。 从逻辑上讲,组合还意味着部分的主键构成整个主键的一部分—例如,Person的主键可能由其标识文档ID组成。 在实践中,这可能很麻烦,但逻辑关系是正确的。

    9. 定义关系角色

    对于每个关联类型关系,可以使用角色信息进一步指定关系的每一端。 通常,您将包括主键约束名称和外键约束名称。 图6说明了这个概念。 这在逻辑上定义了两个类之间的关系。 此外,您可以指定额外的约束(例如。 {Not NULL})的角色和基数约束(例如。 0 . . n)。

    10. 模型行为

    现在我们要讨论另一个难题:是否将部分或全部类行为映射到数据库供应商以触发器、存储过程、惟一性和数据约束以及关系完整性的形式提供的功能功能。 非持久对象模型通常会实现一种或多种编程语言(例如。 Java或c++)。 每个类将以公共、保护和私有方法的形式被赋予其所需的行为和责任。

    来自不同供应商的关系数据库通常包括某种形式的基于SQL的可编程脚本语言来实现数据操作。 两个常见的示例是触发器和存储过程。 当我们混合对象模型和关系模型时,通常要决定是在类模型中实现所有业务逻辑,还是将一些逻辑转移到关系DBMS中实现的更高效的触发器和存储过程中。 从纯粹面向对象的观点来看,答案显然是避免触发器和存储过程,并将所有行为放在类中。 这本地化了行为,提供了更清晰的设计,简化了维护,并在DBMS供应商之间提供了良好的可移植性。

    在现实世界中,底线可能是扩展到每秒100或1000个事务,这是存储过程和触发器设计的目的。 如果纯粹的设计、可移植性、维护和灵活性是主要的驱动因素,那么将所有行为本地化到对象方法中。

    如果性能是最重要的考虑因素,可以考虑将一些行为委托给更高效的DBMS脚本语言。 但是要注意,以安全的方式将对象模型与存储过程集成所花费的额外时间(包括与远程效果和调试有关的问题)可能比简单地部署到功能更强的硬件上花费更多的开发时间。

    如前所述,UML Data Profile提供了以下扩展(类型化操作),您可以用它们建模DBMS行为:

  • 主键约束(PK)
  • 外键约束(FK)
  • 索引约束(指数)
  • 触发(触发)
  • 唯一性约束(独特的)
  • 有效性检查(检查)
  • 11. 生成物理模型

    在UML中,物理模型描述了一些东西将如何部署到现实世界中——硬件平台、网络连接、软件、操作系统、dll和其他组件。 您生成一个物理模型来完成这个循环—从初始用例或域模型,到类模型和数据模型,最后到部署模型。 通常,对于这个模型,您将创建一个或多个节点来托管数据库,并将DBMS软件组件放置在这些节点上。 如果数据库被分割到多个DBMS实例上,您可以将表的包(«schema»)分配给单个DBMS组件,以指示数据将驻留在何处。

    结束语

    这篇关于使用UML进行数据库建模的短文到此结束。 如您所见,在从对象世界映射到关系世界时,有许多问题需要考虑。 UML为弥合这两个领域之间的鸿沟提供了支持,并且连同诸如UML Data Profile之类的扩展,是成功集成这两个领域的一种很好的语言。

     

    后记

    希望您读了此文后有所受益。

    如果您有经验乐于分享,欢迎投稿给我们。

    如果您对我们的培训、咨询和工具感兴趣:

    课程:
  • 基于UML和EA进行分析设计
  • MBSE(基于模型的系统工程)  
  • 基于模型的需求管理)方法与实践
  • 基于SysML和EA进行系统设计与建模  
  • 企业架构建模
  • 系统架构建模方法与案例
  • 领域驱动的建模与设计
  • 基于模型的设计
  • 业务建模与业务分析
  • 基于模型的设计

  • MBSE工具链 :
  • 建模工具:EA
  • MBSE平台:iSpace
  • 模型共享:WebEA
  • 文档生成:DocGenerator
  • 模型仿真:Simulator
  • 质量管理:inspector

  • 咨询方案:
  • MBSE(基于模型的系统工程)
  • 基于UML的模型驱动的开发
  • 基于模型的工程管理
  • 基于Sys ML进行系统分析设计
  • 基于模型进行系统分析设计
  • 欢迎联系我们: 俎涛 Zutao@uml.net.cn

       
    次浏览       
     
    相关文章

    用户手册:EA Helper
    自然语言自动化生成图
    使用iSpace进行多人协作建模
    基于模型的软件复用(MBSR)
     
    相关文档

    AUTOSAR_TR_BSW UML模型建模指南
    UML时间图建模(基于EA)
    UML 模型框架(基于EA)
    UML序列图编写规范
     
    相关课程

    UML+EA+面向对象分析设计
    UML + 嵌入式系统分析设计
    业务建模与业务分析
    基于UML和EA进行系统分析设计

    最新活动计划
    QT应用开发 11-21[线上]
    C++高级编程 11-27[北京]
    LLM大模型应用与项目构建 12-26[特惠]
    UML和EA进行系统分析设计 12-20[线上]
    数据建模方法与工具 12-3[北京]
    SysML建模专家 1-16[北京]
     
     
    最新文章
    在EA中内嵌文档- Artifact
    EA中模型视图
    EA中的实体关系图
    使用EA进行风险建模
    EA中的项目词汇表
    EA的模型导出或导入csv文件
    自定义表格(Custom Table)在EA中的使用
    Gap Analysis Matrix(差距分析矩阵)
    更多...   
    MBSE工具
    MBSE平台
    建模工具 EA
    模型库-Model Center
    需求管理-ReqManager
    自动建模-Modeler
    多级仿真-Sys Simulator
    代码工程-Code Engineer
    文档生成器-DocGenerator
    更多...   
    成功案例
    广汽研究院 SysML+EA+软件分析设计
    高合汽车研发部门 建模工具EA、WebEA、学习视频
    国汽智联 建模工具EA、模型库、WebEA和iSpace
    亿咖通 MBSE工程体系与工具链咨询
    中航无人机 MBSE工具链
    吉利汽车 购买EA工具
    华科汽车零部件 购买EA工具
    东风岚图汽车 购买EA工具 以及EA定制开发
    更多...