Hybris Enterprise Commerce Platform这个系列之前已经由我的同事,SAP成都研究院Hybris开发团队的同事张健(Zhang Jonathan)发布过两篇文章了。这里Jerry要特别感谢张健,尽管最近他的第二个孩子诞生了,工作之余的生活变得更加忙碌,然而张健仍然抽出少的可怜的业余时间完成了这个系列的第三篇文章。
前两篇文章分别介绍了SAP Hybris的前端和DTO层:
本文由张健继续向我们介绍SAP Hybris的持久层即Service层。下面是他的正文。
在开始介绍Hybris Service层之前,让我们再次回顾SAP Hybris的架构概要图。前两篇文章,我们已经介绍过图中Hybris前端的Controller,Facade以及用于沟通这两层的DTO。
当我们打开Hybris某个Product的明细页面时,Hybris后台执行了下面三步逻辑:
Service层从数据库里把数据取出,以Model(又称为DAO对象)的形式返回给Facade层。
Facade层调用Converter, 在Populator的帮助下,基于Model生成了DTO。
Product明细页面的Controller将其对应的JSP视图路径返回给Hybris框架,通过JSP技术绘制出最后的UI。
其中步骤2和3已经在这个系列前两篇文章介绍过,本文将详细介绍上述步骤1。
Service层用XML定义的形式来管理Hybris的类型系统,既建立起与数据库表的关联,又将类型系统与具体的数据库实现进行了隔离。Service层的ModelService和Flexible Search提供了便捷的增删改查功能。让我们来一一了解。
Hybris类型系统
在DTO层的ProductFacade里,调用了Service层中获取ProductModel的方法如下。
很简单的几句代码,和其他常见的Java Web项目的Service很相似。那么ProductModel是需要开发人员自行创建吗?
应当注意ProductModel这样的POJO类(Plain Old Java Object)不需开发人员自行创建,而是通过Hybris自有的类型系统生成的。
Hybris里的类型分为两类,第一类是包含所有Hybris业务相关类型的ItemType(又称ComposedType),Product即是这种类型。第二类是为ItemType的集合属性和关系属性提供支持的DataType, 包括了 CollectionTypes, MapTypes, EnumerationTypes, RelationTypes 和 AtomicTypes。例如产品对应的多个媒体文件,就可以用CollectionTypes来定义,然后再用RelationTypes和Product类型做关联。
ABAP顾问们可以把前者(ItemType)类比成ABAP Data Dictionary里定义的具有业务含义的全局数据类型,而后者就是ABAP里用STANDARD, SORTED和HASHED TABLE等等基于业务数据类型形成的新的聚合类型。
对于Java开发者来说, CollectionTypes, MapTypes这些类型其实就是Hybris对JDK中的List, Map等类型一个更高层次的抽象,主要用于描述那些需要持久化的或者需要从数据库中解析出的集合数据。例子可以参加下图的LanguageList和LanguageSet。
上图第77行定义了一个集合数据类型LanguageList,这个集合里每个元素的类型为Language。该集合类型自动生成的Java类型为List。Java类型的生成逻辑,在下一节详细阐述。
items.xml
和Hibernate框架使用XML定义类型和数据库配置相似,Hybris类型系统的具体定义存在各个extension的items.xml文件里。比如Product类型是存在于"../hybris/bin/platform/ext/core/resources"文件夹下的core-items.xml文件内。这个文件也定义了很多Hybris核心的业务类型。
我们看一个实际的例子,即Product类型在items.xml中的定义。SAP Hybris的帮助文档里有items.xml里每个字段的详细含义,这里只介绍下图中红色高亮的字段。
extends: GenericItem表明Product这个类型是在另一个类型GenericItem基础上做扩展。
GenericItem是根类型,相当于Java类型系统的java.lang.Object。Hybris类型系统通过继承的方式避免了字段的重复建模。
假设我们已经用上面展示的items.xml进行了Product的建模,现在有一个新的需求,定义CustomerProduct类型。从业务上说,CustomerProduct仅仅是在Product类型的基础上增加一个字段用于维护Customer ID。ABAP顾问们会新建一个CustomerProduct结构,把Product类型通过Include的方式添加到CustomerProduct中去,以此来重用前者维护的所有字段,然后只需在CustomerProduct上定义一个新字段CUSTOMER_ID即可。
对于Hybris类型系统,思路类似,用CustomerProduct类型去extends Product类型,然后只需定义一个CustomerID字段即可。
autocreate = true: 在执行Hybris命令行ant initialize进行Hybris系统初始化时,根据items.xml的定义在数据库表中创建对应的类型。
generate = true:在ant编译时生成该类型对应的POJO类。
以上图的Product类型为例,因为generate属性设置为true, 因此编译之后,我们能在下面的文件夹发现一个自动生成的POJO类,命名规范为Model.java:
hybris\bin\platform\bootstrap\gensrc\de\hybris\platform\core\model\product
和ABAP很多自动生成的资源通常都放在名为$GEN之类的包的套路一样,POJO类所在的文件目录gensrc,也暗示了该文件是自动生成的。
打开ProductModel.java查看其内容,能进一步了解items.xml里定义的属性是如何映射到这个自动生成的POJO类的:items.xml里定义的每一个类型属性,都会在POJO类里自动生成一套set和get方法。
以name属性为例,在ProductModel.java里自动生成的setName和getName:
table = Products:数据库对应的表名,在整个Hybris类型系统唯一存在。
attribute autocreate="true" qualifier="code" type="java.lang.String" generate="true":POLO类中会出现一个新的成员,名称为code,类型为String,并带有set和get方法,ant initialize时在数据库表中创建该属性。
ModelService
下图是一个例子,通过getModelService拿到ModelService实例,执行save操作。
Flexible Search
对于复杂查询,Hybris也提供了自己的查询语句Flexsible Search。如ProductDao中使用的关联Category类型的查询:
用过ADBC和JDBC的ABAP顾问和Java开发者,对上面的代码一定不会陌生。
下面是从Jerry的博客里摘出来的一张图,ADBC和JDBC具体编码的对比。ADBC和JDBC里作用相同的语句,在图中以同样颜色的下划线标注,方便大家对比。
https://blogs.sap.com/2017/05/08/adbc-and-jdbc/
Hybris支持主流数据库,包括MySQL,Oracle,SQL Server及SAP HANA数据库等等。而Flexible Search概念的引入,思路类似ABAP Open SQL,通过编写不依赖于任何具体数据库提供商的Flexible Search代码,将Hybris应用层同底层数据库的具体实现做了解耦。而上面的语句中,POJO类里如ProductModel._TYPECODE 的值就是“Product”,是编译时自动生成的。因此查询语句可转译成如下文本:
SELECT COUNT( ) FROM = } WHERE =?category
上述语句问号后面的category是一个变量,即执行Flexible Search的方法的输入参数。
Flexible Search里这种问号的用法,和ABAP Open SQL里ABAP变量前的@是相同的。
到这里Hybris的Service层就基本介绍完了,而Hybris概要的系列文章也告一段落。希望大家通过这些文章对Hybris Enterprise Commerce Platform有一定的认识。感谢阅读。
领取专属 10元无门槛券
私享最新 技术干货