Apache OFbiz entity engine源代码解读

简单介绍

近期一直在看Apache OFbiz entity engine的源代码。为了能够更透彻得理解,也由于之前没有看人别人写过分析它的文章,所以决定自己来写一篇。

首先,我提出一个问题,假设你有兴趣能够想一下它的答案:

JDBC真的给数据訪问提供了足够的抽象,以至于你能够在多个支持jdbc訪问的数据库之间随意切换而全然不须要操心你的数据訪问代码吗?

我以前在微博上有过关于该问题的思考:


事实上这个感慨正是来自于我之前在看的一篇关于jdbc的文章,里面提到了jdbc中的一些设计模式(工厂方法),提供了与底层数据库交互的抽象(不可否认是非常好的做法),能够应对在不须要改动DAO的情况下,自由切换数据库。而我结合近期在看的OFbiz的entityengine发了上面的这些吐槽!

OFbiz对数据源的訪问是否仍然借助于JDBC?当然,毫无疑问。但它攻克了我在吐槽中所说的,它并不依赖程序猿遵循SQL标准,而是通过entity engine依据它给出的语义,生成标准的sql。这些sql包括了DML、DDL、TCL(事务控制语言),从而提供一套完整的数据訪问Engine。它带来的优点是什么?省去了大量机械而反复的DAO CRUD 的编写工作量,无缝支持多达13种数据库,抽象了经常使用的查询/筛选逻辑,提供了通过配置生成服务而无需编写代码的底层支持!(当然优点还不止这些)。

以下我们就一起来看看entity engine究竟是怎样做到这些的。

源代码解读

SQL的运行——SQLProcessor

SQLProcessor 负责entityengine中全部SQL的终于运行,包括了事务的提交、回滚等。从该类的实现中你能看出,ofbiz终于跟数据库打交道的还是JDBC,而由于JDBC本身就是对数据库訪问的抽象,所以使用JDBC操作数据库对于不论什么RDBMS都是适用的,你仅仅须要提供终于使用的数据库的jdbc-driver。但数据库的某些特性,sql,数据类型,这些并非全然一致的标准,因此entityengine提供的语义层有效得屏蔽了SQL与字段类型等在各个数据库上的差异,使得终于用户无需直接这些差异打交道,一切都有它来处理。

提供了对实体最基本的CRUD功能——GenericDAO

GenericDAO使用了多线程技术来运行某些操作,因此创建该对象的时候须要实例化一组线程以及创建一个线程池,这些操作相对来说有些“昂贵”,在其内部採用一个staticmap 来缓存已被成功创建的对象:

public static GenericDAO getGenericDAO(GenericHelperInfo helperInfo) {
        GenericDAO newGenericDAO = genericDAOs.get(helperInfo.getHelperFullName());

        if (newGenericDAO == null) {
            genericDAOs.putIfAbsent(helperInfo.getHelperFullName(), new GenericDAO(helperInfo));
            newGenericDAO = genericDAOs.get(helperInfo.getHelperFullName());
        }
        return newGenericDAO;
    }

假设你随意浏览一个方法的实现,在其内部都会实例化一个SQLProcessor来运行拼接而成的SQL(当然这里仅仅有简单的SQL是直接拼接的,复杂一点的,带条件的SQL都是採用各种方式生成的,比方EntityCondition)。

提供很多其它面向业务层的数据訪问方法的抽象——GenericHelper接口

GenericHelper接口提供了更偏向业务的数据訪问抽象,这使得你能够不用去关注数据源是什么,基于数据源的实现交给该接口的实现类就可以。事实上,GenericHelper也确实有一个全然基于内存的实现(MemoryHelper),仅仅是除了用于測试,好像没有正式在线上场景中使用,只是这是一个不错的想法——随着nosql的流行,一部分关系不是非常强或者不是非常重要的数据能够基于一些memorydb来实现。来看看该接口极事实上现类的关系图:


它有两个实现者,当中一个实现便是GenericHelperDAO,毫无疑问它是基于RDBMS的实现,由于它内部都是通过GenericDAO的实例来完毕数据訪问的。还有一个之前提到过了,是基于内存的实现。

梳理一下上面的几个部件:GenericDAO实现了基本的数据訪问操作的(CRUD)。这当中,每一个方法都须要详细去跟数据库通信,通过什么?通过SQLProcessor,SQLProcessor负责每次SQL的运行。通常一个系统里对于数据的訪问/操作并不仅仅仅仅是简单的CRUD,当中还包括有更复杂的操作(比方关系查询、条件删除等),因此OFbiz为这些相对复杂的操作抽象出了一个接口GenericHelper。GenericHelperDAO是对GenericHelper的实现,其内部借助于GenericDAO,来实现这些相对复杂的数据訪问。到最后我们会看到entity engine内最关键的一个部件——Delegator,其内部就是基于GenericHelper来实现数据訪问的。

数据库连接信息实体抽象——GenericHelperInfo

GenericHelperInfo提供了对数据库建立连接所需信息的封装(包括username、password等)

数据库连接的创建工厂——ConnectionFactory

该类提供了对JDBC连接的集中创建,提供了非常多静态方法,依据给定的不同參数,来创建JDBC连接:


同一时候也提供了载入/卸载jdbc-driver以及关闭连接的动作。

GenericHelper的创建工厂——GenericHelperFactory

同上面的数据库连接工厂,此工厂类也对GenericHelper的实例对象进行类缓存。后面会看到entityengine最关键的接口Delegator就拥有一个getEntityHelper方法,该方法用于获取GenericHelper的实例,事实上现就是通过GenericHelperFactory来获取的。

通用的数据实体、数据库对象实体

类关系图:


要提供一个通用的数据訪问引擎,抽象出数据库相关的表示对象是必须的。entityengine对于全部数据库相关对象的定义都放在org.ofbiz.entity.model package下。

当中,ModelInfo提供了对model基本信息的定义(这些信息一般是一些描写叙述信息,跟详细数据库相关的内容无关)。

ModelEntity继承自ModelInfo,它是真实的用于表述一条数据库表中记录的实体。上图中一些对象会作为依赖对象成为它的一部分。它本身也是Transfer Object模式的实现(很多其它关于ofbiz entity engine中使用过的j2ee pattern,请看我之前 一篇博文)。

ModelViewEntity能够简单地理解为数据库的view,它是合成的产物。他相比ModelEntity更为复杂,并且定义了非常多inner class。

ModelChild是非常多数据库对象的抽象父类。它无法被实例化,抽象出它来的目的是,它含有一个parentModelEntity属性,该属性指向关联它的ModelEntity,而其它继承自它的数据库对象基本都须要该属性。

ModelRelation继承自ModelChild,它用于表示Model之间的关系(比方外键关系)。该实体映射到的entitymodel.xml中的例如以下配置节点:

<relation type="one" fk-name="BUDGET_BGTTYP" rel-entity-name="BudgetType">
        <key-map field-name="budgetTypeId"/>
      </relation>
      <relation type="one" fk-name="BUDGET_CTP" rel-entity-name="CustomTimePeriod">
        <key-map field-name="customTimePeriodId"/>
      </relation>
      <relation type="many" rel-entity-name="BudgetTypeAttr">
        <key-map field-name="budgetTypeId"/>
      </relation>

当中的<key-map />节点相应package中的ModelKeyMap实体。

ModelField给出了一个数据字段的抽象,它相应的配置形如:

<field name="geoPointId" type="id-ne"></field>
        <field name="dataSourceId" type="id"></field>
        <field name="latitude" type="floating-point" not-null="true"></field>
        <field name="longitude" type="floating-point" not-null="true"></field>
        <field name="elevation" type="floating-point"></field>
        <field name="elevationUomId" type="id"><description>We need an UOM for elevation (feet, meters, etc.)</description></field>
        <field name="information" type="comment"><description>To enter any related information</description></field>

ModelIndex 抽象出了数据库索引对象,它对象配置文件里的节点形如:

<index name="GLACCT_UNQCD" unique="true">
        <index-field name="accountCode"/>
      </index>

额外的一些辅助类:

ModelReader:Model 定义读取器,从配置文件里读取model的定义

ModelFieldTypeReader: ModelFieldType 定义读取器,从配置文件里读取ModelFieldType的定义

ModelGroupReader: ModelGroup 定义读取器,从配置文件里读取ModelGroup的定义信息

DynamicViewEntity:定义动态视图实体

ModelEntityChecker: entity 定义检查器,它内部定义了非常多预留字符串,在对entity 定义进行检查的时候,会对table name以及fieldname进行检查。

ModelUtil:它实现了从数据库命名到entity定义命名的相互转换规则

上面介绍了对数据库相关对象的抽象,包括通用的数据库实体。但实际上在entityengine以及其它层对实体的訪问并非引用直接上面的ModelEntity对象。entityengine又对ModelEntity进行了包装,构建了GenericEntity对象。

作为TransferObject的数据实体模型——GenericEntity

类继承关系图:


GenericEntity是一个复杂对象,它包括了好多功能:

(1)将field类型从Object强转为其被定义好的特定的类型(通过一系列的get訪问器)

(2)为了表明一个实体是包括了若干个field的集合,它实现了Map<String,Object>接口

(3)实现了Observable接口,这使得某个field被更新之后它能使得观察者得到通知以便数据能够被持久化到数据库

GenericValue能够看做是对GenericEntity的扩展,用于持久化不论什么数据库entity。

它通过组合前面提到的Delegator,来“赋予”对象的CRUD功能(从这里也能够看出ofbiz的entity engine并非传统意义上的ORM模型,这里的实体对象也不是贫血对象,而是被赋予了行为的充血对象),另外它还定义了非常多方法用于获取跟当前实体相关的方法:



GenericPK:继承自GenericEntity,表示一个主键对象。它没有太多override父类的行为,仅仅是提供了几个静态方法,但须要提供跟构建GenericEntity相似的參数(由于它须要对GenericEntity进行初始化)。

备注:个人觉得这里GenericPK继承GenericEntity有些不伦不类,由于它们从语义上应该理解为从属/包括关系。说白了,一个PK仅仅是一个Entity的specialfield而已。(注意GenericEntity是实现了Map接口的,由于它包括全部的field集合),这里用GenericPK继承GenericEntity,也就间接实现了Map接口,而事实上它本身不能调用该接口的方法,由于它不具备一个field集合来装载全部的field,所以在它实例化的时候,它须要注入ModelEntity来实例化GenericEntity。

当然了,我这里仅仅是从语义上来讲这样的做法有些奇怪,而事实上从上下文能够看出这个继承链上的对象都是Transfer Object(原来的Value Object),这么实现应该是出于这个目的(目的是将对象的相关信息集中起来,避免在网络中频繁调用getter/setter方法,造成性能底下或者资源浪费。对于这一点能够參见《Core J2EE Patterns》)。

entity engine层的业务代表——Delegator

前面我们或多或少已经提及过Delegator了,如今是时候来更进一步得探讨它。

你可能感兴趣的:(apache)