
当您阅读供内部使用的下一个公司Web应用程序的要求时,通常(根据我的经验)是一组相同的:用于存储数据的关系数据库,通常从该应用程序的先前版本继承而来,具有许多不同难度级别的形式(但是同时(典型的)数据输入,各种报告形式,复杂的业务逻辑,与其他应用程序的集成-从会计到供应管理,几千个同时工作的用户。 通常会想到什么?
“因此,我将使用我所知道的适合其数据量的DBMS,固定Hibernate / JPA,在Spring Boot上编写应用程序,公开REST API并在JS框架上编写客户端,最有可能是Angular / React。”
“是的,我们仍然需要拧紧Spring Security。 并在数据库行或数据对象级别对不同部门和角色的数据访问进行限制。 怎么做? 提交或VPD,您需要观看。”
“呃,我必须写一堆DAO-它们很快完成,但是有很多。”
“以及从Entity到DTO的转换-我正在使用ModelMapper 。”
“最主要的是不要忘记提醒学员关于惰性属性和联接的信息,这样就不会像上次那样。”
“枞树,当您开始业务逻辑时,您需要编写那么多服务代码……”
本文适用于那些已经在Spring / Spring Boot上从头开始编写了至少两个企业应用程序的人,并且现在认为以某种方式加快此类不同但同时相似的应用程序的开发会很不错。 接下来,我们将看到如何使用
CUBA Platform摆脱“典型”任务。
另一个框架?

第一,当为开发人员提供新框架(尤其是CUBA)时,它是:“我为什么要为此烦恼? 我将熟悉熟悉的Spring Boot,并完成所有工作。” 这是合理的。 新框架是对新开发方法,新陷阱和局限性的研究,您已经学到了在熟悉的框架中巧妙地避免的局限性。
但是,当我开始在CUBA上进行开发时,我不必对Spring进行太多改动。 自然,我不得不花一点时间来了解平台,但这并不是所有习惯的根本破坏,而是开发技能的微小转变。
结果,用于DTO,逐页数据输出,数据过滤(对传递到表单的参数的分析和请求的编译)的代码消失了。 同时,我几乎不必修改Spring Security设置并编写管理代码,登录表单和用户界面语言切换逻辑。
让我们从头开始,看看CUBA开发与在Spring上进行常规开发的方式相比,以及如何利用您的经验并学习一些新知识,最终可以更快地创建应用程序。
本文的主要重点是服务器端的开发,以免失去重点并使文本量保持在可接受的水平。
春季应用架构
在90%的情况下,输入Google“ Spring Application Architecture”或“ Spring Application Architecture”,您会看到类似的图片。 经典的三层应用程序,某些层共有模块。
域模型(域模型) -
域的实体类(实体),通常存储在数据库中。 类通常是手动创建的,但是您可以基于数据库模式自动构建结构。
存储库层(Repository Layer) -提供与数据仓库一起工作的一组类。 通常,该层使用各种ORM框架,并包含用于执行CRUD操作的逻辑。 如果我们谈论Spring,则存储库非常紧凑,这主要是由于JPA Query方法所致,但是通常您必须从数据库中编写选择逻辑并将其手动映射到域模型。
服务层 -
服务层,其中包含业务逻辑的实现,特定于主题领域的信息处理算法。 这对于复杂的处理算法以及处理来自各种来源(数据库,外部应用程序,Internet服务等)的数据很有用。
Web /控制器层 -负责REST API的类。 如果我们使用Thymeleaf,Velocity之类的框架,以及使用诸如GWT,Vaadin,Wicket等之类的负责渲染和处理用户界面事件的类,则页面模板文件也可以位于此层。

通常,控制器使用DTO,而不使用域层中的类,因此将DTO转换为Entity(反之亦然)的功能会添加到应用程序中。
如果以上所有内容对您来说都是显而易见的,则甚至“常规船长”都是出色的选择。 因此,您可以毫无问题地开始使用CUBA。参考应用-宠物诊所
让我们看一个特定的示例并编写一些代码。 对于Spring,在
GitHub上有一个“参考”应用程序-Pet Clinic。 此应用程序是使用不同的工具以不同的版本创建的-从经典的Spring到Kotlin和Angular。 接下来,我们将看到如何在CUBA上制作此应用程序。 对于那些不熟悉Spring版本的人,这里有不错的文字; 摘录将在本文中使用。
资料模型

ER图如上图所示。 域模型重复此结构,向其中添加了几个具有公共字段的类,并且实体类已经从它们继承。 UML可以在我前面提到的
演示文稿中找到。
储存库层
应用程序中有四个存储库,负责与实体所有者(所有者),宠物(宠物),访问(访问)和兽医(兽医)合作。 存储库是使用Spring JPA编写的,实际上不包含任何代码,仅包含方法声明。 但是,已向与Owner实体一起使用的存储库中添加了一个查询,该查询允许在一个示例中从数据库中提取所有者及其宠物。
使用者介面
宠物诊所有九页,可让您查看所有者列表,他们的宠物和兽医列表。 该应用程序提供了一个简单的CRUD功能:可以编辑一些数据-主人,宠物,还可以增加对诊所的访问。 但是,正如已经提到的,我们不会在本文中深入讨论用户界面。
选配
除了使用实体进行简单操作的代码外,该应用程序还具有一些有趣的功能,旨在展示Spring的功能:
- 缓存-仅从数据库中选择一次兽医列表,然后将其存储在缓存中,直到应用程序服务器重新启动。
- 输入有关新宠物的信息时,请检查必填字段的填写情况。
- 在显示动物之前先对其格式进行格式化。
- i18n-该应用程序支持英语和德语。
- 事务管理-一些事务被标记为只读。
边注

我真的很喜欢这张照片。 它100%反映了我在使用框架时的感受。 为了有效地使用框架,您必须了解它是如何内部构建的(无论如何)。 例如,你们中有多少人想知道要使JPA接口工作需要多少个类?
即使在像Pet Clinic这样的小型应用程序中,也有一些Spring Boot的魔力:
- 缓存没有配置(
@Caheable
注释除外),但是,Spring Boot“知道”如何启动所需的缓存(在这种情况下为EhCache)。 - CRUD存储库未使用
@Transactional
批注标记(它们的父类也是org.springframework.data.repository.Repository
),但是所有save()
方法均按预期工作。
但是,尽管有很多“隐含”,Spring Boot还是很受欢迎。 怎么了 它是透明且可预测的。 好的文档和开源代码可以帮助您理解原理,并在必要时深入研究实现细节。 在我看来,每个人都喜欢这样的框架,透明度和可预测性-这是应用程序稳定性和可维护性的关键。
CUBA平台上的宠物诊所
好吧,让我们从Spring开发人员的角度看一下使用CUBA制作的Pet Clinic,并尝试了解可以在哪里保存。该应用程序的源代码可以在
GitHub上
找到 。 此外,CUBA平台还有非常不错的俄语和英语
文档 ,详细说明了如何在该平台上正确开发。
GitHub上的许多示例,包括一些教程。 在本文中,我将经常参考文档,以免重复编写同一本书。
CUBA应用架构
CUBA应用程序由图中所示的模块组成。
全局(全局模块) -包含在不同模块中使用的实体类,CUBA表示形式和服务接口。
核心(服务模块) -在此处放置实现业务逻辑的服务代码以及与数据仓库一起工作的代码。 应当注意,这些类无法从其他应用程序模块中获得,这样做是为了提供
单独的部署以获得更好的可伸缩性。 为了在其他应用程序模块中使用服务,您需要使用在全局模块中声明的接口。
GUI,Web,桌面,门户 -这些模块包含与用户界面事件处理相关的类的代码,以及如果
内置的 CUBA
REST API还不够的话,还包括其他REST控制器。
为了节省开发人员的时间,CUBA拥有一个
工作室 -一个小型的图形开发环境,可帮助进行例行工作,例如生成实体,在配置文件中注册服务等。 使用图形界面。 要生成已开发应用程序的界面,有一个(几乎)所见即所得的编辑器。
因此,基于CUBA平台的应用程序包含两个主要模块-核心和GUI(可单独部署)以及一个通用模块-全局。 让我们仔细看看这些模块的设计。
全局模块
实体模型
CUBA应用程序中的实体建模与Spring开发人员所使用的相同。 创建域类,并在其上添加@
@Table
,@
@Entity
等注释。 然后,将这些类注册在
persistence.xml
文件中。
编写Pet Clinic时,我只是从原始Spring应用程序中复制了类,并对其进行了一些更改。 如果您是在CUBA平台上编写的,则需要了解以下一些小附加信息:
- CUBA为应用程序的每个组件引入了“ 名称空间 ”的概念,以避免在数据库中重复表名。 这就是为什么将前缀“ petclinic $”添加到每个实体的原因。
- 建议使用注释
@NamePattern
- toString()
方法的类似物,以便在用户界面中可读地显示实体。
自然的问题:“除了前缀和声明性的
toString()
以外,CUBA还添加了什么?” 以下是其他功能的部分列表:
- 具有从Integer到UUID的自动生成ID的基类。
- 有用的标记界面,提供其他功能:
Versioned
-支持实体实例的版本控制。SoftDelete
支持“逻辑”删除数据库中的记录。Updatable
-添加字段以注册更新记录,用户名和时间的事实。Creatable
-添加字段以注册记录的创建。
您可以在文档中阅读有关实体建模的更多信息。 - 使用CUBA Studio自动生成用于创建和更新数据库的脚本。
因此,为Pet Clinic创建数据模型归结为复制实体类并向它们添加CUBA特定的内容,这在我上面已经提到。观看次数
CUBA中的“表示形式”概念似乎有些不寻常,但很容易解释。 视图是一种声明属性的声明方式,该属性的值需要从数据仓库中检索。
例如,您需要从数据库(或兽医及其职业)中提取所有者和他们的宠物-创建界面时的一项非常常见的任务是,当您需要以与主界面相同的形式显示相关数据时。 在春季,这可以通过JPA连接来完成。
@Query("SELECT owner FROM Owner owner left join fetch owner.pets WHERE owner.id =:id") public Owner findById(@Param("id") int id);
...或设置EAGER / LAZY属性以在单个事务的上下文中检索主实体的集合。
@ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = "vet_specialties", joinColumns = @JoinColumn(name = "vet_id"), inverseJoinColumns = @JoinColumn(name = "specialty_id")) private Set<Specialty> specialties;
在CUBA中,您可以通过EntityManager和JPQL(人人都知道)或通过视图和DataManager来做到这一点:
- 形成演示文稿(可以在CUBA Studio中完成,也可以在配置中手动进行)
<view class="com.haulmont.petclinic.entity.Vet" extends="_minimal" name="vet-specialities-view"> <property name="specialities" view="_minimal"> </property> </view>
- 并使用DataManager获取:
public Collection<Vet> findAll() { return dataManager.load(Vet.class) .query("select v from cubapetclinic$Vet v") .view("vet-specialities-view") .list(); }
您可以使用所需的属性集和实体的嵌套级别为不同的任务创建不同的视图。 关于Mario David的
博客 文章,有一篇很棒的
文章 。
Pet Clinic应用程序提交了六种不同的提交方式。 它们中的大多数是在创建用户界面时“半自动”生成的,并且上述视图是为数据导出服务实现的。服务接口
由于全局模块由所有其他应用程序模块使用,因此在其中声明了服务接口,以便以后可以通过依赖项注入(DI)机制在其他模块中使用它们。
另外,您需要在Web模块的
web-spring.xml
中注册服务。 初始化上下文时,CUBA将创建代理类,以便在与Core模块中的服务进行交互时对类进行序列化和反序列化。 这仅提供了Core和Web模块的单独部署,而开发人员无需付出额外的努力,则所有工作都是透明地完成的。
因此:在CUBA中创建实体类时,所花费的时间与在纯Spring中花费的时间相同(如果您不使用CUBA Studio),但是您无需创建用于生成主键的基类。 另外,您无需编写代码来支持实体版本字段,逻辑删除和审核。 另外,我认为,视图可以节省一些时间来调试JPA join和操作EAGER / LAZY示例。核心模块
该模块包含在全局模块中声明的接口的实现。 CUBA应用程序中的每个服务都由
@Service
注释,您可以使用其他Spring注释,但是需要考虑以下几点:
- 如果希望该服务在其他模块中可用,则必须设置
@Service
批注。 - 如果为实现相同接口的应用程序中出现组件,建议对服务进行命名,以避免重复。
- 数据采样的方式与Spring有所不同,更多内容将在下一部分中进行。
否则,核心模块是常规的Spring代码。 您可以从数据库中选择数据,也可以调用外部Web服务,通常,您可以按照习惯的方式编写代码。
实体管理器和数据管理器
CUBA平台使用其自己的
EntityManager ,它将部分调用委派给熟悉的
javax.persistence.EntityManager
,但未实现此接口。 EntityManager主要提供底层操作,并且不完全支持CUBA安全模型。 处理数据的主要类是
DataManager,它提供以下功能:
- 在行和属性级别的访问控制。
- 在获取数据时使用视图。
- 动态属性。
文档中还包含有关DataManager和EntityManager的更多信息。 无需显式地使用这些类将数据选择到用户界面组件(表等)中,而在GUI中使用
数据源(Datasources) 。
如果我们谈论PetClinic-核心模块中几乎没有代码,则此应用程序中没有复杂的业务逻辑。从宠物诊所到CUBA的其他功能
在上一节中,参考应用程序中考虑了其他功能。 由于CUBA使用Spring,因此在基于此平台进行开发时,也可以使用相同的功能,但是您将不得不摆脱一些Spring Boot的魔力。 除此之外,CUBA还提供类似的现成功能。
快取
CUBA平台具有查询缓存和实体缓存。 在
文档中对它们进行了详细描述,如果您想在应用程序中使用缓存,可以将它们视为优先解决方案。 嵌入式解决方案支持分布式应用程序部署和群集。 好吧,当然,您可以使用
@Cacheable
批注(如
Spring文档中所述) ,如果内置缓存不适用于某些东西。
输入检查
CUBA使用
BeanValidation来验证输入的数据。 如果内置类未提供必要的功能,则可以编写
自己的check逻辑 。 而且,除此之外,CUBA还提供了
Validator
类,在此进行介绍。
输出格式
CUBA平台为用户界面组件提供了几种
格式化程序 ,除了标准之外,您还可以
自己制作。 您可以使用
@NamePattern
批注将实体实例表示为字符串
i18n
CUBA使用
message.properties
文件支持
多语言输出 ,这里没有新内容。 CUBA Studio使创建和编辑此类文件变得快速而轻松。
交易管理
支持以下类型的事务管理:
- 熟悉Spring注释
@Transactional
, Persistence
接口(和类)(如果需要低级事务管理)。
当我写《宠物诊所》时,我只需要在一个地方进行交易管理:当我开发了一个输入表单,使我可以在一个屏幕上编辑多个相关实体时。 必须编辑主人及其宠物,以及增加对诊所的访问。 必须仔细存储数据并在其他屏幕上进行更新。数小时内即可到达宠物诊所。 真的
我在六个小时内用标准的CUBA界面制作了Pet Clinic的副本。 并不是说我是CUBA的专家(自从我在Haulmont工作以来已经过去了几个星期),但是我在Spring方面有经验,并且对我有很大帮助。
让我们从经典架构的角度看一下CUBA应用程序:
数据模型 -全局模块中的实体类。 创建模型是众所周知的过程。 多亏了BaseIntegerIdEntity类,它节省了通常花时间摆弄生成ID的时间。
不需要
存储库层 。 我创建了几个视图,仅此而已。 当然,CUBA Studio为我节省了一些时间,而我不必手动编写XML文件。
服务层 -应用程序只有两个用于将数据导出到JSON和XML的服务。 顺便说一下,在当前版本的Spring Boot应用程序中,此功能已被删除。 尽管它不适用于JSON。 在CUBA版本中,我在全局模块中声明了接口,并将实现放在Core中。 除了DataManager没什么新奇的,但是掌握它的时间很少。
控制器层 -CUBA Pet Clinic只有一个REST控制器可导出到JSON和XML。 我将此控制器放置在Web模块中。 同样,没有什么特别的,注释是一样的,一个常规的Spring控制器。
用户界面 -制作标准CRUD表单,甚至使用CUBA Studio都不会造成任何困难。 无需编写代码即可将数据传输到组件,无需从数据过滤表单中解析数据,也无需分页。 所有这些都有组件。 花一些时间使界面看起来像Spring Boot版本中的界面。 Vaadin仍然不是纯HTML,而且样式很难。
个人经验表:
当然,Pet Clinic并没有使用CUBA的所有功能,可以在
站点上找到完整的平台功能列表,还可以找到用于解决开发企业应用程序时出现的典型问题的代码示例。
我个人认为,CUBA简化了应用程序服务器端的开发,并且如果您使用标准的用户组件和样式功能,则可以帮助节省更多的时间来开发用户界面。 但是,即使您需要一些特殊的用户界面,由于标准的服务器端,仍然会节省时间。一加! 有什么缺点吗?
当然,没有完美的框架。 它们并不重要,但是起初,当我刚开始使用CUBA时,会有一些不适。 但是
,WTF / m的
值并不高。
- 对于CUBA平台,有一个IDE可以简化项目的初始创建。 首先,在Studio和IDEA之间切换有点烦人。 好消息是,有一个beta版的CLI,您不需要运行Studio即可生成项目结构,而且-CUBA Studio的下一个版本将是Intellij IDEA插件。 没有更多的切换。
- CUBA比普通的Spring Boot应用程序具有更多的XML文件,这是因为CUBA中的上下文初始化是以其自己的方式完成的。 现在正在努力减少XML的数量,在可能的情况下我们转向注释。
- 用户界面表单没有“可读” URL。 可以通过到屏幕的链接来访问表单,但是它们对用户的友好程度不是很高。
- 要使用数据,您需要使用DataManager,视图和EntityManager,而不是Spring JPA或JDBC(但如果需要,也可以使用这些API)。
- CUBA最适合将关系数据库用作数据仓库。 对于NoSQL,您将必须对这些存储库使用访问库,并编写自己的存储库层。 但我认为,这与在不使用CUBA的情况下开发应用程序的工作量相同。
合计
如果要开发使用关系数据库作为数据仓库并专注于以表格格式处理数据的应用程序,那么CUBA是一个不错的选择,因为:
- CUBA是透明的。 源代码可用,任何方法都可以调试。
- CUBA是可扩展的 (当然, 扩展到已知的限制)。 您可以继承几乎所有实用程序类,并将其放到平台上,制作自己的REST API,并使用自己喜欢的框架进行用户界面。 创建 CUBA的目的是使您可以轻松地为任何客户调整解决方案。 有一篇关于平台可扩展性的好文章。
- CUBA是春天。 您80%的服务器代码将只是Spring应用程序。
- 快速入门。 创建第一个实体后,您将拥有一个带有管理面板的功能完善的应用程序,以及用于使用它的屏幕。
- 该平台已解决了许多例行任务。
因此,借助CUBA,您可以节省编写单调的“服务”代码的时间,并专注于编写解决业务问题的代码。 是的,Haulmont的我们自己使用CUBA来开发盒装和定制产品。