显示导航

365bet地区 GORM数据服务

在本指南中,我们将学习如何在365bet地区应用程序中创建GORM数据服务。

s尼拉夫·阿萨尔·塞尔吉奥·德尔阿莫

365bet地区版本 4.0.1

365bet地区培训

365bet地区培训由创建并积极维护365bet地区框架的人们开发和交付

入门

在本指南中,我们将深入探讨GORM数据服务在GORM中引入GORM数据服务通过添加使用GORM逻辑自动实现抽象类或接口的功能来消除已实现的服务层逻辑,从而减少了编写代码的时间,从而优化了编译时间并简化了测试

本指南将详细介绍如何通过示例365bet地区应用程序创建和使用GORM数据服务。该指南将始终关注与持久性保持一致的应用程序服务层

您将需要什么

要完成本指南,您将需要以下内容

  • 花些时间在你手上

  • 体面的文本编辑器或IDE

  • 安装了JDK或更高版本JAVA首页适当配置

如何完成指南

要开始,请执行以下操作

要么

365bet地区指南存储库包含两个文件夹

  • 初始初始项目通常是一个简单的365bet地区应用程序,其中包含一些其他代码,可以帮助您快速入门

  • 完成一个完整的示例它是按照指南中介绍的步骤进行操作并将这些更改应用于文档的结果。初始

要完成指南,请转到初始

  • 光盘进入grails指南grails gorm数据服务初始

并按照下一节中的说明进行操作

您可以直接前往完成的例子如果你光盘进入grails指南grails gorm数据服务已完成

编写申请

我们将使用域编写一个简单的应用程序地址这些域具有一对多关系。我们将开发执行查询和写入操作的GORM数据服务

域对象

创建地址域类他们具有一对多的关系

grails应用程序域示例grails Person groovy
例子进口 grails编译器365bet地区CompileStatic

365bet地区CompileStatic
  {

    那么整数年龄
地址(1) 静态的有很多地址地址
1 您通常不需要指定默认类型java有用集在一个有很多关联但是,在本教程的后面部分,我们将在JPA QL查询中查询关联,因此我们需要在此处进行明确说明
grails应用程序域示例grails地址groovy
例子进口 grails编译器365bet地区CompileStatic

365bet地区CompileStatic
 地址 {
    街道名称国家静态的属于

为什么选择GORM数据服务

自动实现接口或抽象类可减少编写的代码量。另外,GORM Data Services自动定义事务边界语义。例如,所有公共方法都标有交易性对查询方法只读

数据服务的优势

总结优势

  • 类型安全数据服务方法签名经过编译时检查,如果任何参数的类型与您的域类中的属性不匹配,编译将失败

  • 测试中由于数据服务是接口,因此使其易于模拟

  • 性能生成的服务是静态编译的,并且与Java空间中的竞争技术不同,不会创建代理,因此运行时性能不会受到影响,而且更新操作的执行效率很高

  • 正确定义事务语义用户通常没有正确定义事务语义。数据服务中的每个方法都包装在适当的事务中,对于只读操作,则为只读事务。

  • 支持多租户与结合当前租户注释GORM数据服务可简化多租户开发

要编写数据服务,请创建一个接口并使用进行注释grails gorm services服务与应用的领域类服务转换着眼于接口的方法签名以推测应该生成哪种实现。它着眼于返回类型以及实现功能的方法。数据服务约定详情

如果您过去使用过GORM,则很可能您曾经使用过动态发现者查询

动态查找器看起来像是静态方法调用,但是方法本身在代码级别实际上并不以任何形式存在,而是在运行时根据给定类的属性使用代码合成自动生成方法。

使用GORM Data Services,您可以使用与Dynamic Finders具有相同表现力的方法来创建接口,而没有缺点,例如,由于GORM Data Services是静态编译的,因此可以确保类型安全

找到一个通过动态查找器按名称使用人员findByName让我们实现一个数据服务以实现相同的查询

创建一个带有注释的接口进口grails gorm服务服务并声明具有相同签名的方法

grails应用程序服务示例grails PersonDataService groovy
例子进口 grails gorm服务查询

进口 grails gorm services服务

服务(1)
接口PersonDataService人员findByName那么(1)

    虚空删除可序列化ID(3)

}
1 合格服务与您要使用的域类
2 在方法中茎是它告诉GORM这是一个查询,因此将使用只读事务,并且name匹配域属性
3 方法删除需要ID

单元测试

您可以为以前的数据服务编写单元测试,如下所示

src测试groovy示例grails PersonDataServiceWithoutHibernateSpec groovy
例子进口 grails gorm Transactions事务性

进口 组织grails orm休眠HibernateDatastore
进口 org springframework测试注释回滚
进口 org springframework交易PlatformTransactionManager
进口 只是自动清理
进口 spock lang共享
进口 spock lang规格

交易性
  延伸规格共享 (2)共享 (2)
    自动清理 (3)HibernateDatastore hibernateDatastore共享 (2)PlatformTransactionManager transactionManager虚空setupSpec hibernateDatastoreHibernateDatastore人员(4)transactionManager hibernateDatastore getTransactionManager(5)个性化服务这个回滚 (6)
    虚空 "通过名字测试找到人"() {
        给定那么: "紧张", 年龄: 39保存什么时候"紧张")

        然后人名"紧张"人年龄39
    }
}
1 测试应扩展spock lang规格
2 共享注释用于向Spock指示HibernateDatastore, 个性化服务transactionManager属性在所有测试中共享
3 自动清理注释可确保在所有测试完成执行后关闭HibernateDatastore
4 setupSpec方法,使用域类构造一个新的HibernateDatastore,以用作构造函数的参数
5 通常,您必须将测试执行逻辑包装在会话或事务中。您可以从HibernateDatastore获取PlatformTransactionManager
6 通常,您需要使用Rollback注释来注释特征方法,该注释用于回滚每个测试中所做的任何更改

幸运的是365bet地区包括一个实用程序类您可以从中扩展并简化先前的单元测试

src测试groovy示例grails PersonDataServiceSpec groovy
例子进口 
进口 spock lang共享

 PersonDataServiceSpec 延伸休眠规范共享定义 设定personDataService hibernateDatastore getService PersonDataService虚空 "通过名字测试找到人"() {
        给定:
        那么: "紧张", 年龄: 39保存齐平: 真正)

        什么时候"紧张")

        然后人名"紧张"人年龄39

        清理personDataService删除人员ID

物业投影

向GORM数据服务中添加一个返回投影的方法,这具有仅返回一列而不是整个对象的优点。有几种实现投影的方法,一种方法是使用约定.

例如物业年龄域类该方法将是

grails应用程序服务示例grails PersonDataService groovy
整数找到人士那么

添加到单元测试以验证正确性

src测试groovy示例grails PersonDataServiceSpec groovy
虚空 "测试寻找人的年龄预测"() {
    给定人人那么: "紧张", 年龄: 39保存齐平: 真正)

    什么时候:
    整数年龄personDataService findPersonAge"紧张")

    然后39

    清理

保存操作

grails应用程序服务示例grails PersonDataService groovy
那么整数年龄

添加到单元测试以验证正确性

虚空 "测试保存人"() {
    什么时候"", 22)

    然后""人年龄22personDataService计数oldpersonDataService计数1 (1)

    清理
1 块被执行
gradlew检查.

grails gorm服务加入注解默认情况下,延迟加载365bet地区关联对于多对一的一对多关系加载可能会导致ñ许多选择语句导致的问题这可能严重影响性能

从本质上讲,我们可以使用加入将此概念应用于查询地址.

grails应用程序服务示例grails PersonDataService groovy
加入('地址') (1)那么

如下所示

grails应用程序服务示例grails PersonDataService groovy
例子进口 grails gorm服务查询

进口 grails gorm services服务
进口 grails gorm服务加入
进口 grails gorm Transactions事务性
进口 groovy util日志记录Slf j

接口IPersonDataService人员findByName那么整数找到人士那么计数('地址') (1)那么虚空删除可序列化那么整数服务抽象   实施交易性人员saveWithListOfAddressesMap那么整数年龄清单<地图<, 宾语地图地址街道名称m streetName ,
                米城 ,
                 ,
                国家米国 )
        }  清单
) } 交易性人员saveWithAddresses那么整数年龄清单
地址人人那么那么年龄年龄地址每个地址地址人addToAddresses地址保存人

创建一个集成测试

src集成测试groovy示例grails PersonDataServiceIntSpec groovy
例子进口 grails测试mixin集成集成
进口 spock lang规格

积分 (1)
 PersonDataServiceIntSpec 延伸规范PersonDataService personDataService(2)

    虚空 "测试加入渴望的负荷"() {
        给定人p personDataService saveWithListOfAddressesMap'紧张',39, [
                [街道名称: "主街", : "葡萄藤", : "得克萨斯州", 国家: "美国"],
                [街道名称: "珍珠街", : "奥斯丁", : "得克萨斯州", 国家: "美国"],
                [街道名称: "塞维克利山庄博士", : "塞威克利", : "功放", 国家: "美国"]
        ])

        什么时候人员人员人员数据服务findEagerly"紧张")

        然后人名"紧张"人年龄39

        什么时候:
        清单<城市人地址城市然后城市包含"葡萄藤"城市包含"奥斯丁"城市包含"塞威克利")

        清理personDataService删除ID
1 将您的集成测试放在src集成测试并用它们注释grails测试mixin集成集成.
2 注入数据服务

为了观察数据服务操作如何转换为SQL语句,我们将调整记录器配置

在里面logback groovy追加下一行以获取正在执行的SQL查询的更详细的输出

365bet地区应用程序配置会议日志回切
日志'组织休眠SQL'跟踪'标准输出'], 

执行整合测试gradlew integrationTest测试示例grails PersonDataServiceIntSpec

先前的测试输出了表明已发出联接的SQL日志语句,下面是一个示例

日志输出
DEBUG测试工作人员组织冬眠SQL插入人ID版本年龄名称值null DEBUG测试工作人员组织冬眠SQL插入人地址ID版本人员ID街道名城市国家/地区值null城市国家状态值null DEBUG测试工作人员组织休眠SQL插入地址ID版本人员ID街道名称城市国家状态值null DEBUG测试工作人员组织休眠状态SQL选择此ID作为ID版本号此年龄版本该年龄名称作为名称地址人员我作为人的身份证地址id为id地址id为id地址版本为版本地址人id为人i地址名称为街道n地址城市为城市地址国家为国家地址国家为人的状态来自此ID的左外连接地址地址为人id这个名字在哪里

如您所见,仅执行一个选择查询。

您可以在测试报告中看到这些语句构建报告测试类示例grails PersonDataServiceIntSpec html标准输出

JPA QL查询

JPA QL JPA查询语言查询查询实体使用类似SQL的文本查询语言的模型,查询使用实体属性和关系表示

GORM Data Service通过以下方式支持JPA QL查询:grails gorm服务查询注解

JPA QL查询允许进行更复杂的查询,尽管如此,它们仍然是静态编译的,可以防止SQL注入攻击

创建一个按国家/地区搜索人员的查询

grails应用程序服务示例grails PersonDataService groovy
    (1)
    询问("""\
选择不同的${p}${人p}加入${p个地址}一个国家$国家
""") (2)
    清单findAllByCountry国家(3)
1 询问用于定义JPA QL查询
2 您可以使用多行字符串来定义JPA QL查询,从而增加可读性
3 国家可以在HQL查询中使用参数

创建一个集成测试

src集成测试groovy示例grails PersonDataServiceIntSpec groovy
例子进口 grails测试mixin集成集成
进口 spock lang规格

积分 (1)
 PersonDataServiceIntSpec 延伸规范PersonDataService personDataService(2)

    虚空 "按国家测试搜寻人员"() {
        给定:
        [
                [那么: '紧张', 年龄: 39, 地址: [
                        [街道名称: "主街", : "葡萄藤", : "得克萨斯州", 国家: "美国"],
                        [街道名称: "珍珠街", : "奥斯丁", : "得克萨斯州", 国家: "美国"],
                        [街道名称: "塞维克利山庄博士", : "塞威克利", : "功放", 国家: "美国"]]],
                [那么: '', 年龄: 50, 地址: [
                        [街道名称: "橄榄街", : "圣路易斯", : "MO", 国家: "美国"],
                        [街道名称: "MLK大道", : "奥斯丁", : "得克萨斯州", 国家: "美国"]]],
                [那么: '塞尔吉奥', 年龄: 35, 地址: [
                        [街道名称: "卡勒市长", : "瓜达拉哈拉", : '卡斯蒂利亚拉曼恰', 国家: "西班牙"地图<, 宾语m personDataService saveWithListOfAddressesMap m名称 年龄 整数m个地址 清单<地图<, 宾语>>)
        }

        什么时候:
        清单usaPersons personDataService findAllByCountry"美国")

        然后usaPersons大小2找人那么"紧张"找人那么""}

        什么时候:
        清单西班牙人personDataService findAllByCountry"西班牙")

        然后西班牙人的大小1西班牙人发现那么"塞尔吉奥"}
    }

JPA QL投影

您还可以使用JPA QL为POGO Plan Groovy对象创建投影

如果您有POGO,例如

src main groovy示例grails国家groovy
例子进口 常规转换CompileStatic
进口 groovy变换TupleConstructor

元组构造函数 (1)
静态编译
 国家 {
    那么
1 使用Groovy元组构造函数AST转换以生成具有一个参数的构造函数

为创建GORM数据服务地址域类

grails应用程序服务示例grails AddressDataService常规
例子进口 grails gorm服务查询
进口 grails gorm services服务

服务地址接口AddressDataService询问("选择新的示例grails国家${一个国家}${地址a}通过...分组${一个国家}") (1)
    清单findCountries
1 构造函数由元组构造函数在JPA QL查询中使用

您可以创建一个单元测试来验证行为

src测试groovy示例grails AddressDataServiceSpec
例子进口 
进口 spock lang共享

 AddressDataServiceSpec 延伸休眠规范共享AddressDataService addressDataService共享定义 设定addressDataService hibernateDatastore getService AddressDataService personDataService hibernateDatastore getService PersonDataService定义 "对POGO工作的预测"() {
        给定:
        清单<longpersonIds[]
        [
                [那么: '紧张', 年龄: 39, 地址: [
                        [街道名称: "主街", : "葡萄藤", : "得克萨斯州", 国家: "美国"],
                        [街道名称: "珍珠街", : "奥斯丁", : "得克萨斯州", 国家: "美国"],
                        [街道名称: "塞维克利山庄博士", : "塞威克利", : "功放", 国家: "美国"]]],
                [那么: '', 年龄: 50, 地址: [
                        [街道名称: "橄榄街", : "圣路易斯", : "MO", 国家: "美国"],
                        [街道名称: "MLK大道", : "奥斯丁", : "得克萨斯州", 国家: "美国"]]],
                [那么: '塞尔吉奥', 年龄: 35, 地址: [
                        [街道名称: "卡勒市长", : "瓜达拉哈拉", : '卡斯蒂利亚拉曼恰', 国家: "西班牙"地图<, 宾语m personIds personDataService saveWithListOfAddressesMap m名称 年龄 整数m个地址 清单<地图<, 宾语ID什么时候:
        清单国家addressDataService findCountries然后国家国家大小2任何国家那么'美国'任何国家那么'西班牙' }

        清理personIds每个personDataService删除)
        }
    }
}

结论

只需定义接口,即可允许GORM为您实现方法并管理事务。

GORM数据服务文档以了解更多信息

在Greach Conf上观看Graeme Rocher的演讲

帮助365bet地区

OCI赞助了本指南的创建OCI提供了几种365bet地区服务:

免费咨询

OCI 365bet地区团队包括365bet地区联合创始人Jeff Scott Brown和Graeme Rocher检查我们的365bet地区课程并向发展和维护365bet地区的工程师学习

Grails OCI团队