“我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第2篇文章,点击查看活动详情”
介绍
我们已经构建了 Soccer Squad 应用程序的基础。在本章中,我们将构建一个新的功能,允许 Adrian 在比赛期间保持得分,然后将其存储在数据库中。
为此,我们将使用非持久实体。我们可能以前没有听说过的术语,所以让我们先深入了解可持久实体和不可持久实体之间的区别背后的理论!
非持久化实体
领域模型中实体的“持久性”属性定义了对象是否可以存储在数据库中。我们需要更详细地来学习实体可持久化的含义以及相关术语“瞬态”的含义。
持久化和非持久化
当一个实体被声明为可持久化时,会为此实体创建一个数据库表。提交此类实体的实例会导致将一行插入此表中。该实例中存储的属性和关联信息也保存在数据库中。
因此,在没有意识到这一点之前,您一直在使用可持久化的实体:可以存储在数据库中的信息。现在让我们来看看当一个实体是不可持久的时它意味着什么!
非持久实体不能存储在数据库中,因此没有关联的数据库表。虽然可以提交一个不可持久的实体,但提交只将当前属性值和关联值存储在内存中,而不是存储在数据库中。在会话结束时(当 Adrian 暂时停止使用该应用程序时),所有非持久性信息都将被垃圾收集,这意味着它将被删除。
瞬时对象
创建对象时,这是在内存中完成的。这意味着此时的对象只存在于内存中,而不存在于数据库中。这就是所谓的瞬时对象。创建对象时,如果存在此类型的属性,则不会访问数据库,但检索自动编号信息除外。因为非持久化实体没有数据库表,所以它们也不能有自动编号属性。
非持久化实体的关联
当我们在非持久实体和可持久实体之间创建关联时,关联的所有权(带点的一侧)必须始终仅位于非持久实体的一侧。这意味着有两种可能的关联,如下图所示:
我们不难发现非持久实体如何具有不同的颜色?这是我们无需打开属性屏幕即可轻松识别实体是否可持久化的方法。
其他限制
你现在应该知道的关于不可持久实体的最后一件事是,它们不能在域模型上具有验证规则并且不能具有索引(因为这是数据库功能)。
为什么要使用非持久化的实体?
在阅读了非持久实体的规则和限制之后,您可能想知道为什么还要使用它们。好吧,有时数据不应该最终出现在数据库中。这可能是因为它是流程数据(连接到交易),或者因为它是敏感信息,法律上不能存储。无论哪种方式,这些数据都不应该能够提交到数据库。
但是你为什么不只是不提交一个可持久的实体呢?你可以这样做,但很容易忘记,然后你可能会遇到麻烦。此外,您将不必要地创建数据库表,使您的数据库变得混乱!所以,基本的操作是:如果对象不属于数据库,它们应该是一个非持久的实体!
开始完成我们的实操场景
保存比赛分数
Adrian 希望能够保留正在进行的比赛的分数。为了防止不断更新数据库(因为阿德里安希望他的球队能进很多球),这些实时比分应该只在比赛完成后才进入数据库。为此,您将创建一个不可持久的score 实体,您可以使用它来保持实时score实体。比赛结束后,最终结果将被推送到数据库中。
扩展领域模型
- 打开足球小队领域模型。
- 在Match和Goal实体的右侧放置一个新实体。
- 打开新实体的属性并使用下图进行配置:
扩展领域模型
-
打开足球小队域模型。
-
在Match和Goal实体的右侧放置一个新实体。
-
打开新实体的属性并使用下图进行配置:
-
添加从Score实体到Match实体 的一对多关联。
-
添加从Score实体到Goal实体 的多对多关联。
-
这就是您的领域模型现
建立评分系统
- 打开 Soccer Squad SNP_Match_Details 片段。
- 在布局网格的正上方放置一个容器,其中包含显示球队球员的列表视图。 为容器中的Score实体 添加一个数据视图。
- 使用以下表达式将容器的可见性设置为基于表达式:currentObject/StartDateAndTime <= [%CurrentDateTime%]
- 使用下图配置数据视图:
- 看看数据 源如何是微流?使用以下图像构建DS_Match_GetScore微流:
- 将数据视图设置为如下图所示:
在每一列中,第一个参数应显示球队的名称,第二个参数应显示进球数。
-
是时候给这个页面添加一些逻辑了!两个 +1按钮应将主队或客队的得分加一。它还应该打开一个弹出页面 ( Goal_NewEdit ),Adrian 可以在其中填写谁进了球,以及比赛的时间。立即创建此页面。
- 此页面上的保存按钮不应是默认按钮,否则数据库仍会更新。改为关闭页面按钮。目标对象将保留在内存中,直到 Adrian 最终确定得分。
-
将左侧 +1按钮的On click 操作设置为 Call a microflow。此微流程应命名为ACT_Score_GoalHomeTeam。使用以下图像构建此微流:
确保将NewGoal对象传递给Goal_NewEdit页面。
-
将底部 +1按钮的On click 操作设置为 Call a microflow。此微流应命名为ACT_Score_GoalAwayTeam。使用上面的图片来构建这个微流,但是在它显示 TeamHome或HomeTeam的地方,将其更改为客队。********************
-
当 Adrian 单击Finalize score按钮时,Score对象应转换为存储在Match对象中的字符串值。您将使用微流进行此操作:
- 将按钮的On click 操作设置为 Call a microflow。
- 创建一个新的微流并将此微流命名为ACT_Match_FinalizeScore。
- 使用下图构建微流。它将分数设置为字符串值并提交Match。它还将匹配的所有目标提交到数据库。
数据模型中的计算属性
前面我们了解了如何通过使用非持久化实体来让数据不保存到数据库中。现在我们将研究另一种不在数据库中输入数据的方法,即始终使用计算属性即时计算数据。
计算属性在领域模型中定义,并在访问时获取其值。属性的值是使用选择的微流确定的。在定义域模型中的计算属性时选择此微流,并在每次后续检索属性时执行。这确保了值始终是最新的,但其代价是需要在实体的任何访问上执行此微流。此外,这些值在查询数据库时不能直接使用,因为这些值在数据库中不存在。
因此,最好的做法是尽量减少计算属性的使用,因为它们会在每次检索和列表检索的每行时触发。这意味着每次向用户显示信息或在微流中使用信息时,都会触发微流来计算值。尤其是在列表视图中显示计算属性时,这可能会损害应用程序的性能。
因此,您应该始终尝试将派生值尽可能多地存储在数据库中,也许可以通过使用事件处理程序在新派生值更改时自动提交它。您可能还记得快速开发学习路径中的这个技巧。在这里,您使用事件处理程序来存储注册总数,无论何时将注册添加到数据库或从数据库中删除。
但是如果一个值经常变化呢?那么使用计算属性可能是个好主意。经验法则是:如果它的变化比您查询的要多,请使用计算属性。如果您查看它的次数多于值的变化,请将其存储在数据库中。
您可以通过名称后面的小微流图标识别域模型中的计算属性:
显示播放的分钟数
Adrian 想到了一个新的很酷的功能来添加到应用程序中:显示一场比赛已经进行的分钟数。该值每分钟都在变化,因此最好为此使用计算属性。当然,您只会为当前正在玩的比赛显示此值,而不是未来或过去的比赛。此外,阿德里安还想在中场休息时展示。
扩展领域模型
-
打开足球小队域模型。
-
将新的MinutesPlayed属性添加到Match实体。
- Type: String
- Value: Calculated
- Pass entity: Yes
- 微流:CAL_Match_CalculateMinutesPlayed(您仍然需要创建此微流)。
计算上场时间
- 打开CAL_Match_CalculateMinutesPlayed微流程。
- 看看 end event String类型的返回值?这将是MinutesPlayed属性的计算值。
- 使用下图构建微流。选择的第一个 StartDateAndTime 检查是为了防止微流在创建新匹配时崩溃(因为此时 StartAndDateTime 属性将为空)。
显示比赛的时间
- 打开Match_Details片段。
- 复制显示最终score的容器并将其直接粘贴到现有容器的上方。将显示的值更改为MinutesPlayed。像这样:
- 调整条件可见性以使用以下表达式:currentObject/StartDateAndTime <= [%CurrentDateTime%]
总结
在本章中,我们了解了处理仅存在于内存中的数据的两种不同方法:非持久实体和计算属性。
在下一个章中,我们一起来学习一下,如何完成数据模块的跨模块实体的关联,这样可以帮助我们更好的进行系统的模块切分,根据不用的业务来设计不同的功能模块了。