介绍

GORM是一个具有多个后端实现的数据访问框架,使您可以轻松编写自己喜欢的数据库的数据访问代码

当前有几种GORM的实现。此文档涵盖了基于Hibernate ORM的GORM的原始实现。您可以在下面找到其他实现的链接

如前所述,用于Hibernate的GORM是GORM的原始实现,并且在过去的几年中已从几个元编程功能急剧演变为具有针对不同数据存储关系和NoSQL的多种实现的完整数据访问框架。

发行历史

GORM支持最新版本的关键依赖性,包括

  • Java最低Java支持

  • 最低休眠

  • 春季最低

GORM包括对GORM的各种增强,包括

  • GORM数据服务

  • 多租户转换

  • 支持Bean验证API

  • 内置包裹扫描

  • JPA注释映射支持

  • 用于脏检查受管实体的Hibernate转换等

  • HQL SQL查询转义为GString查询

看到GORM的新功能指南以获取更多信息

GORM继续发展新的基于特征的模型,并包括以下新功能

  • 支持MongoDB x驱动程序

  • 支持Neo j x驱动程序

  • 所有实现中的统一配置模型

  • 对Hibernate MongoDB和Neo j的统一多个数据源支持

  • 对Hibernate MongoDB和Neo j的多租户支持

  • 用于MongoDB的RxGORM基于MongoDB Rx驱动程序构建

  • 基于RxNetty的REST的RxGORM

GORM取代了支持GORM的大多数自定义AST转换时髦特质.

添加了对MongoDB x驱动程序Neo j x和Hibernate x的支持

365bet地区GORM继续将GORM API与365bet地区核心API分开,并且是第一个在365bet地区之外支持独立执行的版本

365bet地区GORM与365bet地区一起发布,还具有针对Spring Boot的自动配置启动器

引入了对MongoDB x驱动程序Neo j x和Hibernate x的支持

365bet地区GORM是GORM的第一个版本,它是与365bet地区框架本身分开发布的,并在365bet地区中引入

重构了更多的元编程功能,并将其替换为AST转换,并引入了API,这些API使GORM可以针对包括MongoDB在内的多种数据库实现进行操作

GORM API与Hibernate分离,并发布了SDK和TCK以构建兼容的实现

365bet地区GORM作为365bet地区的一部分发展而来,并将依赖于ExpandoMetaClass的一些元编程逻辑重新设计为一组Groovy AST转换.

365bet地区这些AST转换依赖于365bet地区 x编译器基础结构,因此该版本的GORM也只能在365bet地区中使用

GORM的第一个版本是一系列基于Groovy s功能的元编程功能。ExpandoMetaClass.

它纯粹是动态的,并已集成到365bet地区365bet地区框架365bet地区且在没有365bet地区的情况下无法使用,因此该版本没有实际发行版本,仅作为365bet地区的一部分提供

升级说明

依赖升级

GORM支持Java Hibernate x和Spring x的最低版本

这些基础组件中的每一个都可能具有需要更改应用程序的更改,这些更改超出了本文档的范围。

打包重组和弃用

此版本中已删除了先前不推荐使用的类,并且为了将来支持Java模块,已经进行了一些软件包重组

代理处理的更改

GORM不再创建自定义代理工厂,也不自动解开Hibernate代理

这使其与常规Hibernate的行为方式更加一致,并降低了框架级别所需的复杂性

您可能需要更改实例在某些情况下,检查是手动打开代理的

模组验证弃用并删除

在GORM x中验证不推荐使用该模块,并替换为grails资料储存库gorm验证.

维护了不推荐使用的接口以实现向后兼容性。在GORM中,已删除了这些不推荐使用的类,并且所有对验证已移除

现在所有操作都需要交易

以前的Hibernate版本允许在没有声明事务的情况下执行读取操作

休眠及以上版本要求存在活动事务。如果看到javax持久性TransactionRequiredException例外,这意味着您的方法缺少交易性周围的注释

入门

365bet地区要在365bet地区中将GORM用于Hibernate,可以在以下位置指定以下配置:建立gradle:

依赖项编译org grails插件hibernate编译org hibernate休眠ehcache

365bet地区如果您使用的365bet地区版本早于此版本,则可能需要强制执行GORM版本。如果您使用的365bet地区或更高版本,则可以通过修改gormVersion设置gradle属性:

gormVersion发布

365bet地区否则,如果您使用的是较早版本的365bet地区,则可以通过在以下代码的正上方添加以下代码块来强制执行GORM版本:依存关系

建立gradle
配置所有解决方案策略eachDependency DependencyResolveDetails详细信息是否请求了详细信息组org grails详细信息请求的名称开始了with grails数据存储详细信息useVersion RELEASE依赖关系

常见问题

如果收到指示无法解决问题的错误grails数据存储简单依赖性,您可能需要将以下内容添加到建立gradle正上方依存关系

建立gradle
配置全部排除模组:'grails数据存储简单'
}

配置不同的休眠版本

365bet地区使用GORM RELEASE时,根据365bet地区的版本,您必须对构建进行各种更改。

365bet地区Hibernate的365bet地区 x及以上

365bet地区365bet地区 x基于Spring Boot x,后者将Hibernate x强制为默​​认版本。如果要继续使用Hibernate,必须在以下位置显式声明Hibernate依赖项:建立gradle.

建立gradle
依赖项编译"org grails插件休眠"编译"组织休眠休眠核心决赛"编译"组织休眠休眠ehcache最终版"
}

365bet地区使用Hibernate的365bet地区 x及以下

365bet地区365bet地区 x及以下版本基于Spring Boot x,后者将Hibernate强制为Hibernate的默认版本,因此您必须使用显式版本来依赖Hibernate

建立gradle
依赖项编译"org grails插件休眠"编译"组织休眠休眠核心决赛"编译"组织休眠休眠ehcache最终版"
}

365bet地区365bet地区 x Spring版本冲突

365bet地区365bet地区 x将Spring x强制为Spring版本,因此,如果要使用Hibernate,则必须强制升级到Spring x in建立gradle:

建立gradle
365bet地区以下内容在365bet地区及更高版本中是不必要的,但在365bet地区 x中是必需的配置所有解决方案策略eachDependency DependencyResolveDetails详细信息如果详细信息要求组'org springframework'详细信息useVersion'发布')
            }
        }
    }
}

解析策略365bet地区强制执行Hibernate支持所需的对Spring x的升级,如果使用365bet地区或更高版本,则不需要此块

在Spring Boot中使用GORM

要在Spring Boot中将GORM用于Hibernate,请将必要的依赖项添加到Boot应用程序中

编译org grails gorm冬眠春季引导发布编译org冬眠休眠核心编译org冬眠休眠ehcache运行时com h数据库h for MySQL运行时mysql mysql连接器java用于连接池运行时org apache tomcat tomcat jdbc runtime org apapache tomcat嵌入tomcat嵌入日志日志j

然后确保您已配置根据Spring Boot指南的数据源和Hibernate以MySQL为例

:
    hbm ddl:
        汽车: 更新
    方言: org休眠方言MySQL InnoDBDialect
弹簧:
    数据源:
        driverClassName: com mysql jdbc驱动程序
        网址:   jdbc mysql gorm
        用户名: 
        密码: ""
365bet地区如果您更喜欢使用365bet地区方式配置数据源dataSource网址等等,那么你可以添加EnableAutoConfiguration排除DataSourceAutoConfiguration给你应用类,它将允许GORM接管配置数据源

确保您的启动应用该类带有注释组件扫描例如

 org springframework boot SpringApplication
 org springframework boot autoconfigure EnableAutoConfiguration
 org springframework上下文注释

组态
启用自动配置
组件扫描
 应用 {
    静态的 虚空主要[]args SpringApplication运行应用程序args
使用组件扫描如果没有值,则会导致引导程序扫描同一软件包或嵌套在软件包中的任何软件包中的类。应用类包如果您的GORM实体位于其他包中,则将包名称指定为组件扫描注解

最后创建您的GORM实体,并确保为它们添加注释grails持久性实体:

 持久性

实体
  {
    名字

请注意,Spring Boot不包含任何类型的OpenSessionInView拦截器,因此,如果您尝试在Spring中调用GORM方法,控制者您可能会遇到找不到会话的错误要消除此问题,请确保您的控制者方法带有注释交易性例如

 org springframework交易注释Transactional
 org springframework网络绑定注释RequestMapping
 org springframework Web绑定注释RestController

RestController
 人控制器 {

    请求映射("")
    交易性只读)
    上市 清单<人员人员列表收集人员p"$p名字$p"toString

另外,如果您希望从Spring返回GORM实例控制者应当注意,Spring使用Jackson进行JSON编组,而Jackson将尝试将整个对象编组为JSON,这可能会出现问题,因为GORM向您的域实例添加了其他与持久性相关的属性。要解决此问题,您应该使用JsonIgnoreProperties在您的GORM实体类上忽略GORM添加的任何属性

 持久性
 com fastxml jackson批注JsonIgnoreProperties

实体
JsonIgnoreProperties(['dirtyPropertyNames', '错误', '', '附上', ''])
  {
    名字

使用GORM休眠外部365bet地区

365bet地区如果您希望在365bet地区应用程序之外将GORM用于Hibernate,则应声明GORM和所用数据库的必要依赖关系,例如在Gradle中

编译组织grails grails数据存储gorm休眠释放运行时com h数据库h运行时org apache tomcat tomcat jdbc runtime org apache tomcat embed tomcat embed日志记录日志j runtime org slf j slf j api
上面的示例还使用了H数据库和Tomcat连接池,但是支持其他池实现,包括公地dbcp, 雄猫池要么轻便如果未指定连接池org springframework jdbc数据源DriverManagerDataSource用于在您每次请求连接时创建与数据库的新连接。后者可能会导致内存数据库中的H出现问题,因为每次请求连接时,它将在内存数据库中创建一个新数据库,而丢失了先前创建的表的MySQL, Postgres的甚至基于文件H不受影响

然后在src主界面目录并使用grails gorm注释实体注解

实体
  实施GormEntity { (1)
    名字静态的约束firstName空白:空白:
    }
}
1 用于GormEntity365bet地区只是为了帮助365bet地区外部的IDE支持在365bet地区上下文中使用某些IDE时,将使用365bet地区应用程序域位置提示启用代码完成

然后,您需要将引导程序逻辑放置在应用程序中使用HibernateDatastore:

 组织grails orm休眠HibernateDatastore
组态'休眠hbm ddl自动':'创建放置',
    'dataSource网址':'jdbc h mem myDB'HibernateDatastore数据存储HibernateDatastore配置人员

有关如何配置GORM的更多信息,请参见组态部分

快速入门指南

可以使用创建域类365bet地区如果您使用的是365bet地区或不使用365bet地区,则只需创建时髦手动归档

grails创建域 你好,世界

这将在该位置创建一个类grails应用程序域helloworld Person groovy如下面的一个

你好,世界  {
}
如果您已配置数据源dbCreate属性并将其设置为update create或create drop GORM将自动为您生成修改数据库表

您可以通过添加属性来自定义类

  {
    整数日期lastVisit

一旦有了域类,请尝试使用安慰365bet地区在365bet地区中输入以下命令

grails控制台

这将加载一个交互式GUI,您可以在其中运行Groovy命令并访问Spring ApplicationContext GORM等

365bet地区或者,如果您不使用365bet地区,则可以使用以下单元测试模板斯波克可以运行以测试示例

 斯波克长
 grails gorm注释实体
 grails交易回滚
 组织grails orm休眠HibernateDatastore
 org springframework交易PlatformTransactionManager

 ExampleSpec 延伸规格共享 自动清理HibernateDatastore hibernateDatastore共享PlatformTransactionManager transactionManager虚空setupSpec hibernateDatastoreHibernateDatastore人员transactionManager hibernateDatastore getTransactionManager回滚
    虚空 "在单元测试中独立执行GORM测试"() {
       你的逻辑在这里
    }
}

实体
  {
    ...
}

基本增删改查

尝试执行一些基本的CRUD创建读取更新删除操作

创建

要创建域类,请使用Map构造函数设置其属性并调用保存方法

定义p: "弗雷德", : 40, lastVisit:  日期p保存

保存方法将使用基础的Hibernate ORM层将您的类持久化到数据库中

保存方法由GormEntity特征

GORM透明地添加一个隐式ID域类的属性,可用于检索

定义p人得到1)
断言 1身份

这使用静态获取ID期望数据库标识符读取的方法从数据库返回对象

您还可以使用以下命令将对象加载为只读状态:读取ID方法

定义p阅读的人1)

在这种情况下,基础的Hibernate引擎将不会进行任何脏检查,并且该对象也不会持久化。请注意,如果您显式调用保存方法,然后将对象放回读写状态

此外,您还可以使用来加载实例的代理加载ID方法

def p人员负荷1)

直到调用getId以外的方法,然后再初始化代理实例或如果找不到指定ID的记录,则引发异常,这不会导致数据库访问。

更新资料

要更新实例,请更改一些属性,然后调用保存再次

定义p人得到1p名"鲍勃"p保存

删除

要删除实例,请使用删除方法

定义p人得到1p删除

组态

可以使用以下命令配置GORM for Hibernategrails应用配置会议应用yml365bet地区使用365bet地区时的文件src主要资源应用yml使用Spring Boot或通过传递一个文件时PropertyResolver连接到组织grails orm休眠HibernateDatastore独立使用时的类

读取所有配置选项并将其实例化为HibernateConnectionSourceSettings.

配置实例

365bet地区如果您使用的是365bet地区或Spring Boot,则以下是在中指定的配置示例:

数据源:
    派对: 
    dbCreate: 创建放置
    网址: jdbc h mem devDb
    driverClassName: org h驱动程序
    用户名: 她的
    密码:
:
    快取:
        查询: 
        使用二级缓存: 
        使用查询缓存: 
        地区工厂类: 组织休眠休眠ehcache EhCacheRegionFactory

下的每个设置数据源块设置在数据源设置的财产HibernateConnectionSourceSettings.

虽然“块设置在HibernateSettings属性

配置参考

你可以参考HibernateConnectionSourceSettings所有可用配置选项的类,但以下是常用选项的表

描述 默认值

grails gorm flushMode

使用的冲洗模式

汽车

格栅美食档案

是否在验证错误时引发异常

grails gorm默认映射

适用于所有类的默认映射

空值

grails gorm默认约束

适用于所有类的默认约束

空值

grails gorm多租户模型

多租户模式

没有

以下是SQL连接的常用配置选项

描述 默认值

dataSource网址

JDBC URL

jdbc h mem grailsDB

dataSource driverClassName

JDBC驱动程序的类

从网址检测到

dataSource用户名

JDBC用户名

空值

dataSource密码

JDBC密码

空值

dataSource jndiName

JNDI资源的名称数据源

空值

合并的数据源

连接是否池化

数据源懒

是否一个LazyConnectionDataSourceProxy应该使用

数据源transactionAware

是否一个TransactionAwareDataSourceProxy应该使用

数据源只读

数据源是否为只读

dataSource选项

传递给底层JDBC驱动程序的选项的映射

空值

以下是Hibernate的常见配置选项

描述 默认值

冬话

休眠方言使用

从数据源自动检测

休眠readOnly

Hibernate是否应为只读

休眠configClass

要使用的配置类

HibernateMappingContextConfiguration

休眠hbm ddl自动

是否在启动时创建表

没有

休眠使用二级缓存

是否使用二级缓存

休眠缓存查询

是否缓存查询,请参阅缓存查询

休眠缓存使用查询缓存

启用查询缓存

休眠配置位置

其他Hibernate XML配置文件的位置

休眠程序包

此外,任何以开头的其他设置冬眠的传递给Hibernate,因此,如果您希望配置Hibernate的任何特定功能,则可以

上表涵盖了常见的配置选项。有关所有配置,请参见HibernateConnectionSourceSettings

默认映射约束

grails gorm默认映射grails gorm默认约束设置值得特别提及这些定义了默认ORM映射和默认验证约束每个实体使用

更改默认数据库映射

您可能有理由要更改所有域类映射到数据库的方式。例如,默认情况下,GORM使用本机数据库的ID生成策略,无论是自动增量列还是序列

如果您希望全局更改所有域类以使用uid策略,然后您可以在默认映射中指定

grails应用配置会议应用程序
grails gorm默认映射缓存ID发电机:'uid'
}

如您所见,您可以分配与映射过去用来自定义域类如何映射到数据库表.

由于该设置是Groovy配置,因此必须采用Groovy感知的配置格式。grails应用配置会议应用程序365bet地区在365bet地区或src主要资源应用程序在Spring Boot中

更改默认约束

为了验证,GORM应用了一组默认值约束所有领域类别

例如,默认情况下,GORM类的所有属性默认情况下都不为空,这意味着必须为每个属性提供一个值,否则您将收到验证错误

在大多数情况下,这就是您想要的,但是如果您要处理大量的列,可能会很不方便

您可以使用Groovy配置更改默认约束,方法是使用grails gorm默认约束设置

grails应用配置会议应用程序
grails gorm默认约束'*'(可为空: , 尺寸: 1..20)
}

在上面的示例中,所有属性都允许可为空默认情况下,但限于介于和之间的大小

休眠定制

如果您想了解GORM并自定义Hibernate的配置方式,那么在使用GORM时可以通过多种方法来实现。

首先,如前所述,您在为Hibernate配置GORM时指定的任何配置都将传递给Hibernate,因此您可以配置Hibernate本身的任何设置

对于更高级的配置,您可能需要配置或提供新的HibernateConnectionSourceFactory实例或HibernateMappingContextConfiguration或两者

HibernateConnectionSourceFactory

HibernateConnectionSourceFactory用于创建新的Hibernate会话工厂启动时

如果您使用的是Spring,则会使用名称将其注册为Spring beanhibernateConnectionSourceFactory因此可以被覆盖

如果您不使用Spring,则可以将其传递给HibernateDatastore实例化类

HibernateConnectionSourceFactory有一些有用的设置器,可让您指定休眠模式拦截器要么元数据贡献者仅休眠

HibernateMappingContextConfiguration

HibernateMappingContextConfigurationHibernateConnectionSourceFactory但可以使用休眠configClass在您的配置中设置

grails应用配置会议应用yml
:
        configClass: 与示例MyHibernateMappingContextConfiguration

自定义版本应扩展HibernateMappingContextConfiguration并使用该类,您可以添加其他类包HBM CFG XML文件等

GORM中的领域建模

在构建应用程序时,您必须考虑要解决的问题域,例如,如果要构建一个亚马孙风格的书店,您可能会在考虑书籍作者,客户和出版商

这些在GORM中被建模为Groovy类,因此类中可能有标题,发布日期,ISBN号等等,接下来的几节说明如何在GORM中为域建模

考虑以下域类

grails应用程序域组织书店groovy
组织书店  {
}

此类将自动映射到数据库中名为与班级同名

此行为可通过ORM领域特定语言

现在有了域类,您可以将其属性定义为Java类型。

组织书店  {
    标题日期发布日期书号

每个属性都映射到数据库中的一列,其中列名的约定全部用小写字母加下划线分隔。例如发布日期映射到列发布日期可从Java类型中自动检测到SQL类型,但可以使用约束条件或者ORM DSL.

蓝色协会

关系定义域类之间的交互方式,除非在两端明确指定,否则关系仅在定义的方向上存在

多对一和一对一

多对一关系是最简单的一种,并使用另一域类的类型的属性定义

例子A
 面对鼻子鼻子
 鼻子 {
}

在这种情况下,我们有一个单向的多对一关系面对鼻子要使该关系双向定义,请如下定义另一边,并参阅下面有关控制关联末端的部分

例子B
 面对鼻子鼻子
 鼻子 {
    静态的属于面对面对

在这种情况下,我们使用属于设置说鼻子属于面对这样的结果是我们可以创建一个面对附加一个鼻子实例,当我们保存或删除面对实例GORM将保存或删除鼻子换句话说,保存和删除将从面对到相关的鼻子:

面对鼻子:鼻子保存

上面的示例将保存脸部和鼻子,请注意,相反不是正确,由于瞬态会导致错误面对:

鼻子面对:脸部保存会导致错误

现在,如果我们删除面对实例鼻子也会去

定义f脸得到1f删除脸部和鼻子都被删除

要使关系真正一对一,请使用hasOne拥有方的财产面对:

范例C
 面对 {
    静态的hasOne鼻子鼻子
 鼻子面对面

请注意,使用此属性会将外键放在示例A的反向表上,因此在这种情况下,外键列存储在鼻子表内的列称为人脸编号.

hasOne仅适用于双向关系

最后,在一对一关系的一侧添加唯一约束是一个好主意

 面对 {
    静态的hasOne鼻子鼻子静态的约束鼻子独特: 
    }
}
 鼻子面对面
控制关联的目的

有时,您可能会遇到具有相同类型的多个属性的域类,它们甚至可能是自引用的,即,关联属性与域类具有相同的类型。在这种情况下,这可能会引起问题,因为GORM可能会错误地猜测该协会考虑这个简单的类

  {
    姓名人父母静态的属于主管静态的约束主管可为空:  }
}

就GORM而言父母主管属性是相同关联的两个方向,所以当您设置父母物业实例GORM将自动设置主管其他财产实例这可能是您想要的,但是如果您查看类,我们实际上拥有的是两个单向关系

要引导GORM正确映射,您可以通过以下方式告诉它特定的关联是单向的被映射属性

  {
    姓名人父母静态的属于主管静态的被映射主管: "没有", 父母: "没有" ]

    静态的约束主管可为空:  }
}

您也可以将其替换为目标类的任何属性名称,这当然也适用于普通域类,而不仅仅是自我引用类。被映射属性仅限于多对一和一对一的关联,它也适用于一对多和多对多的关联,如您在下一部分中将看到的

如果您的域类上有一个名为none的属性,则该方法目前无法正常工作。none属性将被视为关联的反向方向或反向引用。幸运的是,none不是通用的域类属性名
取代多对一的收藏

鉴于这些GORM实体

  {
    静态的有很多评论评论 评论 {
    作者引用静态的属于: ]
}

假设您有一本书有两个评价

 (: '守护进程'addToReviews评论引用: 'Daemon在网上冲浪时的行为与Jaws在大海中游泳时的行为相同', 作者: '芝加哥太阳时报'addToReviews评论引用: '守护进程被自己弄湿了,令人恐惧的技术头脑清醒', 作者: '复活节商店'保存

您可以创建一种方法来替换评论集合如下图所示

replaceReviews可序列化idParam清单新评论id idParam加入哪里'评论'获取clearReviews图书newReviews每本书addToReviews图书保存虚空clearReviews清单<可序列化编号[]书评收集每本书removeFromReviews编号id审查executeUpdate"删除评论r其中id中的id", [编号编号

或者,您可以利用级联行为

  {
    静态的有很多评论评论静态的地图评论级联: '全部删除孤儿'
    }
}
 评论 {
    作者引用静态的属于: ]
}

级联行为需要删除每个孤儿评论因此调用明确足以删除该书的先前评论

replaceReviews可序列化idParam清单新评论id idParam加入哪里'评论'获取书评清除newReviews每本书addToReviews图书保存

一对多

一对多关系是一个班级的例子作者有许多其他类实例的实例使用GORM,您可以与有很多设置

 作者 {
    静态的有很多图书: ]

    那么
  {
    标题

在这种情况下,我们有一个单向一对多的GORM,默认情况下,它将与联接表映射这种关系

ORM DSL允许使用外键关联映射单向关系

GORM将自动注入类型为java有用集进入基于有很多设置这可以用来遍历集合

定义作者得到1)

对于一本书println书名
GORM使用的默认获取策略是惰性的,这意味着将在首次访问时对集合进行延迟初始化。如果您不小心,可能会导致N

如果您渴望获取,可以使用ORM DSL或指定将渴望获取作为询问

默认的级联行为是级联保存和更新,但不删除级联,除非属于也被指定

 作者 {
    静态的有很多图书: ]

    那么
  {
    静态的属于作者作者标题

如果在一对多的多个边上具有两个相同类型的属性,则必须使用被映射指定要映射的集合

 飞机场 {
    静态的有很多航班飞行静态的被映射航班: "departureAirport"]
}
 飞行机场出发机场机场目的地

如果您有多个集合在许多方面映射到不同的属性,则也是如此

 飞机场 {
    静态的有很多出站航班飞行入站航班飞行静态的被映射出站航班: "departureAirport",
                       入站航班: "目的地机场"]
}
 飞行机场出发机场机场目的地

多对多

GORM通过定义一个有很多在关系的两边都有属于在关系的拥有方面

  {
    静态的归属于作者静态的有很多作家作者标题
 作者 {
    静态的有很多图书:]
    那么

GORM在数据库级别使用连接表将多对多映射。在这种情况下,关系的拥有方作者负责维持关系,并且是唯一可以级联保存的方面

例如,这将起作用并级联保存

作者:"斯蒂芬·金"addToBooks (标题:"展台"addToBooks (标题:"闪耀"保存

但是,这只会保存而不是作者

 (:"Groovy在行动"addToAuthors作者:"迪尔克·科尼格(Dierk Koenig)"addToAuthors作者:"纪尧姆·拉福格(Guillaume Laforge)"保存

这是预期的行为,就像Hibernate一样,许多对多方中只有一方可以负责管理关系

基本集合类型

除了不同域类之间的关联外,GORM还支持基本集合类型的映射。例如,以下类创建了一个昵称关联是实例

  {
    静态的有很多昵称]
}

GORM将使用联接表来映射与上述类似的关联。您可以使用以下方法更改联接表的映射方式的各个方面可接合论点

  {

    静态的有很多昵称]

    静态的映射昵称joinTable名称'一堆昵称''人名''昵称'类型"文本"]
    }
}

上面的示例将映射到一个如下所示的表

一堆昵称表

个人编号昵称1弗雷德

GORM中的组成

以及协会GORM支持组合的概念。在这种情况下,可以将类嵌入到当前表中,而不是将类映射到单独的表上

 地址homeAddress地址workAddress静态的嵌入式的'家庭地址', '工作地址']
}

 地址 {
    

生成的映射看起来像这样

组成
如果您定义地址类放在单独的Groovy文件中365bet地区应用程序域目录,您还将获得一个地址表如果您不希望发生这种情况,请使用Groovy的功能为每个文件定义多个类,并包括地址下面的类中的课程365bet地区应用程序域人groovy文件另一个选择是定义地址上课src main groovy地址groovy并用grails gorm注释实体

GORM中的继承

GORM支持从抽象基类和具体的持久性GORM实体继承。

 内容 {
     作者
 BlogEntry 延伸内容网址网址
  延伸内容书号
 播客 延伸内容字节[]audioStream

在上面的示例中,我们有一个父级内容类,然后是具有更具体行为的各种子类

注意事项

在数据库级别,默认情况下,GORM使用每个层次结构映射的表以及一个名为所以父母班内容及其子类BlogEntry, 等分享相同

每个层次结构表的映射有一个缺点不能具有带有继承映射的非可为空的属性替代方法是为每个子类使用表格,可以通过ORM DSL

但是,由于使用外部联接查询,过度使用继承和每个子类的表可能会导致查询性能下降。通常,我们的建议是,如果您打算使用继承,请不要滥用它,也不要使继承层次结构太深

多态查询

继承的结果是您可以进行多态查询,例如使用清单上的方法内容超类将返回的所有子类内容:

定义内容内容列表列出所有博客条目书籍和播客内容内容findAllByAuthor'乔·博格斯') 按作者查找全部

定义podCasts PodCast列表仅列出播客

集合列表和地图

对象集

默认情况下,当您定义与GORM的关系时,它是一个java有用集这是一个无序的集合,不能包含重复项

 作者 {
    静态的有很多图书: ]
}

GORM注入的book属性是一个java有用集设置保证唯一性,但不能保证您可能不想要的顺序要进行自定义排序,请将设置配置为SortedSet:

 作者 {

    SortedSet图书静态的有很多图书: ]
}

在这种情况下java util SortedSet使用实现意味着您必须实现java long兼容在您的Book课堂中

  实施 可比 {

    标题日期发布日期 日期()

    整型compareTo obj发布日期compareTo obj发布日期

以上类的结果是Author类的books集合中的Book实例将按其发布日期排序

对象清单

为了使对象按添加顺序排列,并能够像数组一样通过索引引用它们,可以将集合类型定义为清单:

 作者 {

    清单图书静态的有很多图书: ]
}

在这种情况下,当您向books collection中添加新元素时,订单将保留在从中索引的顺序列表中,因此您可以

作者书0>> 得到第一本书

Hibernate在数据库级别上的工作方式是创建一个书籍IDX列,它保存集合中元素的索引以在数据库级别保留此顺序

当使用清单元素必须在保存之前添加到集合中,否则Hibernate会引发异常组织hibernate HibernateException用于收集的空索引列

这行不通
定义 (标题: '闪耀'图书保存作者addToBooks图书用这种方式代替
定义 (标题: '苦难'作者addToBooks本书作者保存

袋物品

如果不关心顺序和唯一性,或者如果您明确地管理它们,则可以使用Hibernate类型代表映射的集合

唯一需要做的更改就是将集合类型定义为采集:

 作者 {

   采集图书静态的有很多图书: ]
}

由于唯一性和顺序不是由Hibernate管理的,因此添加到映射为Bag的集合中或从其中删除不会触发数据库中所有现有实例的加载,因此,与使用Abernetes相比,此方法将更好地执行并且需要更少的内存。或一个清单.

物体图

如果您想要一个简单的字符串值对映射,GORM可以使用以下内容对此进行映射

 作者 {
    图书ISBN书名地图
}

定义一种写一本书'1590597583':"我的书"保存

在这种情况下,地图的键和值必须为字符串

如果您想要对象图,则可以执行此操作

  {

    作家静态的有很多作家作者定义一种作者:"斯蒂芬·金")

定义 书作者斯蒂芬书本保存

静态有很多属性定义Map中元素的类型Map的键必须是字符串

关于收集类型和性能的注释

Java类型不允许重复,以确保向条目添加条目时的唯一性关联Hibernate必须从数据库中加载整个关联。如果关联中有大量条目,则在性能方面可能会付出高昂的代价。

需要相同的行为清单类型,因为Hibernate需要加载整个关联以保持顺序,因此,建议您如果预期关联中有大量记录,则将关联设为双向,以便可以在相反侧创建链接。例如,考虑以下内容码

def书 标题"365bet地区新365bet地区书"def作者作者get1书作者作者书保存

在此示例中,关联链接是由子Book创建的,因此不必直接操作该集合,从而导致更少的查询和更有效的代码。作者与大量相关实例,如果您要编写如下代码,则会对性能产生影响

def书 标题"新365bet地区书"def作者作者get1作者addToBooks本书作者保存

您还可以如上所述将集合建模为休眠包

持久性基础

关于GORM要记住的关键一件事是GORM在表面上使用冬眠持久性如果您来自使用背景活动记录要么iBatis MyBatisHibernate的会话模型可能会感到有些奇怪

365bet地区如果您使用的是365bet地区,则365bet地区会自动将Hibernate会话绑定到当前正在执行的请求。这使您可以使用保存方法以及其他GORM方法都是透明的

365bet地区如果您不使用365bet地区,则必须确保将会话绑定到当前请求。withNewSession关闭方法

withNewSession你的逻辑在这里
}

另一种选择是使用with事务关闭方法

withTransaction你的逻辑在这里
}

交易后写

Hibernate通过直接JDBC调用甚至其他框架的一个有用功能是,当您调用保存要么删除它不一定执行任何SQL操作在那时候Hibernate批处理SQL语句,并在刷新和关闭会话时在请求结束时尽可能晚地执行它们

365bet地区如果您正在使用365bet地区,这通常会自动为您完成,它会管理您的Hibernate会话。如果您在365bet地区之外使用GORM,则可能需要在操作结束时手动刷新会话

Hibernate会在可能的情况下仅在知道需要刷新或以编程方式触发刷新时才实际推动更改的情况下缓存数据库更新。Hibernate将刷新缓存的更新的一种常见情况是在执行查询时,因为缓存的信息可能包含在查询结果中但是,只要您执行无冲突的保存更新和删除操作,它们就会被分批处理,直到刷新会话为止。这对于执行大量数据库写操作的应用程序而言可能是显着的性能提升

请注意,刷新与提交事务不同。如果您的操作是在事务上下文中执行的,则刷新将执行SQL更新,但是数据库会将更改保存在其事务队列中,并且仅在提交事务时才完成更新。

保存和更新

使用示例保存方法可以看下面

定义p人得到1p保存

此保存不会立即推送到数据库,而是在下一次刷新发生时立即推送到数据库,但是有时您希望控制何时执行这些语句,或者在会话刷新时使用Hibernate术语来执行此操作,您可以使用将save参数的flush参数

定义p人得到1p保存齐平: )

请注意在这种情况下所有待处理的SQL语句(包括先前的保存,删除等)将与数据库同步。这还使您可以捕获任何异常,这在涉及以下情况的高度并发的场景中通常很有用乐观锁定:

定义p人得到1)
尝试p保存齐平: )
}
抓住org springframework dao DataIntegrityViolationException e处理异常
}

要记住的另一件事是,GORM每次保存时都会验证一个域实例。如果验证失败,则该域实例将默认情况下保留到数据库保存只会返回空值在这种情况下,但是如果您希望它引发异常,则可以使用failOnError论点

定义p人得到1)
尝试p保存failOnError: )
}
抓住ValidationException e处理异常
}

您甚至可以使用中的设置更改默认行为应用程序如中所述配置部分请记住,当您保存与用户提供的数据绑定的域实例时,验证异常的可能性非常高,并且您不希望这些异常传播到最终用户

删除物件

一个例子删除方法可以看下面

定义p人得到1p删除

与保存一样,Hibernate将使用事务性写操作执行删除操作,以执行删除操作,您可以使用齐平论点

定义p人得到1p删除齐平: )

使用齐平参数使您可以捕获在删除期间发生的任何错误。可能发生的常见错误是,如果您违反数据库约束,尽管通常归因于编程或架构错误,以下示例显示了如何捕获DataIntegrityViolationException违反数据库约束时抛出的异常

 org springframework dao.*

定义 p = 人得到(1)

尝试 {
    p删除(齐平: )
}
抓住 (DataIntegrityViolationException ) {
    处理错误
}

为了执行批量删除,有两种方法可以实现该目的。一种方法是使用哪里查询:

名字的人"弗雷德"删除所有

另一种选择是在executeUpdate方法

客户执行更新"删除客户c,其中c名称为oldName",
                       [旧名称: "弗雷德"])

了解级联更新和删除

了解使用GORM时级联更新和删除的工作方式非常重要。要记住的关键部分是属于设置控制哪个类拥有关系

无论是一对一一对多还是多对多定义属于将导致从拥有类到依赖关系的级联更新,对于许多一对一和一对多关系,删除操作也会级联

如果你不要限定属于那么将不会发生级联,并且您将必须手动保存每个对象,除非一对一的情况如此,如果新实例在一个存储区中,则保存将自动级联。有很多采集

这是一个例子

 飞机场 {
    静态的有很多航班飞行
 飞行 {
    静态的属于飞机场飞机场

如果我现在创建一个飞机场并添加一些飞往它,我可以节省机场并将更新级联到每个排期,从而节省了整个对象图

飞机场: "盖特威克"addToFlights飞行: "BA"addToFlights飞行: "EZ"保存

相反,如果我以后删除飞机场所有飞行与之关联的实例也将被删除

定义机场机场findByName"盖特威克"机场删除

上面的示例称为传递持久性,并且通过属于和级联策略如果我要删除属于然后上面的级联删除代码将无法正常工作.

无向单向多对一

例如考虑以下领域模型

 位置 {
     作者 {
    名称位置位置

看起来很简单,只需将location属性设置为Location实例,并且您已经将作者链接到了位置,但是看看我们运行以下代码会发生什么

定义一种作者: "尼尔·弗格森", 位置: 位置: "波斯顿"保存

引发异常如果您查看由异常引起的最终原因,您将看到消息不为null的属性引用的是null或瞬态值作者位置这是怎么回事

瞬态实例是未附加到Hibernate会话的实例。从代码中可以看到,我们将Author location属性设置为一个新的Location实例,而不是从数据库中检索到的实例,因此该实例是瞬态的。通过保存将位置实例持久化

定义位置: "波斯顿"我保存定义一种作者: "尼尔·弗格森", 位置我保存

另一种选择是更改关联的级联策略。有两种方法可以实现:一种是定义属于位置

 位置 {
    静态的归属于作者

请注意,由于未定义任何属性,因此上述语法不会使关联成为双向。

 位置 {
    静态的属于作者作者

或者,如果您更喜欢位置班级与作者您可以在中定义级联策略的类作者:

 作者 {
    名称位置位置静态的地图位置级联:'保存更新'
    }
}

上面的示例将配置级联策略以级联保存和更新,但不删除

双向一对多与belongsTo
 一种 { 静态的有很多蜜蜂
  { 静态的属于一种一种

在双向一对多的情况下,多边定义了一个属于然后将级联策略的一侧设置为ALL,将多侧设置为NONE

这意味着每当一个实例一种被保存或更新的任何实例至关重要的是,无论何时一种已删除因此,所有相关实例!

单向一对多
 一种 { 静态的有很多蜜蜂
  {  }

在单向一对多的情况下,当多方未定义belongsTo时,级联策略将设置为SAVE UPDATE

自从属于未定义,这意味着保存和更新将从一种但是删除将不会级联

仅当您定义属于或更改的级联策略一种将删除级联

双向一对多不属于
 一种 { 静态的有很多蜜蜂
 a

在双向一对多的情况下,多边未定义属于然后将级联策略的一侧设置为SAVE UPDATE,将多侧设置为NONE

所以就像以前的单向一对多情况属于定义不会删除操作级联,但是默认情况下至关重要的是保存和更新,如果您不希望保存和更新到cacade,则你必须改变...的级联政策一种:

 一种 {
    静态的有很多蜜蜂静态的测绘蜜蜂级联:"没有"
    }
}
单向多对一与belongsTo
 一种 {  }
  { 静态的属于一种一种

在单向多对一关联的情况下,该关联定义了属于然后将关联策略A B的拥有方的级联策略设置为ALL,并从定义该方的那一侧将NONE设置为NONE属于A

您可能想知道为什么这种关联是多对一的而不是一对一的?原因是因为可能有多个实例关联到的相同实例一种如果您希望将此关联定义为真正的一对一关联,则a独特需要约束

  {
    静态的属于一种一种静态的约束a独特:
    }
}

请注意,如果您需要进一步控制级联行为,可以使用ORM DSL.

渴望和懒惰获取

默认情况下,GORM中的关联是惰性的。

 飞机场 {
    静态的有很多航班飞行
 飞行 {
    编号目的地静态的属于飞机场飞机场
 位置 {
    国家

给定以上域类和以下代码

定义机场机场findByName"盖特威克")
对于飞行机场航班println飞行目的地城市

GORM将执行一个SQL查询以获取飞机场实例另一个以获取其航班,然后额外查询每次迭代航班关联以获取当前航班的目的地。换句话说,如果您排除原始查询以获取机场,则会得到N个查询

配置渴望获取

避免N个查询的另一种方法是使用预先获取,可以如下指定

 飞机场 {
    静态的有很多航班飞行静态的映射航班: 
    }
}

在这种情况下航班关联将在与其关联的同时加载飞机场实例,尽管将执行第二个查询以获取集合,您也可以使用取得加入代替懒惰的假在这种情况下,GORM将仅执行一次查询以获取机场及其航班。这对于单端关联非常有效,但是您需要小心处理一对多的查询,直到您添加限制后,查询才能正常工作达到所需的结果数量到那时,您最终得到的结果可能会比您预期的少。原因是技术性的,但最终问题出在使用左外连接的GORM上

因此,该建议目前正在使用取得加入对于单端协会和懒惰的假一对多

请注意如何使用紧急加载以及在何处使用紧急加载,因为您可能会通过过多的紧急关联将整个数据库加载到内存中。您可以在有关ORM DSL的部分.

更改查询的获取策略

与其将联接获取配置为关联的默认配置,不如仅针对需要联接的查询更改联接策略,这可能会更好。大多数GORM方法的论点

使用列表方法作者名单: [位置: '加入'每个印刷品都在一个位置城市使用动态取景器作者findAllByNameLike"约翰", [ 分类: '', 订购: 'ASC', : [位置: '加入'每个一个

或使用加入使用时的方法哪里查询或标准

作者姓名"斯蒂芬·金"加入'位置'清单

使用批量提取

尽管热切获取在某些情况下是适当的,但这并不总是可取的。如果您进行了所有急切工作,则很可能将整个数据库加载到内存中,从而导致性能和内存问题。批量获取结果,例如

 飞机场 {
    静态的hasMany航班静态的映射航班批次大小10
    }
}

在这种情况下由于batchSize当您遍历航班关联Hibernate将批量获取结果,例如,如果您有一个飞机场如果您未配置批量抓取,则有航班,您将获得查询以提取飞机场接着30查询以获取每个航班通过批量获取,您可以查询以获取飞机场和查询以获取每个飞行换句话说,批量获取是延迟获取策略的优化,也可以在类级别上按以下方式配置批量获取

 飞行静态映射batchSize10
    }
}

悲观和乐观锁定

乐观锁

默认情况下,将GORM类配置为进行乐观锁定乐观锁定是Hibernate的一项功能,其中涉及将版本值存储在特殊的数据库中在每次更新后增加的列

列被读入包含您可以访问的持久性实例的当前版本状态的属性

定义机场机场10println机场版本

当您执行更新时,Hibernate将根据数据库中的version列自动检查version属性,如果它们不同,则会抛出一个错误。StaleObjectException如果一个活动,这将回滚事务

这很有用,因为它允许一定程度的原子性,而不必求助于具有继承性能损失的悲观锁定。不利之处是,如果您具有高度并发写入,则必须处理此异常。这需要刷新会话

定义机场机场10)

尝试机场名称"希思罗机场"机场节省齐平: )
}
抓住org springframework dao OptimisticLockingFailureException e处理异常
}

处理异常的方式取决于应用程序您可以尝试以编程方式合并数据或返回给用户并要求他们解决冲突

另外,如果出现问题,您可以采用悲观锁定

仅在刷新会话后才会更新

悲观锁定

悲观锁定等效于执行SQL SELECT FOR UPDATE语句并锁定数据库中的一行。这意味着在锁定释放之前,其他读取操作将被阻塞。

在GORM中,悲观锁定是使用方法

定义机场机场10机场锁锁定更新机场名称"希思罗机场"机场节省

交易一旦提交,GORM将自动为您处理释放锁

但是,在上述情况下,我们正在做的是从常规SELECT升级到SELECT FOR UPDATE,并且另一个线程可能仍已在两次调用之间更新了记录。得到并致电.

要解决此问题,您可以使用静态锁号需要一个id的方法就像获取ID:

定义机场机场锁10) 锁定更新机场名称"希思罗机场"机场节省

在这种情况下,仅发出SELECT FOR UPDATE

以及锁号方法,您还可以使用查询获得悲观锁定,例如使用动态查找器

def机场机场findByName"希思罗机场"])

或使用条件

def机场Airport createCriteria获取均衡'', '希思罗机场'
}

修改检查

一旦加载并可能修改了持久域类实例,就很难直接检索原始值。获取IDHibernate将从其会话缓存中返回当前修改后的实例

使用另一个查询重新加载将触发刷新,如果您尚未准备好刷新数据,可能会导致问题,因此GORM提供了一些方法来检索Hibernate在加载用于脏检查的实例时缓存的原始值

是脏的

您可以使用是脏的检查任何字段是否已修改的方法

定义机场机场10)
断言机场isDirty机场属性参数如果机场很脏根据状态改变做某事
}
是脏的当前不检查集合关联,但是会检查所有其他持久属性和关联

您还可以检查各个字段是否已被修改

定义机场机场10)
断言机场isDirty机场属性参数如果机场很脏'')) {
   根据更改的名称进行操作
}

getDirtyPropertyNames

您可以使用getDirtyPropertyNames检索已修改字段名称的方法,此方法可以为空,但不能为null

定义机场机场10)
断言机场isDirty机场属性参数定义修改FieldNames机场setDirty PropertyNames对于fieldNamemodifiedFieldNames根据价值变化做某事
}

getPersistentValue

您可以使用getPersistentValue fieldName检索修改后的字段的值的方法

定义机场机场10)
断言机场isDirty机场属性参数定义修改FieldNames机场setDirty PropertyNames对于fieldNamemodifiedFieldNames定义currentValue机场"$fieldName"
    定义originalValue机场getPersistentValue字段名称如果currentValue originalValue根据价值变化做某事
    }
}

用GORM查询

GORM支持多种强大的查询方式,从动态查找器到条件查询,再到Hibernate的面向对象查询语言HQL,根据查询的复杂程度,您可以按以下顺序选择以下选项:灵活性和功能强大

  • 动态发现者

  • 哪里查询

  • 条件查询

  • 休眠查询语言HQL

此外,Groovy可以通过以下方式操纵集合闲聊以及诸如sort findAll之类的方法与GORM相结合,可实现强大的组合

但是,让我们从基础开始

清单实例

使用清单获取给定类的所有实例的方法

定义清单

清单方法支持执行分页的参数

定义清单抵销:10, 最大值:20)

以及排序

定义清单分类:"标题", 订购:"ASC")

在这里分类参数是您希望排序的域类属性的名称,订购论证是ASC用于asc结尾或描述用于降序

通过数据库标识符检索

检索的第二种基本形式是使用获取ID方法

定义得到23)

您还可以使用来获取一组标识符的实例列表getAll:

定义getAll23, 93, 81)

动态发现者

GORM支持以下概念动态发现者动态查找器看起来像静态方法调用,但是方法本身在代码级别实际上并不以任何形式存在

取而代之的是,在运行时根据给定类的属性使用代码合成方法自动生成一种神奇的方法,例如

  {
    标题日期发布日期作者作者
 作者 {
    那么

类具有诸如标题, 发布日期作者这些可以由findByfindAllBy方法表达式形式的方法

定义findByTitle"展台"findByTitleLike"哈里·波特"findByReleaseDateBetween firstDate secondDate书之间findByReleaseDateGreater than someDate bookfindByTitleLikeOrReleaseDateLessThan"东西"一些日期

方法表达式

GORM中的方法表达式由前缀组成,例如findBy后跟一个包含一个或多个属性的表达式基本形式是

findBy><<比较器>><<布尔型操作员><<比较器>>

标记有一个?是可选的每个比较器都会更改查询的性质,例如

定义findByTitle"展台"findByTitleLike"哈里·波特")

在上面的示例中,第一个查询等效于等式,而后者由于喜欢比较器等效于SQL喜欢表达

可能的比较器包括

  • 清单在给定值列表中

  • 少于小于给定值

  • 小于等于小于或等于给定值

  • 比...更棒大于给定值

  • 大于等于大于或等于给定值

  • 喜欢等效于类似SQL的表达式

  • 我喜欢类似于喜欢除了不区分大小写

  • 不平等否定平等

  • InRange在。。之间Groovy范围的值

  • Rlike在MySQL或Oracle中执行Regexp LIKE,否则回退到喜欢

  • 之间两个值之间需要两个参数

  • IsNotNull非null值不接受参数

  • 一片空白是否为空值不带参数

请注意,与其余示例相比,后三个需要不同数量的方法参数,如以下示例所示

定义现在 日期()
定义现在上周7
定义findByReleaseDateBetween lastWeek现在预定findAllBy发行日期IsNull书籍findAllByReleaseDateIsNotNull

布尔逻辑与或

方法表达式也可以使用布尔运算符来组合两个或多个条件

定义findAllByTitleLikeAndReleaseDateGreaterThan"爪哇",  日期() - 30)

在这种情况下,我们使用在查询中间以确保同时满足两个条件,但您可以同样使用要么:

定义findAllByTitleLikeOrReleaseDateGreaterThan"爪哇",  日期() - 30)

您可以根据需要组合任意多个条件,但必须将它们全部与或全部要么如果需要结合要么或者,如果条件数创建了一个非常长的方法名,只需将查询转换为标准要么高品质询问

查询关联

关联也可以在查询中使用

定义作者作者findByName"斯蒂芬·金")

定义书籍作者findAllByAuthor作者[]

在这种情况下作者instance不为null,我们在查询中使用它来获取所有给定的实例作者.

分页和排序

相同的分页和排序参数可用清单通过提供地图作为最终参数,该方法也可以与动态查找器一起使用

定义findAllByTitleLike"哈里·波特",
               [最大值: 3, 抵销: 2, 分类: "标题", 订购: "描述"])

哪里查询

哪里方法基于对独立标准通过为常见查询提供增强的编译时检查查询DSL哪里该方法比动态查找器更灵活,比标准更少冗长,并且提供了一种强大的组合查询机制

基本查询

哪里方法接受一个与Groovy的常规集合方法非常相似的闭包。该闭包应使用常规Groovy语法定义逻辑条件,例如

定义查询Person的名字"巴特"人员巴特查询查找

返回的对象是分离标准实例,这意味着它与任何特定的数据库连接或会话均未关联。这意味着您可以使用哪里在类级别定义常见查询的方法

 烧烤美食

  {
    静态的分离标准辛普森一家,姓氏"辛普森"人员对每个人员进行印刷

查询执行是惰性的,仅在使用分离标准实例如果要立即执行where样式查询,则可以使用找到所有实现此目的的方法

定义结果人员findAll lastName"辛普森"
}
定义结果人员findAll分类:"名字""辛普森"人p人找到名字"巴特" }

每个Groovy运算符映射到常规条件方法下表提供了Groovy运算符与方法的映射

操作员 准则方法 描述

==

当量

等于

!=

不要

不等于

>

gt

比...更棒

<

lt

少于

>=

大于或等于

<=

小于或等于

inList

包含在给定列表中

==~

喜欢

像给定的字符串

=~

我喜欢

不区分大小写

可以使用常规的Groovy比较运算符和逻辑来制定复杂的查询

定义查询姓氏在哪里的人"辛普森"名字"弗雷德"名字"巴特"年龄9)
}
定义结果查询列表分类:"名字")

Groovy regex匹配运算符映射到like和ilike查询,除非右侧的表达式是图案在这种情况下,它们会映射到询问

定义查询Person的名字//
}
注意仅当基础数据库支持正则表达式时才支持查询

一种之间条件查询可以通过组合具有范围的关键字

定义查询年龄的人 18..65
}

最后你可以做一片空白isNotNull通过使用样式查询空值与常规比较运算符

定义查询Person中名空值
}

查询组成

既然返回值哪里方法是分离标准您可以从原始查询组成新查询的实例

分离标准查询姓氏在哪里的人"辛普森"分离标准bartQuery查询其中firstName"巴特"人p bartQuery查找

请注意,您不能将定义为变量的闭包传递给哪里除非已显式转换为分离标准换句话说,以下将产生错误

定义可调用的姓"辛普森"
}
定义查询可调用的人

以上内容必须写成如下

 365bet地区 Gorm DetachedCriteria

定义可调用的姓"辛普森"
} 分离标准
定义查询可调用的人

如您所见,闭包定义是使用Groovy强制转换的关键字分离标准针对的实例

析取与否定

如前所述,您可以组合常规Groovy逻辑运算符||&&形成合取和析取

定义查询姓氏在哪里的人"辛普森"名字"弗雷德"名字"巴特"年龄9)
}

您也可以使用否定逻辑比较!:

定义查询Person的名字"弗雷德"'辛普森')
}

物业比较查询

如果在比较表达式的左侧和右侧同时使用属性名称,则会自动使用适当的属性比较条件

定义查询人员,其中firstName lastName

下表描述了每个比较运算符如何映射到每个条件属性比较方法

操作员 准则方法 描述

==

eqProperty

等于

!=

neProperty

不等于

>

gtProperty

比...更棒

<

lt属性

少于

>=

财产

大于或等于

麻风病

小于或等于

查询关联

可以通过使用点运算符来指定要查询的关联的属性名称来查询关联

定义查询拥有者名字的宠物""所有者的名字"弗雷德"
}

您可以在闭包方法调用中将多个条件分组,其中方法名称与关联名称匹配

定义查询宠物名字的人"插口"那么"" }
}

该技术可以与其他顶级标准结合使用

定义查询宠物名字的人"插口"名字""
}

对于集合关联,可以对集合的大小应用查询

定义查询宠物大小的人2
}

下表显示了每个尺寸比较哪个运算符映射到哪个标准方法

操作员 准则方法 描述

==

sizeEq

集合大小等于

!=

尺寸Ne

集合大小不等于

>

大小

集合大小大于

<

sizelt

集合大小小于

>=

大小

集合大小大于或等于

要SIZEL

集合大小小于或等于

查询别名和排序

如果为关联定义查询,则会自动为该查询生成一个别名,例如以下查询

定义查询拥有者名字的宠物"弗雷德"
}

将为所有者诸如所有者别名这些生成的别名在大多数情况下都很好,但是如果您以后想要对结果进行排序或使用投影则没有用,例如,以下查询将失败

失败,因为使用了动态别名主人名字在哪里的宠物"弗雷德"清单分类:"所有者姓")

如果您打算对结果进行排序,则应使用显式别名,并且可以通过简单地在变量中声明一个变量来定义它们。哪里询问

定义查询宠物在哪里定义马上(1)o名字"弗雷德" (2)清单分类:'o姓氏') (3)
1 定义一个别名
2 在查询本身中使用别名
3 使用别名对结果进行排序

通过将关联的名称分配给局部变量,它将自动成为可在查询本身中使用的别名,也可用于对结果进行排序或投影

子查询

可以在其中查询的地方执行子查询,例如,查找所有比平均年龄大的人,可以使用以下查询

最后查询平均年龄的人

下表列出了可能的子查询

方法 描述

平均

所有值的平均值

所有值的总和

最大值

最大值

最小值

计数

所有值的计数

属性

您可以使用来将附加条件应用于任何子查询方法并传入包含条件的闭包

定义查询其平均姓氏年龄的人"辛普森"名字"荷马"
}

自从属性子查询返回多个结果,使用的条件将所有结果进行比较。例如,以下查询将查找所有比姓Simpson的人年轻的人

姓氏的年龄财产年龄的人"辛普森" }
}

GORM中的更多高级子查询

对子查询的支持已扩展,现在可以与嵌套子查询一起使用

定义结果名字在哪里的人年龄18名字列表

条件和可以无缝混合查询的位置

定义结果符合条件的人不在"名字"年龄的人18名字

子查询可与投影一起使用

定义结果人年龄年龄18平均'')
}

可以使用跨两个域类的相关查询

定义员工员工所在地区 ['亚太地区', "欧洲,中东和非洲"ID定义结果员工在哪里销售员工总数100000员工名单

在简单查询中,使用简单变量声明支持别名交叉查询引用

定义查询员工在哪里定义员工存在销售地点定义销售定义返回你的身份证你的身份证定义结果查询列表

其他功能

在查询的上下文中有几种可用的功能,这些功能汇总在下表中

方法 描述

第二

日期属性的第二个

分钟

日期属性的分钟

小时

日期属性的小时

日期属性的日期

日期属性的月份

日期属性的年份

降低

将字符串属性转换为小写

将字符串属性转换为大写

长度

字符串属性的长度

修剪

修剪字符串属性

当前,函数只能应用于域类的属性或关联。例如,您不能在子查询的结果上使用函数

例如,以下查询可用于查找所有出生于的宠物

定义查询宠物出生年份2011
}

您还可以将函数应用于关联

定义查询年份宠物出生日期的人2009
}

批量更新和删除

由于每个哪里方法调用返回一个分离标准您可以使用的实例哪里执行批处理操作(例如批更新和删除)的查询。例如,以下查询将更新姓Simpson的所有人的姓氏Bloggs

分离标准查询姓氏在哪里的人'辛普森'
}
整型总查询updateAll:"博客")
请注意,关于批处理操作的一个限制是不允许查询关联的联接查询

要批量删除记录,您可以使用删除所有方法

分离标准查询姓氏在哪里的人'辛普森'
}
整型总查询deleteAll

标准

条件是一种高级查询方法,它使用Groovy构建器构造可能复杂的查询,这比使用StringBuilder.

条件可以与createCriteria要么方法

构建器使用Hibernate的Criteria API。此构建器上的节点映射在Hibernate的Criteria API中找到的静态方法。限制条件Hibernate Criteria API的类,例如

定义c帐户createCriteria定义结果之间"平衡", 500, 1000当量"", "伦敦"或喜欢"保持名字", "弗雷德"喜欢"保持名字", "巴尼"maxResults10订购"保持姓氏", "描述")
}

此条件将选择最多帐户列表中符合以下条件的对象

  • 平衡在和之间

  • 伦敦

  • 保持名字以。。开始弗雷德要么巴尼

结果将按降序排列保持姓氏.

如果没有找到符合上述条件的记录,则返回一个空列表

连词和析取词

如前面的示例所示,您可以使用要么

或之间"平衡", 500, 1000当量"", "伦敦")
}

这也适用于逻辑与

和之间"平衡", 500, 1000当量"", "伦敦")
}

您也可以使用逻辑NOT取反

不介于"平衡", 500, 1000当量"", "伦敦")
}

暗含所有顶级条件

查询关联

可以通过具有与属性名称匹配的节点来查询关联。例如,说帐户全班有很多交易对象

 帐户静态hasMany交易交易

我们可以使用属性名称查询此关联交易作为建造者节点

def c帐户createCriteria def现在 日期def结果c列出了之间的交易'日期'现在10现在

上面的代码将找到所有帐户已执行的实例交易在过去的几天内,您还可以在逻辑块中嵌套此类关联查询

def c帐户createCriteria def现在 日期def结果c列表或之间'被创造'现在10现在之间的交易'日期'现在10现在

在这里,我们找到所有在过去几天执行过交易或最近在最近几天创建过的帐户

查询投影

可以使用投影来定制结果。在条件构建器树中定义一个投影节点以使用投影。投影节点内的方法与Hibernate中的方法等效。投影

def c帐户createCriteria def numberOfBranches c获取投影数countDistinct'')
    }
}

当投影中指定了多个字段时,将返回值列表。否则将返回单个值

转换投影结果

结果转换器假设我们要将标准结果转换为Map,以便我们可以轻松地通过key引用值

def c帐户createCriteria def accounts概述c获取结果变压器标准规范实体地图的投影总和'平衡', '全部平衡'countDistinct'保持姓氏', '姓氏')
    }
}

帐户概览全部
帐户概述姓氏

请注意,我们已经为每个投影添加了一个别名作为要用作键的附加参数。要执行此操作,所有投影都必须定义别名,否则将不会构建对应的地图条目

我们还可以通过将结果转换为我们选择的对象变形金刚aliasToBean方法在这种情况下,我们将其转换为帐户概述:

 帐户概述 {
    全部平衡姓氏
def c Account createCriteria def accountsOverview c get resultTransformer Transformers aliasToBean AccountsOverview投影总和'平衡', '全部平衡'countDistinct'保持姓氏', '姓氏')
    }
}

accountsOverview实例的AccountsOverview

每个别名必须在Bean上具有一个对应的属性或显式的setter,否则将引发异常

SQL投影

DSL标准提供对Hibernate的SQL投影API的访问

Box是一个域类
  {
    整型宽度整型高度
使用SQL投影来检索所有Box实例的周长和面积定义createCriteria def结果c列表投影sqlProjection'宽度高度作为周边宽度高度作为面积', ['', '区域'整数整数

第一个参数sqlProjection方法是定义投影的SQL。第二个参数是一个字符串列表,表示与SQL中表示的投影值相对应的列别名。第三个参数是以下各项的列表:org休眠类型类型与SQL中表示的预计值相对应的实例API支持所有org休眠类型类型DSL提供了对象,但常量(如INTEGER LONG FLOAT等)由DSL提供,这些常量对应于组织休眠类型StandardBasicTypes.

考虑到下表代表了

宽度 高度

2

7

2

8

2

9

4

9

上面的查询将返回这样的结果

[[18, 14], [20, 16], [22, 18], [26, 36]]

每个内部列表包含每个项目的预测值周长和面积

请注意,如果范围中存在其他引用,无论您表达的条件查询的名称与上述任何类型常量冲突,则条件中的代码都将引用那些引用,而不是DSL提供的类型常量。您可以通过引用完全限定的Hibernate类型来消除冲突,例如StandardBasicTypes整数代替整数.

如果仅投影值,则别名和类型不需要包含在列表中

def结果c列表投影sqlProjection'总宽度高度总面积', 'totalArea'整数

该查询将返回单个结果,其值为作为所有实例

DSL通过sqlGroupProjection方法

def结果c列表投影sqlGroupProjection'width和height的总和', '宽度', ['宽度', 'CombineHeightsForThisWidth'整数整数

第一个参数sqlGroupProjection方法是定义投影的SQL。第二个参数表示应该作为查询一部分的group by子句。该字符串可以是单个列名,也可以是逗号分隔的列名列表。第三个参数是代表列别名的字符串列表。对应于SQL中表示的预计值第四个参数是org休眠类型类型实例与SQL中表示的预计值相对应

上面的查询正在投影按宽度分组的框的组合高度,并将返回如下所示的结果

[[2, 24], [4, 9]]

每个内部列表包含值。第一个值是框的宽度,第二个值是所有具有该宽度的框的高度之和

使用SQL限制

您可以访问Hibernate的SQL限制功能

def c Person createCriteria def peopleWithShortFirstNames c列出sqlRestriction"字符长度名字"
}

可以将SQL限制参数化以处理与动态限制有关的SQL注入漏洞

def c Person createCriteria def peopleWithShortFirstNames c列出sqlRestriction"字符长度名和字符长度名"maxValue minValue
注意,该参数存在SQL名字示例中引用的属性是指持久性模型,而不是像HQL查询中那样的对象模型。属性命名名字映射到名字数据库中的列,您必须在sqlRestriction

还要注意,这里使用的SQL不一定可以跨数据库移植

使用滚动结果

ScrollableResults通过调用滚动方法的功能

def结果暴击滚动maxResults10def f结果第一def l结果最后def n结果下一个def p结果上一个def将来的结果滚动10def accountNumber结果getLong'')

引用Hibernate ScrollableResults的文档

一个结果迭代器,它允许在结果中以任意增量移动

与JDBC列相反,结果从零开始编号

在条件实例中设置属性

如果构建器树中的节点与特定条件不匹配,它将尝试在Criteria对象本身上设置属性。这允许完全访问此类中的所有属性,此示例调用setMaxResultssetFirstResult标准实例

 组织休眠的FetchMode  调频
...
定义 结果 = c清单 {
    maxResults(10)
    firstResult(50)
    fetchMode("", 调频加入)
}

急切查询

在关于渴望和懒惰获取我们讨论了如何以声明方式指定访存以避免N SELECT问题。但是,这也可以使用条件查询来实现

def标准任务createCriteria def任务标准列表eq"受让人ID"任务受让人ID加入'受让人'加入'项目'订购'优先', 'ASC'
}

请注意加入告诉条件API使用的方法加入以获取与任务实例最好不要将它用于一对多关联,因为您很可能最终会得到重复的结果,而应使用选择提取模式

 组织休眠的FetchMode  调频def结果Airport withCriteria eq"区域", "欧洲,中东和非洲"fetchMode"航班"调频选择

尽管此方法会触发第二个查询来获取航班关联,即使使用maxResults选项

fetchMode加入是查询的常规设置,只能在顶层指定,即您不能在投影或关联约束内使用它们

需要牢记的重要一点是,如果您在查询约束中包含关联,则会自动急切加载这些关联。例如,在此查询中

定义结果机场withCriteria eq"区域", "欧洲,中东和非洲""", "BA"
    }
}

航班即使未明确设置获取模式,集合也将通过连接快速加载

方法参考

如果您调用的构建器没有方法名称,例如

C

该版本默认列出所有结果,因此上述内容等同于

c清单
方法 描述

清单

这是默认方法,它返回所有匹配的行

得到

返回唯一的结果集,即仅一行。标准必须以仅查询一行的方式形成:此方法不应与仅限于第一行的限制相混淆

滚动

返回可滚动的结果集

listDistinct

如果使用子查询或关联,则可能在结果集中多次出现同一行。这允许仅列出不同的实体,并且等效于不同的根实体条件规格

计数

返回匹配的行数

合并标准

您可以通过以下方式组合多个条件闭包

定义标准"区域", "欧洲,中东和非洲"def结果Airport withCriteria emeaCriteria代表代表emeaCriteria航班如"", "BA"
    }
}

此技术要求每个条件必须引用相同的域类,即飞机场一种更灵活的方法是使用独立标准,如以下部分所述

独立标准

365bet地区分离标准是与任何给定的数据库会话连接都不关联的条件查询,受支持,因为365bet地区分离标准查询具有许多用途,包括允许您创建常见的可重用条件查询,执行子查询和执行批处理更新删除。

建立独立的标准查询

使用分离标准的主要切入点是分离标准接受域类作为其构造函数的唯一参数的类

 烧烤美食定义标准独立标准人

一旦获得对分离标准实例的引用,就可以执行哪里查询或条件查询以构建适当的查询。要构建常规条件查询,您可以使用建立方法

定义标准DetachedCriteria人员构建方程'', '辛普森'
}

请注意,分离标准实例不要更改原始对象,但返回一个新查询。换句话说,您必须使用的返回值建立获取变异标准对象的方法

定义标准DetachedCriteria人员构建方程'', '辛普森'
}
定义bartQuery条件构建eq'名字', '巴特'
}

执行分离的标准查询

与常规条件不同,分离条件是懒惰的,因为在定义点不执行任何查询。一旦构造了分离条件查询,则存在许多有用的查询方法,下表中总结了这些方法

方法 描述

清单

列出所有匹配的实体

得到

返回单个匹配结果

计数

计算所有匹配记录

存在

如果存在任何匹配记录,则返回true

删除所有

删除所有匹配的记录

updateAll地图

例如,以下代码将列出按名字属性

定义标准DetachedCriteria人员构建方程'', '辛普森'
}
定义结果标准清单最大值:4, 分类:"名字")

您还可以为列表方法提供其他条件

定义结果标准清单最大值:4, 分类:"名字"gt'', 30
}

要检索单个结果,可以使用得到要么同义词的方法

人员p标准找到或条件得到

分离标准类本身也实现了可迭代的接口,这意味着可以将其视为列表

定义标准DetachedCriteria人员构建方程'', '辛普森'每个println的标准名字

在这种情况下,查询仅在方法被称为同样适用于所有其他Groovy集合迭代方法

分离标准就像在域类上一样

定义标准DetachedCriteria人员构建方程'', '辛普森'
}
定义巴特条件findByFirstName"巴特")

对子查询使用分离的条件

在常规条件查询的上下文中,您可以使用分离标准执行子查询例如,如果您要查找所有比平均年龄大的人,则以下查询将完成该操作

定义结果符合条件的人gt"", DetachedCriteria人员构建投影平均值""订购"名字"
 }

请注意,在这种情况下,子查询类与原始条件查询类相同因此查询可以缩短为

定义结果符合条件的人gt""投影平均""订购"名字"
 }

如果子查询类与原始条件查询不同,则必须使用原始语法

在上一个示例中,投影确保平均年龄仅返回一个结果。如果您的子查询返回多个结果,则需要使用不同的准则方法来比较结果,例如查找所有年龄超过一种gtAll可以使用查询

定义结果符合条件的人gtAll""投影属性""之间'', 18, 65订购"名字"
}

下表总结了对返回多个结果的子查询进行操作的标准方法

方法 描述

gtAll

大于所有子查询结果

杰尔

大于或等于所有子查询结果

少于所有子查询结果

全部

小于或等于所有子查询结果

eqAll

等于所有子查询结果

网络

不等于所有子查询结果

具有分离条件的批处理操作

分离标准该类可用于执行批处理操作,例如批处理更新和删除。例如,以下查询将更新姓氏为Simpson的所有人的姓氏为Bloggs

定义标准DetachedCriteria人员构建方程'', '辛普森'
}
整型总标准updateAll:"博客")
请注意,关于批处理操作的一个限制是:在查询中不允许使用查询关联的联接查询分离标准实例

要批量删除记录,您可以使用删除所有方法

定义标准DetachedCriteria人员构建方程'', '辛普森'
}
整型总标准deleteAll

休眠查询语言HQL

GORM类还支持Hibernate的查询语言HQL,可以找到非常完整的参考。在Hibernate文档中的Hibernate文档

GORM提供了许多适用于HQL的方法,包括, 找到所有executeQuery.

查询示例如下

找到所有"摘自Book as b,其中b的标题像the Lord of")

命名参数

在这种情况下,传递给查询的值是硬编码的,但是您同样可以使用命名参数

找到所有"从Book as b" +
                   "其中b标题(如search)或b author(如search)"搜索"the shi"])
def作者作者findByName"斯蒂芬·金"def书找到所有"从本书作为本书作者的作者"作者

多行查询

使用三引号引起来的字符串将查询分隔为多行

找到所有"""来自Book as b作者as a,其中b author a和一个姓氏"""'史密斯'])

分页和排序

您还可以在使用HQL查询时执行分页和排序。为此,只需在方法调用的末尾将分页选项指定为Map,并在HQL中包含ORDER BY子句

找到所有"从Book as b那里" +
                   "b标题之类的" +
                   "按b标题升序排列"最大值10抵销20])

先进的GORM功能

以下各节介绍了GORM的更高级用法,包括缓存自定义映射和事件

活动和自动时间戳

GORM支持将事件注册为在发生某些事件(例如删除插入和更新)时会触发的方法。以下是受支持的事件的列表

  • beforeInsert在对象最初保留到数据库之前执行。如果返回false,则插入将被取消

  • 更新之前在更新对象之前执行。如果返回false,则更新将被取消

  • beforeDelete在删除对象之前执行。如果返回false,则删除将被取消

  • 验证之前在验证对象之前执行

  • afterInsert对象持久化到数据库后执行

  • 之后更新对象更新后执行

  • afterDelete删除对象后执行

  • 负载从数据库加载对象时执行

要添加事件,只需在您的域类中注册相关方法

不要尝试在诸如obj save flush true之类的事件中刷新会话,因为在刷新过程中触发了事件,这将导致StackOverflowError

beforeInsert事件

在将对象保存到数据库之前触发

  {
   私人的 静态的 最后 日期空日期 日期(0)

   名字日期signupDate NULL DATE def beforeInsert如果signupDate NULL DATE signupDate 日期()
      }
   }
}

beforeUpdate事件

在更新现有对象之前触发

 def securityService名字lastUpdatedBy静态的约束lastUpdatedBy可为空def beforeUpdate lastUpdatedBy securityService currentAuthenticatedUsername

beforeDelete事件

在删除对象之前触发

  {
   将def命名为DeleteDelete ActivityTrace withNewSessionActivityTrace eventName"人已删除"数据名称保存

请注意withNewSession上面的方法由于事件是在Hibernate刷新时使用持久性方法(例如,保存删除除非您使用新的操作运行操作,否则不会导致对象被保存届会.

幸运的是withNewSession方法使您可以共享相同的事务性JDBC连接,即使您使用的是不同的基础届会.

beforeValidate事件

  {
   静态的约束名称大小5..45def beforeValidate名称名称修剪

验证之前在运行任何验证程序之前运行方法

验证的运行频率可能比您想象的要高,它是由验证保存您所期望的方法,但是通常也会在呈现视图之前触发它,因此在编写时验证之前实现确保它们可以处理具有相同属性值的多次调用

GORM支持的重载版本验证之前接受一个清单参数,其中可能包含将要验证的属性的名称验证之前验证方法已被调用并传递了一个清单属性名称作为参数

  {
   整数静态的约束名称大小5..45年龄范围4..99def beforeValidate清单propertiesBeingValidated根据propertiesBeingValidated进行预验证工作定义人名'雅各布·布朗'年龄10p验证'', ''])
请注意,当验证是由于对的调用而间接触发的该方法验证没有参数的情况下调用方法清单包括所有属性名称

的一个或两个版本验证之前可以在域类中定义,GORM会更喜欢清单版本,如果清单被传递给验证但如果使用清单版本不存在同样,如果未将任何参数传递给GORM,GORM将首选no arg版本验证但会落在清单如果没有arg版本,则显示版本号空值被传递给验证之前.

onLoad beforeLoad事件

从数据库加载对象之前立即触发

  {
   日期创建日期日期lastUpdated def onLoad日志调试"载入编号"
   }
}

beforeLoad实际上是的同义词负载所以只能声明一个或另一个

afterLoad事件

从数据库加载对象后立即触发

  {
   日期创建日期日期lastUpdated def afterLoad名称"我装了"
   }
}

自定义事件监听器

要注册自定义事件侦听器,您需要子类化AbstractPersistenceEventListener包装内组织grails数据存储区映射引擎事件并实施方法onPersistenceEventsupportsEventType您还必须向侦听器提供对数据存储的引用。最简单的实现如下所示

上市MyPersistenceListener最后数据存储数据存储数据存储覆写
受保护的 虚空onPersistenceEvent最后AbstractPersistenceEvent事件开关事件eventType案件 预插入打印"预先插入\$事件entityObject"
        打破
        案件 插入后打印"插入后\$事件entityObject"
        打破
        案件 预更新打印"预先更新\$事件entityObject"
        打破;
        案件 PostUpdate打印"发布更新\$事件entityObject"
        打破;
        案件 预删除打印"预先删除\$事件entityObject"
        打破;
        案件 PostDelete打印"\$事件entityObject"
        打破;
        案件 预载打印"预载\$事件entityObject"
        打破;
        案件 后加载打印"发布后\$事件entityObject"
        打破;
    }
}

覆写
上市 布尔值supportsEventType延伸ApplicationEvent eventType返回 
}

AbstractPersistenceEvent类有很多子类PreInsertEvent, PostInsertEvent提供有关事件A的进一步信息的等取消事件中还提供了方法,使您可以否决插入更新或删除操作

创建事件监听器后,您需要对其进行注册。如果您使用的是Spring,则可以通过ApplicationContext:

HibernateDatastore数据存储applicationContext getBean HibernateDatastore applicationContext addApplicationListenerMyPersistenceListener数据存储

如果您不使用Spring,则可以使用来注册事件监听器获取ApplicationEventPublisher方法

HibernateDatastore数据存储获取对数据存储的引用数据存储区getApplicationEventPublisher addApplicationListenerMyPersistenceListener数据存储

休眠事件

通常建议使用上述非Hibernate特定的API,但是如果您需要访问更详细的Hibernate事件,则可以定义自定义的Hibernate特定事件侦听器

您也可以在应用程序中注册事件处理程序类grails app conf春季资源groovy或在doWithSpring通过注册名为Spring bean的插件描述符中的闭包hibernateEventListeners这个豆有一个属性listenerMap它指定侦听器以注册各种Hibernate事件

Map的值是实现一个或多个Hibernate侦听器接口的类的实例。您可以使用一个实现所有必需接口的类,也可以使用每个接口一个具体的类,或者任意组合。此处列出了有效的Map键和相应的接口。

name 接口

自动冲洗

AutoFlushEventListener

MergeEventListener

创建

PersistEventListener

创建冲洗

PersistEventListener

DeleteEventListener

脏支票

DirtyCheckEventListener

逐出

EvictEventListener

齐平

FlushEventListener

同花顺实体

FlushEntityEventListener

加载

LoadEventListener

负荷收集

InitializeCollectionEventListener

锁等待监听器

刷新

RefreshEventListener

复制

ReplicateEventListener

保存更新

SaveOrUpdateEventListener

SaveOrUpdateEventListener

更新

SaveOrUpdateEventListener

预载

PreLoadEventListener

预更新

PreUpdateEventListener

删除

PreinstallEventListener

预插入

收集前重建

PreCollectionRecreateEventListener

预先收集删除

PreCollectionRemoveEventListener

收集前更新

PreCollectionUpdateEventListener

加载后

PostLoadEventListener

发布更新

PostUpdateEventListener

发表删除

PostDeleteEventListener

帖子插入

PostInsertEventListener

提交后更新

PostUpdateEventListener

提交后删除

PostDeleteEventListener

提交后插入

PostInsertEventListener

帖子收集重新创建

PostCollectionRecreateEventListener

帖子集删除

PostCollectionRemoveEventListener

帖子收集更新

PostCollectionUpdateEventListener

例如,您可以注册一个课程AuditEventListener实施PostInsertEventListener, PostUpdateEventListenerPostDeleteEventListener在应用程序中使用以下内容

bean auditListener AuditEventListener hibernateEventListeners HibernateEventListeners listenerMap'帖子插入'auditListener'发布更新'auditListener'发表删除'auditListener

或在插件中使用

定义doWithSpring auditListener AuditEventListener hibernateEventListeners HibernateEventListeners listenerMap'帖子插入'auditListener'发布更新'auditListener'发表删除'auditListener

自动时间戳

如果您定义创建日期属性,当您创建新实例时,它将被设置为您的当前日期。最近更新时间属性,当您更改持久实例时,它将自动为您更新

如果这不是您想要的行为,则可以通过以下方式禁用此功能

  {
   日期创建日期日期最近更新时间静态的映射autoTimestamp
   }
}
如果你有可为空的false约束之一创建日期要么最近更新时间您的域实例将无法通过验证,可能不是您想要的结果。除非您禁用自动时间戳,否则从这些属性中忽略约束

也可以暂时禁用自动时间戳记,这通常是在需要定义参数值的测试中完成的。创建日期要么最近更新时间过去对于从其他要保留时间戳的当前值的系统中导入旧数据也可能很有用

可以暂时禁用所有域的时间戳,指定的域列表或单个域。要开始使用,您需要获取对AutoTimestampEventListener如果您已经有权访问数据存储,则可以执行getAuto时间戳事件侦听器方法如果您无权访问数据存储区,请注入autoTimestampEventListener豆角,扁豆

引用事件侦听器后,即可执行没有创建日期, 没有最后更新要么没有时间戳没有时间戳方法将暂时禁用两者创建日期最近更新时间.

仅对Foo域禁用datedated属性处理不带DateCreated Foo的autoTimestampEventListeneroo创建日期:  日期() - 1保存齐平: )
}

仅对Foo和Bar域禁用lastlasted属性处理没有最后更新的Foo Bar的autoTimestampEventListeneroo最近更新时间:  日期() - 1, 酒吧: 酒吧最近更新时间:  日期() + 1保存齐平: )
}

所有域的所有时间戳属性处理将被禁用没有时间戳的autoTimestampEventListeneroo创建日期:  日期() - 2, 最近更新时间:  日期() - 1保存齐平: )
    酒吧创建日期:  日期() - 2, 最近更新时间:  日期() - 1保存齐平: )
    FooBar创建日期:  日期() - 2, 最近更新时间:  日期() - 1保存齐平: )
}
因为仅在关闭期间禁用时间戳处理,所以您必须在关闭执行期间刷新会话

自定义ORM映射

可以使用对象关系映射DSL域特定语言将GORM域类映射到许多旧模式上,以下各节将带您了解ORM DSL可能实现的功能

如果您愿意遵守由GORM为表名,列名定义的约定,则无此必要,仅当您需要定制GORM映射到旧模式或配置缓存的方式时,才需要此功能。

使用静态定义自定义映射映射在您的网域类别中定义的区块

 静态映射版本自动时间戳
    }
}

您还可以在以下位置配置全局映射应用程序或使用此设置的外部配置文件

grails gorm默认映射版本自动时间戳
}

与标准语法相同映射块,但它适用于您的所有域类,然后您可以在映射域类的块

表和列名

表名

类映射到的数据库表名称可以使用方法

 静态映射表''
    }
}

在这种情况下,该类将映射到一个名为而不是默认名称.

列名

还可以自定义各个列到数据库的映射,例如更改名称,您可以执行

  {

    名字静态的映射表''firstName列'名字'
    }
}

这里名字映射具有单个Map参数的闭包由于其名称与域类持久字段相对应,因此在这种情况下,参数值仅用于配置该属性的映射

列类型

GORM支持使用type属性通过DSL配置Hibernate类型,包括指定实现Hibernate的用户类型。org休眠用户类型UserType界面,可以完全自定义类型的持久性例如,如果您有一个PostCodeType您可以按以下方式使用它

 地址 {

    邮编静态的映射postCode类型PostCodeType

另外,如果您只想将其映射到Hibernate的一种基本类型,而不是GORM选择的默认类型,则可以使用

 地址 {

    邮编静态的'文本'
    }
}

这将使邮编列映射到您正在使用的数据库的默认大文本类型,例如TEXT或CLOB

请参阅有关以下内容的Hibernate文档:基本类型了解更多信息

多对一一对一映射

在关联的情况下,还可以配置用于映射关联的外键。在多对一或一对一关联的情况下,这与任何常规列完全相同。例如,考虑以下内容

  {

    名字地址地址静态的映射表''firstName列'名字'地址栏'人员地址ID'
    }
}

默认情况下地址关联将映射到名为地址编号通过使用上面的映射,我们将外键列的名称更改为人员地址ID.

一对多映射

对于双向一对多,您可以通过更改关联的多侧上的列名来更改所使用的外键列,如上一节中一对一关联的示例所示。但是,对于单向关联,需要指定外键关于关联本身例如,给定之间的单向一对多关系地址以下代码将更改地址

  {

    名字静态的hasMany地址地址静态的映射表''firstName列'名字'地址栏'人员地址ID'
    }
}

如果您不希望该列位于地址表,但可以使用一些中间联接表可接合参数

  {

    名字静态的hasMany地址地址静态的映射表''firstName列'名字'地址joinTable名称'人员地址''人ID''地址编号']
    }
}
多对多映射

默认情况下,GORM使用联接表映射多对多关联例如,考虑此多对多关联

 静态有很多人
 静态归属于
    静态的有许多团体]
}

在这种情况下,GORM将创建一个名为组人包含称为人名群组编号引用表要更改列名,可以在每个类的映射中指定一列

 静态映射人列'组人ID'
   }
}
 静态映射组列'群组群组ID'
   }
}

您还可以指定要使用的联接表的名称

 静态映射人列'组人ID'可接合'个人团体协会'
   }
}
 静态映射组列'群组群组ID'可接合'个人团体协会'
   }
}

缓存策略

设置缓存

冬眠具有可自定义的缓存提供程序的二级缓存,需要在grails应用配置会议应用yml

:
  快取:
    使用二级缓存: 
    提供者类别net SF ehcache休眠EhCacheProvider区域:
       工厂级组织休眠休眠ehcache EhCacheRegionFactory

您可以自定义任何这些设置,例如使用分布式缓存机制

要进一步了解缓存,尤其是Hibernate的二级缓存,请参阅休眠文档就此主题而言
缓存实例

致电快取映射块中的方法启用默认设置缓存

 静态映射表''快取
    }
}

这将配置一个读写包含惰性和非惰性属性的缓存您可以进一步自定义此属性

 静态映射表''缓存使用率'只读'包括'不偷懒'
    }
}
缓存关联

除了使用Hibernate的二级缓存来缓存实例的功能之外,您还可以缓存对象的集合关联。例如

  {

    名字静态的hasMany地址地址静态的映射表''地址栏'地址'快取
    }
}
 地址 {
    邮编

这将使读写缓存机制地址您也可以使用收藏

快取'读写' 或只读或事务性

进一步配置缓存使用率

缓存查询

为了缓存查询结果,必须在映射中启用缓存

:
  快取:
    使用查询缓存: 

要为动态查找器GORM等创建的所有查询启用查询缓存,您可以指定

:
  快取:
    查询:     #这隐式设置`使用查询缓存`

您可以缓存查询,例如动态查找器和条件。要使用动态查找器,您可以传递快取论点

def person人员findByFirstName"弗雷德"快取])

您还可以缓存条件查询

def people具有条件的人'名字', 'r'快取
}
缓存使用

以下是不同缓存设置及其用法的说明

  • 只读如果您的应用程序需要读取但从不修改持久类的实例,则可以使用只读缓存

  • 读写如果应用程序需要更新数据,则可以使用读写缓存

  • 非严格读写如果应用程序仅偶尔需要更新数据,即如果两个交易不太可能尝试同时更新同一项目,并且不需要严格的交易隔离,则非严格读写缓存可能是合适的

  • 交易性的交易性的缓存策略为完全事务性的缓存提供程序(例如JBoss TreeCache)提供支持。此类缓存只能在JTA环境中使用,您必须指定休眠事务管理器查找类在里面grails应用配置会议应用程序文件配置

继承策略

默认情况下,GORM类使用每个层次结构的表非空在数据库级别应用于他们的约束如果您希望使用每个子类表您可以按照以下的继承策略进行操作

 付款 {
    整数静态的映射表
    }
}

 信用卡付款 延伸付款卡号

根的映射付款类指定它将不使用每个层次结构的表所有子类的映射

自定义数据库身份

您可以自定义GORM如何使用DSL生成数据库标识符的方式。默认情况下,GORM依赖于本机数据库机制来生成ID。到目前为止,这是最好的方法,但是仍然有许多模式具有不同的身份标识方法

为了解决这个问题,Hibernate定义了ID生成器的概念,您可以自定义ID生成器及其映射到的列,如下所示

 静态映射表''id生成器'螺纹'参数表'喜值''下一个值'最大低100]
    }
}

在这种情况下,我们将使用内置的Hibernate之一螺纹使用单独表生成ID的生成器

有关不同的Hibernate生成器的更多信息,请参见Hibernate参考文档

尽管通常不指定ID字段GORM为您添加了它,您仍然可以像其他属性一样配置其映射。例如,可以为id属性自定义列

 静态映射表''id栏'人名'
    }
}

GORM支持组合标识符的概念,该标识符由多个属性或多个属性组成。这不是我们建议的方法,但如果需要,可以使用

 org apache commons lang builder HashCodeBuilder

  实施 可序列化 {

    名字布尔值等于其他如果其他实例返回 其他firstName firstName其他lastName lastName整型hashCode定义建造者HashCodeBuilder构建器附加firstName构建器附加lastName构建器至HashCode静态的映射ID综合: ['名字', '']
    }
}

上面将创建一个复合ID名字Person类的属性要使用id检索实例,请使用对象本身的原型

def p人得到人名"弗雷德""打火石"println p firstName

用复合主键映射的域类必须实现可序列化接口并覆盖等于hashCode使用组合键中的属性进行计算的方法上面的示例使用HashCodeBuilder为了方便起见,但您可以自己实施

使用复合主键时的另一个重要考虑因素是关联。例如,如果您有一个多对一关联,其中外键存储在关联表中,则列将出现在关联表中

例如考虑以下域类

 地址人人

在这种情况下地址人名人的姓如果您希望更改这些列的映射,则可以使用以下技术进行更改

 地址人人静态的映射列人列: "名字": ""
                        }
        }
    }
}

数据库索引

为了从查询中获得最佳性能,通常有必要调整表索引定义的方式:如何定制它们是特定于域的,以及如何监控查询的使用模式?通过GORM DSL,您可以指定在哪些索引中使用哪些列

  {
    名字地址静态的映射表''id栏'人名'firstName列'名字'指数'名称IDx'地址栏'地址'指数'名称IDx地址索引'
    }
}

请注意,您不能在指数在此示例中的属性索引名称Idx地址索引会导致错误

乐观锁定和版本控制

如上一节所述乐观锁和悲观锁默认情况下,GORM使用乐观锁定并自动注入属性映射到每个类中,每个类又映射到一个数据库级别的列

如果您重新映射到没有版本列的旧模式,或者由于某些其他原因而不需要此功能,则可以使用方法

 静态映射表''
    }
}
如果您禁用乐观锁定,那么就并发更新而言,您基本上是自己一个人,除非您使用,否则用户有可能因数据覆盖而丢失数据悲观锁
版本列类型

默认情况下,GORM会映射作为财产long每次更新实例时,它会增加一,但是Hibernate还支持使用时间戳记例如

 sql时间戳

  {

    ...
    时间戳记 

    静态的 映射 = {
         ''
    }
}

在快速服务器上几乎同时发生的两个更新可能会以相同的时间戳记值结束,但是存在很小的风险,但是这种风险非常低。时间戳记代替long是您将乐观锁定和最后更新的语义组合到了单个列中

渴望和懒惰获取

懒惰的收藏

如上一节所述渴望和懒惰获取GORM集合默认情况下是延迟加载的,但是您可以使用ORM DSL更改此行为。您可以使用几种选项,但最常见的是

  • 懒惰的假

  • 加入

他们像这样被重新使用

  {

    名字宠物宠物静态的hasMany地址地址静态的懒惰的映射地址宠物取'加入'
    }
}
 地址 {
    邮编
 宠物 {
    那么

第一种选择懒惰的假确保当实例已加载地址集合使用第二个SELECT同时加载。第二个选项基本相同,除了该集合使用JOIN而不是另一个SELECT加载。通常,您希望减少查询数量,因此取得加入是更合适的选择另一方面,如果您的域模型和数据产生的结果比其他必要的结果更多和更大,则可能是更昂贵的方法

对于更高级的用户,其他可用设置是

  • batchSize N

  • 懒惰的错误batchSize N

其中N是整数这些使您可以分批获取结果,每批查询一个。作为一个简单的示例,请考虑以下映射::

  {

    名字宠物宠物静态的测绘宠物batchSize: 5
    }
}

如果查询返回多个实例,然后当我们访问第一个宠物Hibernate属性将获取宠物加上接下来的四个,您可以通过组合快速加载而获得相同的行为batchSize懒惰的假选项

您可以在休眠用户指南请注意,ORM DSL当前不支持子选择获取策略

懒惰单端关联

在GORM中,默认情况下一对一和多对一关联是惰性的。在加载许多实体时,非惰性单端关联可能会出现问题,因为每个非惰性关联都将导致一个额外的SELECT语句。如果关联的实体也具有非惰性关联,则数字的查询数量显着增长

使用与懒惰收集相同的技术使一对一或多对一关联非懒惰渴望

  {
    名字
 地址 {

    邮编静态的属于人人静态的映射人懒
    }
}

在这里,我们将GORM配置为加载关联的通过实例每当有财产地址已加载

懒惰的单端关联和代理

Hibernate使用运行时生成的代理来促进单端惰性关联Hibernate动态地对实体类进行子类化以创建代理

考虑前面的示例,但加载延迟休眠关联将设置属以下子类的代理的属性当您呼叫除吸气剂以外的任何吸气剂时IDHibernate上的属性或设置器将从数据库加载实体

不幸的是,这种技术会产生令人惊讶的结果。请考虑以下示例类

 宠物 {
    那么
  延伸宠物
  {
    名字宠物宠物

并假设我们有一个具有一个的实例作为宠物以下代码将按预期工作

定义人人得到1)
断言人宠物实例断言宠物得到人petId实例

但这不会

定义人人得到1)
断言人宠物实例断言宠物清单0] 实例

第二个断言失败,这会使工作更加混乱

断言宠物清单0] 实例

这是怎么回事,取决于代理的工作方式以及Hibernate会话提供的保证。实例Hibernate为其创建代理宠物关系并将其附加到会话上宠物带有查询的实例得到或者宠物关系在同一会话中Hibernate给您代理

对我们来说幸运的是,GORM在您使用时自动解封代理得到findBy或直接访问该关系时,这意味着在大多数情况下您完全不必担心代理,但是GORM不会为查询返回了诸如清单findAllBy但是,如果Hibernate尚未将代理附加到会话,则这些查询将返回真实实例,因此最后一个示例为何起作用

您可以使用instanceOfGORM方法

def person人员获取1)
断言宠物清单0instanceOf狗

但是,如果涉及到强制类型转换,则无济于事。例如,以下代码将引发ClassCastException因为列表中的第一个宠物是一个代理实例,该实例的类都不是不是...的子类:

def person人员获取1狗宠物宠物清单0]

当然,在这种情况下,最好不要使用静态类型。如果您为宠物使用无类型变量,则可以访问任何实例上的属性或方法没有任何问题

这些天来很少遇到这个问题,但是最好以防万一,至少要知道为什么会发生这种错误并能够解决它。

自定义级联行为

如上一节所述级联更新控制从一个关联到另一个关联的级联更新和删除级联的主要机制是静态属于属性

但是,ORM DSL使您可以完全访问Hibernate。传递持久性使用功能级联属性

级联属性的有效设置包括

  • 合并独立关联的状态

  • 保存更新级联仅保存和更新关联

  • 级联仅删除到关联

  • 如果应该将悲观锁与其关联关联起来,则很有用

  • 刷新级联刷新为关联

  • 逐出级联驱逐相当于丢弃在GORM中关联到关联(如果已设置)

  • 所有级联所有协会运作

  • 全部删除孤儿仅适用于一对多关联,并指示当从关联中删除某个孩子时,应将其自动删除。

要指定级联属性,只需将上述设置中一个或多个逗号分隔作为其值即可

  {

    名字静态的hasMany地址地址静态的映射地址级联"全部删除孤儿"
    }
}
 地址 {
    邮编

自定义休眠类型

您在前面的部分中看到可以将合成与嵌入式的属性,可以将表分为多个对象您可以使用Hibernate的自定义用户类型实现类似的效果这些不是域类本身,而是普通的Java或Groovy类。这些类型中的每一个还具有一个实现的相应元类型类。org休眠用户类型UserType.

休眠参考手册有一些关于自定义类型的信息,但是这里我们将重点介绍如何在GORM中映射它们。让我们首先看一个使用老式的Java之前的安全枚举类的简单域类。

  {

    标题作者评分评分静态的映射等级类型RatingUserType

我们要做的就是声明评分字段枚举类型,并将自定义映射中属性的类型设置为对应的用户类型实现这就是开始使用自定义类型所需要做的一切,如果需要,还可以使用其他列设置(例如column)来更改列名和索引,以将其添加到索引中

自定义类型不仅限于单个列,它们可以映射到任意数量的列。在这种情况下,您可以在映射中明确定义要使用的列,因为Hibernate只能将属性名称用于单个列,幸运的是,GORM使您可以使用此语法将多个列映射到属性

  {

    标题name作者评分评分静态的映射作者类型NameUserType列名称"名字"栏名""评分类型RatingUserType

上面的示例将为作者属性您将很高兴知道,您还可以在列定义中使用一些常规的列属性映射属性,例如

栏名"名字"指数"我的身份证"独特

列定义支持以下属性类型, 级联, , 快取可接合.

自定义类型要记住的一件事是它们定义了SQL类型对应的数据库列,这有助于您自己进行配置,但是如果您有一个旧数据库对其中一个列使用不同的SQL类型,会发生什么情况?在这种情况下,请使用sqlType属性

  {

    标题name作者评分评分静态的映射作者类型NameUserType列名称"名字"sqlType"文本"栏名""sqlType"文本"评级类型RatingUserType sqlType"文本"
    }
}

请注意,您指定的SQL类型仍需要与自定义类型一起使用,因此用文本覆盖varchar的默认值很好,但是用yes覆盖文本则行不通

派生属性

派生属性是一种经常从SQL表达式中获取其值的属性,但不一定基于一个或多个其他持久属性的值,请考虑这样的Product类

 产品 {
    浮动价钱浮动浮动

如果属性是根据的值得出的价钱属性,则可能不需要持久属性用于导出派生属性值的SQL可以像这样在ORM DSL中表示

 产品 {
    浮动价钱浮动浮动静态的映射税收公式'价格税率'
    }
}

请注意,ORM DSL中表示的公式是SQL,因此对其他属性的引用应与持久性模型有关,而不是对象模型,这就是该示例所引用的原因价钱税率代替价钱.

使用类似的东西检索产品时产品获得生成的用于支持的SQL将如下所示

选择产品编号id产品版本版本产品价格价格产品税率税收产品价格产品税率产品ID中产品ID的公式

自从属性是在运行时派生的,并且不存储在数据库中,通过添加类似的方法似乎可以达到相同的效果getTax产品该类仅返回的乘积价钱属性使用类似的方法,您将放弃根据数据库的值查询数据库的能力。属性使用派生属性可以完全检索所有产品具有一个值大于您可以执行这样的查询

产品查找所有税收大于21.12)

可以在Criteria API中引用派生的属性

产品与标准gt'', F
}

生成以支持其中任何一个的SQL看起来像这样

选择这个ID标识此版本版本此价格将此税率定价将此价格征税,此税率产品价格的公式,此价格的税率
因为派生属性的值是在数据库中生成的,并且取决于SQL代码的执行,所以派生属性可能没有应用GORM约束。如果为派生属性指定了约束,则将忽略它们

自定义命名策略

默认情况下,GORM使用Hibernate改进的命名策略通过将驼峰式字符串转换为使用下划线作为单词分隔符的字符串,将域类的类和字段名称转换为SQL表和列名称,您可以在映射闭包,但是如果存在一致的模式,则可以指定其他命名策略使用的课程

配置要用于的类名grails应用配置会议应用程序在里面e节

合并的数据源dbCreate"创建放置"休眠缓存使用二级缓存命名策略com myco myproj CustomNamingStrategy

您还可以指定类的名称,它将为您加载

休眠命名策略'com myco myproj CustomNamingStrategy'
}

第三种选择是提供一个实例,如果除了调用默认构造函数外还需要一些配置的话

休眠防御策略com myco myproj CustomNamingStrategy根据需要配置命名策略

您可以使用现有的类,也可以编写自己的类,例如为表名和列名加上前缀的类

 与myco myproj

 休眠组织cfg改进的命名策略
 休眠组织有用的StringHelper

 自定义命名策略 延伸 改进的命名策略 {

     classToTableName( 班级名称) {
        "" + StringHelper取消资格(班级名称)
    }

     propertyToColumnName( propertyName) {
        "" + StringHelper取消资格(propertyName)
    }
}

默认排序顺序

您可以使用查询参数(例如在清单方法

def机场机场列表排序'')

但是,您也可以在映射中声明集合的默认排序顺序

 飞机场静态映射排序""
    }
}

以上表示所有的收藏飞机场默认情况下,实例将按机场名称排序如果您还想更改排序订购使用此语法

 飞机场静态映射排序名称"描述"
    }
}

最后,您可以在关联级别配置排序

 飞机场静态hasMany航班Flight静态的映射航班排序''订购'描述'
    }
}

在这种情况下航班集合将始终按照航班号的降序排序

这些映射不适用于默认的单向一对多或多对多关系,因为它们涉及联接表。请参见这个问题有关更多详细信息,请考虑使用SortedSet或使用排序参数查询以获取所需的数据

程序化交易

GORM的事务管理基于Spring并使用Spring的Transaction抽象来处理程序化事务

但是,GORM类已得到增强,可以通过with事务关闭方法此方法有一个参数a Closure,有一个参数是Spring交易状态实例

使用withTransaction方法

这是使用的示例withTransaction在控制器中的方法

def transferFunds具有交易状态的资金帐户def源帐户从def dest获取参数dest帐户获取参数至def数量参数的数量为Integer如果源激活如果dest活动源余额金额dest金额金额其他状态setRollbackOnly

在此示例中,如果目标帐户未激活,我们将回滚事务

另外如果例外同时检查或运行时异常错误在处理过程中引发事务将自动回滚

之前的GORM版本未回退已检查的交易例外.

如果您不想回滚整个事务,还可以使用保存点将事务回滚到特定的时间点。这可以通过使用Spring来实现。SavePointManager接口

withTransaction方法在代码块范围内为您处理begin commit commit回滚逻辑

使用TransactionService

由于GORM如果您需要更大的灵活性,那么您可以改而利用交易服务可以通过从中查找来获得HibernateDatastore:

 grails gorm交易TransactionService transactionService数据存储区getService TransactionService

或通过依赖注入

 grails gorm交易

自动接线TransactionService transactionService

一旦有了实例,便可以进行各种操作,包括withTransaction, withRollback, withNewTransaction等有助于程序交易的构建

GORM数据服务

GORM数据服务中引入的功能通过添加使用GORM逻辑自动实现抽象类或接口的功能,使工作脱离了已实现的服务层逻辑

为了说明什么是GORM数据服务,让我们来看一个示例

数据服务基础

编写简单的数据服务

365bet地区在365bet地区应用程序中,您可以在以下任一位置创建数据服务src主界面要么365bet地区应用程序服务要编写数据服务,您应该创建一个接口,尽管以后也可以使用抽象类来更多地使用抽象类,并使用grails gorm services服务服务适用于域类的注释

服务()
接口图书服务getBook可序列化ID

服务注解是一种AST转换,它将自动为您实现服务。然后,您可以通过Spring自动装配获得服务

自动接线BookService bookService

或者,如果您正在独立使用GORM,请从HibernateDatastore实例

BookService bookService hibernateDatastore getService BookService
上面的示例在扩展的Spock单元测试中也适用休眠规范

它是如何工作的

服务转换将查看接口的方法签名,并尽最大努力找到实现每种方法的方法

如果无法实现方法,则会发生编译错误。此时,您可以选择使用抽象类,并自己提供实现

服务转型也会产生INF塞尔维亚语之后服务的文件,因此可以通过标准Java服务加载程序发现它,因此无需其他配置

数据服务的优势

数据服务有许多优点,因此值得考虑抽象化您的持久性逻辑

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

  • 测试中由于数据服务是接口,因此可以轻松地通过进行测试麻雀

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

  • 交易管理数据服务中的每种方法都被包装在适当的事务中,这是只读事务(如果可以轻松覆盖读取操作)

抽象类支持

如果遇到GORM不知道如何实现的方法,则可以使用抽象类来提供实现

例如

接口IBookServicegetBook可序列化ID日期someOther方法服务()
抽象  图书服务 实施IBookService覆写
   日期someOther方法展示
   }
}

在这种情况下,GORM将实现未由抽象类定义的接口方法

另外全部上市域类的方法将自动包装在适当的事务处理中

这意味着您可以定义非事务性的受保护抽象方法以组成逻辑,例如

服务()
抽象  图书服务  {

   受保护的 抽象 getBook可序列化ID(1)

   受保护的 抽象作者getAuthor可序列化ID(1)

   updateBook可序列化ID可序列化authorId(2)
      图书getBook ID如果空值作者作者getAuthor authorId如果作者空值) {
                IllegalArgumentException("作者不存在"书作者作者书保存返回
1 受保护的摘要定义了未包装在事务处理中的方法
2 updateBook方法使用由GORM自动执行的两种方法上市自动进行交易
如果你有上市您不希望进行交易的方法,则可以使用非交易的

数据服务查询

GORM Data Services将使用多种不同的策略和约定为您实施查询

它通过查看方法的返回类型和方法主干并选择最合适的实现来做到这一点

下表总结了约定

表数据服务约定
方法干 描述 可能的退货类型

计数

计算结果数

的子类要么可观察的

计数

动态查找器计算结果数

的子类要么可观察的

删除

删除给定参数的实例

Ť, 虚空的子类要么可观察的

, 得到, 清单要么找回

查询给定参数

Ť, 可迭代的, Ť, 清单, 可观察的

findBy, listBy, findAllBy要么得到

动态查找器查询给定参数

Ť, 可迭代的, Ť, 清单, 可观察的

保存, 商店要么坚持

保存一个新实例

Ť要么可观察的

更新

更新现有实例的第一个参数应为ID

Ť要么可观察的

约定在以后的查询方面有更多可扩展性,有两种不同的类型

简单查询

简单查询是使用方法的参数的查询,例如

服务()
接口图书服务findBook标题

在上面的示例中,数据服务将基于以下事实生成实现:标题参数与标题的属性根据名称和类型进行分类

如果您拼错了标题参数或使用错误的类型,则会发生编译错误

您可以更改返回类型以返回更多结果

服务()
接口图书服务清单<findBooks标题

如果您希望控制分页和查询参数,可以添加一个args应该是一个参数:

服务()
接口图书服务清单<findBooks标题args

在这种情况下,以下查询将控制分页和排序

清单<图书bookService findBooks"展台",
    [抵销:10, 最大值:10, 分类:'标题', 订购:'描述']
)

您可以在查询中包含多个参数

服务()
接口图书服务清单<findBooks标题日期发布日期

在这种情况下,连词查询将被执行,如果您需要做一个析取要么然后是时候了解Dynamic Finder样式查询了

动态查找器查询

动态查找器样式查询使用词干加词通过然后一个动态查找器表达式.

例如

服务()
接口图书服务清单<findByTitleAndPublishDateGreaterThan标题日期发布日期

上面的签名将使用方法签名表达式生成一个动态查找器查询

可能的方法表达式与GORM静态可能的方法表达式相同动态发现者

在这种情况下,要查询的属性的名称是从方法签名推断出来的,而参数名称不是关键的信息如果您拼写错误的方法签名,则会发生编译错误

哪里查询

如果您有更复杂的查询,则可能需要考虑使用哪里注解

    哪里标题模式releaseDate fromDate搜索书籍图案日期从日期

随着哪里注释方法名称可以是您想要的任何名称,并且查询在通过传递的闭包中表示哪里注解

将根据参数对查询进行类型检查,如果您拼写错误的参数或属性名称,则编译将失败

语法与传递给GORM的静态语法相同哪里方法请参见哪里查询了解更多信息

查询联接

您可以使用来指定查询联接加入注解

 静态的 javax持久性标准JoinType

服务()
接口图书服务加入('作者')
    标题(1)

    加入'作者'输入LEFT(2)
    findAnother标题
1 参加作者属性
2 参加作者使用一个属性左外加入

JPA QL查询

如果您需要更大的灵活性,则可以通过以下方式使用HQL查询:询问注解

询问("$作为书名喜欢的书$图案")
searchByTitle图案

请注意,在上面的示例中,如果您拼错了图案传递给查询的参数将发生编译错误

但是,如果您错误地输入了标题属性不会发生错误,因为它是String的一部分,而不是变量

您可以通过声明以下内容的值来解决此问题:在传递的GString中

询问("${}哪里${书名}喜欢$图案")
searchByTitle图案

在上面的示例中,如果您拼错了标题属性,然后会发生编译错误。这非常强大,因为它使您能够键入检查HQL查询,这始终是与标准相比使用它们的缺点之一

对类型检查查询的这种支持扩展到联接。例如,考虑此查询

询问("""${} (1)${作者作者书作者} (2)哪里$标题$标题$作者那么$作者""") (3)
标题作者
1 使用定义根查询
2 使用内部联接和声明来定义要联接的关联
3 应用任何条件哪里条款

查询投影

有几种方法可以实现预测,一种方法是使用约定查找域类属性例如说类有一个发布日期类型的属性日期:

服务()
接口图书服务日期findBookReleaseDate标题

这也适用于多种结果

服务()
接口图书服务清单<日期findBookReleaseDate发布者

您可以使用

服务()
接口图书服务清单<日期findBookReleaseDate发布者args
JP Kal投影

您还可以使用JPA QL查询执行投影

服务()
接口图书服务询问("选择$b发行日期${b}哪里$b发布者$发布者订购$b发布日期")
   清单<日期findBookReleaseDates发布者
接口投影

有时您希望向调用类公开一组更有限的数据。在这种情况下,可以使用接口投影

例如

 作者 {
    日期出生日期(1)
}
接口作者信息getName(2)
}
服务作者接口那么(3)
}
1 域类作者有一个叫做出生日期我们不想向客户提供
2 您可以定义一个仅公开要公开的属性的接口
3 从服务返回接口
如果接口上存在属性,但域类上不存在,则会收到编译错误

数据服务写操作

数据服务中的写操作会自动包装在事务中。您可以通过简单地添加交易性转换为任何方法

以下各节讨论了不同写入操作的详细信息

创建

要创建新实体,该方法应返回新实体,并以用于创建实体的参数或实体本身为特征

例如

服务()
接口图书服务保存簿标题保存簿新书

如果任何参数都不匹配域类的属性,则会发生编译错误

如果发生验证错误,则ValidationException将被从服务中抛出

更新资料

更新操作类似于创建操作,主要区别在于第一个参数应为ID要更新的对象

例如

服务()
接口图书服务updateBook可序列化ID标题

如果任何参数都不匹配域类的属性,则会发生编译错误

如果发生验证错误,则ValidationException将被从服务中抛出

您还可以使用JPA QL实施更新操作

询问("更新${}${书名} = $newTitle书名在哪里$")
updateTitlenewTitle旧标题

删除

删除操作可以返回虚空或返回已删除的实例。在后一种情况下,在发出删除之前需要额外的查询来获取实体

服务()
接口图书服务删除所有标题虚空删除可序列化ID

您还可以使用JPA QL实施删除操作

询问("${}哪里$标题$标题")
虚空删除标题

或通过哪里查询

哪里标题标题发布日期虚空删除标题日期日期

验证数据服务

GORM数据服务已内置支持javax验证方法参数的注释

您将需要一个javax验证在您的类路径上的实现,例如休眠验证器然后简单地使用适当的注释来注释方法参数

 javax验证约束

服务()
接口图书服务不为空 标题

在上面的示例中不为空约束应用于标题财产如果空值传递给方法aConstraintViolationException将会抛出异常

RxJava支持

GORM Data Services还支持返回RxJava x要么rx单类型

计划在将来的版本中提供RxJava x支持

要使用RxJava支持,您需要确保grails资料储存库gorm rx通过将以下内容添加到建立gradle:

建立gradle
编译组织grails grails数据库存储gorm rx RELEASE

例如

 接收

服务()
接口BookService单身找一个标题

当一个rx单用于返回单个结果。要查询多个结果,请使用代替

 接收

服务()
接口图书服务可观察的<findBooks标题

对于常规的GORM实体,GORM默认情况下将使用RxJava执行持久性操作IO排程器.

对于基础数据库支持非阻塞访问的RxGORM实体,数据库驱动程序将相应地安排操作

您可以使用RxSchedule注解

 接收
 grails gorm rx服务RxSchedule
 grails gorm services服务
 rx调度程序调度程序

服务()
接口图书服务RxSchedule调度程序调度程序newThread可观察的<findBooks标题

多个数据源

GORM支持多个数据源的概念,其中多个单独的SQL数据源实例可以配置和切换

配置多个数据源

要配置多个数据源,您需要使用数据源设置例如:

数据源:
    派对: 
    dbCreate: 创建放置
    网址: jdbc h mem书籍
    driverClassName: org h驱动程序
    用户名: 她的
    密码:
数据源:
    更多书籍:
        网址: jdbc h mem more书籍
        :
            只读: 
    更多书籍:
        网址: jdbc h mem甚至更多书籍

您可以为每个数据源配置单独的设置如果默认情况下未指定设置,则该设置是从默认数据源继承的,因此在上面的示例中,无需指定driverClassName对于每个数据源,如果所有驱动程序都使用相同的驱动程序

有关配置的更多信息,请参见配置部分.

将域类映射到数据源

如果域类没有数据源默认配置为标准数据源设置数据源物业映射阻止配置非默认值数据源例如,如果您想使用邮政编码域使用数据源抬头像这样配置

 邮政编码 {

   静态的映射数据源'抬头'
   }
}

域类也可以使用两个或多个配置数据源实例使用数据源具有名称列表的属性,例如,可以配置多个名称

 邮政编码 {

   静态的映射数据源'抬头', '审核'])
   }
}

如果域类使用默认值数据源和一个或多个其他您可以使用ConnectionSource默认常数表示

 

 邮政编码 {

   静态的映射数据源'抬头'ConnectionSource默认

如果域类使用所有已配置的数据源实例使用值所有:

 

 邮政编码 {

   静态的映射数据源ConnectionSource ALL

数据源命名空间

如果一个域类使用多个数据源那么您可以使用每个隐含的名称空间数据源拨打特定电话的名称数据源例如,考虑使用两个数据源实例

 邮政编码 {

   静态的映射数据源'抬头', '审核'])
   }
}

首先数据源当不使用显式命名空间时,指定为默认值,因此在这种情况下,我们默认为抬头但是您可以在审核 数据源数据源例如名称

定义zipCode ZipCode审核获取42邮政编码审核保存

如您所见,您添加了数据源在静态情况和实例情况下都调用方法

ConnectionSources API

在GORM中引入连接源API使您可以内省为应用程序配置的数据源

自动接线HibernateDatastore hibernateDatastore ConnectionSourcesconnectionSources hibernateDatastore getConnectionSources对于连接源connectionSourceconnectionSources println"name$connectionSource那么"SessionFactory sessionFactory connectionSource源

多租户

与软件开发相关的多租户是指一个应用程序的单个实例用于为多个客户端租户提供服务,从而使每个租户数据彼此隔离

这种类型的体系结构在软件即服务SaaS和云体系结构中非常普遍。多租户有多种方法,GORM试图灵活地支持尽可能多的体系。

多租户模式

GORM支持以下不同的多租户模式

  • 数据库具有单独连接池的单独数据库用于存储每个租户数据

  • 施玛使用相同的数据库但使用不同的架构存储每个租户数据

  • 判别器同一数据库与用于区分和隔离数据的鉴别器一起使用

列出了以上模式,从最可能在租户之间泄漏数据到最可能在使用判别器需要采取更多的措施以确保租户不会看到彼此的数据

多租户转换

以下转换可以应用于任何类,以大大简化多租户应用程序的开发,其中包括:

  • 当前租户为类或方法的上下文解决当前租户

  • 承租人将特定租户用于类或方法的上下文

  • 没有租客使用默认连接在没有特定租户的情况下执行逻辑

例如

 365bet地区 Gorm多租户

解决每种方法的当前租户
当前租户
 团队服务 {

    在没有租户ID的情况下执行countPlayers方法
    没有租客
    整型countPlayers玩家数对方法中的所有GORM逻辑使用另一个租户ID
    承租人({"另一个"})
    清单allTwoTeams团队名单清单listTeams团队列表最大值:10)
    }

    交易性
    虚空addTeam那么球队保存名称齐平:)
    }
}

每个租户的数据库

每个租户使用数据库是隔离每个租户数据并建立在GORM现有的支持基础上的最安全方法多个数据源.

组态

为了激活每个租户的多租户数据库,您需要将多租户模式设置为数据库在您的配置中并提供一个租户解析器:

ils:
    蓝色的:
        多租户:
            模式: 数据库
            tenantResolverClass: 组织grails数据存储区映射多租户Web SubDomainTenantResolver
数据源:
    dbCreate: 创建放置
    网址: jdbc h mem书籍
数据源:
    更多书籍:
        网址: jdbc h mem more书籍
    更多书籍:
        网址: jdbc h mem甚至更多书籍

上面的示例使用了内置租户解析器365bet地区与365bet地区或Spring Boot一起使用并评估Web应用程序中DNS子域中当前租户ID的实现但是,您可以实现选择的任何租户解析策略

多租户域类

完成上述配置后,您需要实施多租户您想被视为多租户的域类中的特征

  实施多租户> {
    标题

完成此操作后,无论何时尝试在域类上执行方法,租户ID都将通过租户解析器例如,如果租户解析器退货更多书籍然后更多书籍调用GORM方法(例如,保存, 清单等等

多租户和会话工厂

请注意,如果您引用默认会话工厂要么PlatformTransactionManager在通过Spring注入的类中,这些将不了解租户,并且将直接指向默认数据源

如果您想获得一个特定的会话工厂要么PlatformTransactionManager然后您可以使用getDatastoreForConnection名称的方法HibernateDatastore

自动接线HibernateDatastore hibernateDatastore可序列化的tenantId租户currentId HibernateDatastore SessionFactory sessionFactory hibernateDatastore getDatastoreForConnection tenantId toString getSessionFactory

会话多租户

使用GORM时,通常需要绑定到当前线程的会话才能使GORM和事务一致地工作。如果切换到其他租户,则可能是绑定到当前线程的会话不再与基础线程匹配会话工厂考虑到这一点,您可能想使用房客类,以确保当前租户绑定了正确的会话

 静态的 grails gorm多租户租户

清单<与当前的书清单

您也可以使用指定的租户IDwithId方法

 静态的 grails gorm多租户租户

清单<带id的书"更多书籍") {
    清单

请注意,如果您使用多个GORM实施,则可能需要指定实施类型

 静态的 grails gorm多租户租户
 组织grails orm休眠

清单<具有ID HibernateDatastore的图书"更多书籍") {
    清单

在运行时添加租户

在运行时配置和创建SQL数据库并非易事,并且超出了GORM提供的范围,但是ConnectionSources API确实提供了一种挂接到GORM的方法,以实现这一目标

默认情况下InMemoryConnectionSources使用对象将从应用程序配置中读取连接源并将其存储在内存中

但是可以配置替代实现

ils:
    蓝色的:
        connectionSourcesClass: com示例MyConnectionSources
        多租户:
            模式: 数据库

该实现可以在启动时从另一个数据库表中读取连接源,并通过覆盖addConnectionSource在运行时提供新数据库的方法

如果您对更多示例感兴趣,存在MongoDB的实现从MongoDB集合中读取连接源的文件,但是它没有在运行时实现对预配MongoDB实例的支持

每个租户的架构

每个租户的架构是指使用单个数据库但每个租户使用不同的数据库架构的情况

组态

为了激活每个租户的多租户架构,您需要将多租户模式设置为施玛在您的配置中并提供一个租户解析器:

ils:
    蓝色的:
        多租户:
            模式: 施玛
            tenantResolverClass: foo bar MySchemaResolver
数据源:
    dbCreate: 创建放置
    网址: jdbc h mem书籍

租户解析器可以选择实现AllTenantsResolver界面并返回所有租户的架构名称。这使您可以选择硬编码,以将其放置在配置中或动态地从默认架构中读取它们

如果AllTenantsResolver未实现解析器,则GORM将使用已配置的解析器模式处理程序解析数据库中用于租户的所有架构名称

运行时架构创建

在启动时,如果数据源dbCreate设置为在运行时创建数据库,那么如果缺少模式,GORM将尝试创建模式

为此,它使用一个实例模式处理程序默认情况下使用创建模式名称语法,但可以根据需要为其他数据库方言覆盖和自定义

如果要使用此功能并在运行时根据数据库创建模式,则可能需要配置备用实现

数据源:
    dbCreate: 创建放置
    schemaHandler: foo bar MySchemaHandler

您可以通过删除以下命令来完全禁用运行时模式创建dbCreate选项或将其设置为没有.

如果您希望在应用程序运行时添加架构,则可以使用addTenantForSchemaHibernateDatastore

HibernateDatastore数据存储数据存储addTenantForSchema"myNewSchema")
如果dbCreate已禁用,则您必须在调用此方法之前手动创建模式

每个租户注意事项的架构

为了支持每个租户的架构,就像数据库多租户模式GORM使用独特的会话工厂每个租户因此适用于会话管理的所有相同注意事项

分区多租户

分区多租户是指在每个多租户类别中使用区分符列以对数据进行分区时

使用区分符时,所有租户的所有数据都存储在一个数据库中。这意味着作为开发人员,您必须格外小心,以确保正确隔离每个租户的数据

组态

为了激活基于鉴别器的多租户,您需要将多租户模式设置为判别器在您的配置中并提供一个租户解析器:

ils:
    蓝色的:
        多租户:
            模式: 判别器
            tenantResolverClass: foo bar MyTenantResolver
数据源:
    dbCreate: 创建放置
    网址: jdbc h mem书籍
指定的租户解析器也将需要实施AllTenantsResolver界面,该界面具有解决所有已知租户的其他方法

映射域类

与其他形式的多租户一样,您随后需要实施多租户多租户的域类中的特征另外,您还需要指定一个discriminator列。默认的discriminator列称为tenantId例如

  实施多租户> {
    longtenantId标题

但是,租户标识符可以是任何类型或名称,例如

  实施多租户> {
    发布者标题静态的映射tenantId:'发布者'
    }
}

查询和警告

基于区分符的多租户透明地添加了一个Hibernate过滤器,该过滤器在您查询域类时被激活,您也可以使用房客在租户之间切换的API

 静态的 grails gorm多租户租户

清单<与当前的书清单清单带id的书"更多书籍") {
    清单

请注意,这种使用GORM事件自动激活Hibernate过滤器的方法,因此仅在使用GORM方法时有效,如果您在会话工厂上执行任何原始Hibernate查询,则需要手动激活过滤器

 静态的 grails gorm多租户租户
 组织grails orm休眠

从Spring获取对数据存储和sessionFactory的引用HibernateDatastore数据存储SessionFactory sessionFactory数据存储enableMultiTenancyFilter与会话工厂合作sessionFactory openSession

了解租户解析器

如前所述租户解析器介面就是您定义当前租户.

本节将介绍有关实施的更多详细信息租户解析器实例

指定承租人解析器

如前所述,您可以指定租户解析器通过使用grails gorm tenantResolverClass设置

ils:
    蓝色的:
        多租户:
            模式: 数据库
            tenantResolverClass: 组织grails数据存储区映射多租户Web SubDomainTenantResolver

365bet地区但是,如果您使用的是365bet地区或Spring Boot,则租户解析器也可以指定为Spring bean,它将自动注入,这允许您使用依赖项注入来配置解析器的依赖项

内置租户解析器

下表包含所有租户解析器GORM附带的可直接使用的实现。软件包名称已与组织grails数据存储区映射g为了简洁

描述

多租户解析器固定租户解析器

解决固定的租户ID

o g d m多租户解析器SystemProperty租户解析器

通过名为的系统属性解析租户IDgorm tenantId

多租户Web SubDomain租户解析器

通过DNS解析子域中的租户ID

多租户Web Cookie租户解析器

从名为HTTP的HTTP cookie中解析当前租户gorm tenantId默认情况下

多租户Web会话租户解析器

使用属性从HTTP会话解析当前租户gorm tenantId默认情况下

o g d m多租户Web Http标头租户解析器

使用标头名称从请求HTTP标头解析当前租户gorm tenantId默认情况下

租户解析器组织grails数据存储区映射多租户网站包装要求365bet地区数据存储网站依赖

建立gradle
编译组织grails grails数据存储区Web发布

AllTenantsResolver接口

如果您使用的是基于鉴别器的多租户,则可能需要实施AllTenantsResolver在您的界面租户解析器如果您想随时迭代所有可用的租户,请实施

通常,在基于区分符的多租户的情况下,租户是由其他某些域类属性标识的,因此,例如,一种实现看起来像

可迭代的<可序列化resolveTenantIdsDetachedCriteria公司与众不同''清单

上面的示例使用了每个名称的不同名称公司域类,用于解析所有租户标识符

实施Web租户解析器

365bet地区如果您希望为365bet地区或Spring Boot实现自己的租户解析器,则可以使用RequestContextHolder类,而无需注入任何依赖项,例如SubDomainTenantResolver实现如下

可序列化resolveTenantIdentifier RequestAttributes requestAttributes RequestContextHolder getRequestAttributes如果requestAttributes实例ServletWebRequest子域ServletWebRequest requestAttributes getRequest getRequestURL toString子域子域子字符串子域indexOf"/") + 2);
        如果子域indexOf".") > -1 ) {
            返回子域子字符串0子域indexOf"."))
        }
        其他 {
            返回ConnectionSource默认 TenantNotFoundException"无法在网络请求之外解析租户")
}
如果找不到租户IDTenantNotFoundException应该扔

验证和约束

约束是您在使用GORM实体时定义验证的方式

应用约束

在域类中,约束是通过为代码块分配的constraints属性定义的

 用户 {
    登录密码电子邮件整数静态的约束

 用户静态约束登录大小5..15空白独特密码大小5..15空白电子邮件电子邮件空白年龄最小18
    }
}

在此示例中,我们声明了登录密码, 电子邮件属性

默认情况下,所有域类属性均不可为空,即它们具有隐式可为空的false约束

请注意,约束仅评估一次,这可能与依赖于诸如java有用的日期.

 用户静态约束不评估约束时创建此Date对象
        每次验证User类的实例时出生日期最大值 日期()
    }
}

在约束中引用实例

尝试从静态约束块中引用实例变量非常容易,但这在Groovy或Java中是不合法的。如果这样做,您将获得一个MissingPropertyException给您带来麻烦例如,您可以尝试以下方法

 响应静态的约束调查空白: 空白: , inList调查答案

看看如何inList约束引用实例属性调查那行不通,而是使用自定义验证器约束

 响应静态约束调查空白: 空白: , 验证器val响应对象valobj调查答案

在这个例子中对象自定义验证器的参数是域实例已经过验证,因此我们可以访问其调查属性并返回一个布尔值,以指示是否属性小时已验证

级联约束验证

如果GORM实体引用了其他实体,则在其约束评估验证期间,也可以根据需要评估引用实体的约束。有一个特殊参数级联验证在管理这种方式的实体映射部分中级联验证发生

 作者发行人发行人静态的地图发布者级联验证: "")
    }
}

 发行人 {
    静态的约束名称空白: 
    }
}

下表列出了可以使用的所有选项

选项 描述

没有

完全不会为关联执行任何级联验证

默认

在某些情况下,DEFAULT选项GORM执行级联验证

如果引用的对象通过脏检查特征如果对象没有实现DirtyCheckable,它将退回到默认.

拥有的

仅当实体有级联验证拥有被引用的对象

可以为级联验证:

grails应用配置会议应用程序
grails gorm默认映射'*'(级联验证: '')
}

约束参考

下表总结了可用约束,并提供了一个简短示例

约束 描述

空白

验证字符串值不为空

登录空白否

信用卡

验证字符串值是有效的信用卡号

卡号信用卡真实

电子邮件

验证字符串值是有效的电子邮件地址

homeEmail电子邮件true

inList

验证值是否在约束值的范围或集合内

在清单中列出姓名

火柴

验证String值是否匹配给定的正则表达式

最大值

验证值不超过给定的最大值

价格最高F

maxSize

儿童maxSize

验证值不低于给定的最小值

年龄最小新日期 价格最低F

最小尺寸

验证值的大小不低于给定的最小值

不相等

验证属性不等于指定值

不登录等于鲍勃

可为空

允许将属性设置为空值默认为.

范围

使用Groovy范围来确保属性的值出现在指定范围内

规模

设置为浮点数所需的标度,即小数点右边的位数

薪级表

尺寸

使用Groovy范围限制集合的大小或数字或字符串的长度

儿童大小

独特

登录唯一真实

网址

验证字符串值是有效的URL

主页网址为true

验证器

将自定义验证添加到字段

查看文件

约束和数据库映射

尽管约束主要用于验证,但重要的是要了解约束可以影响数据库模式的生成方式

在可行的情况下,GORM使用领域类的约束来影响为相应领域类属性生成的数据库列

考虑下面的示例假设我们有一个具有以下属性的域模型

描述

在MySQL中,默认情况下,GORM会将这些列定义为

数据类型

varchar

描述

varchar

但是,也许该领域类的业务规则规定,描述的长度最多可以是字符。如果是这种情况,我们可能会按以下方式定义该列如果我们正在使用SQL脚本创建表

数据类型

描述

文本

我们也可能希望进行一些基于应用程序的验证,以确保我们不超过该字符数限制之前我们保留所有记录在GORM中,我们通过约束我们将以下约束声明添加到域类中

静态的约束说明maxSize: 1000
}

此约束将提供我们想要的基于应用程序的验证,还将导致生成架构,如上所示。下面是对影响架构生成的其他约束的说明

影响字符串属性的约束

  • inList

  • maxSize

  • 尺寸

如果有maxSize或者尺寸365bet地区定义了约束365bet地区根据约束值设置最大列长度

通常,不建议在同一个域类属性上同时使用两个约束。maxSize约束与尺寸定义约束,然后GORM将列长度设置为最小maxSize约束和大小约束的上限GORM使用两者中的最小值,因为任何超过该最小值的长度都将导致验证错误

如果inList约束被定义并且maxSize尺寸未定义约束条件,则GORM根据有效值列表中最长字符串的长度设置最大列长度。例如,给定一个包含值的列表,Java Groovy和C GORM会将列长度设置为,即字符串Groovy

影响数值属性的约束

  • 最大值

  • 范围

如果最大值, 要么范围定义了约束GORM尝试根据约束值设置列精度该尝试影响的成功很大程度上取决于Hibernate如何与基础DBMS交互

通常,不建议将这对组合在一起/最大值范围约束在同一域类属性上一起使用但是,如果同时定义了这两个约束,则GORM使用约束中的最小精度值GORM使用两者中的最小精度,因为任何超出该最小精度的长度都将导致验证错误

  • 规模

如果定义了比例约束,则GORM尝试设置列规模基于约束值此规则仅适用于浮点数java long浮动, java郎双, Java语言BigDecimalJava语言BigDecimal这种尝试影响的成功很大程度上取决于Hibernate如何与基础DBMS交互

约束定义了最小的最大数值,GORM得出用于精度的最大位数。请记住,仅指定一个/最大值约束不会影响架构的生成,因为可能会有一个很大的负值属性,例如max,除非指定的约束值需要比当前默认的Hibernate列精度更多的位数。

someFloatValue最大值: 1000000, 规模: 3

会屈服

someFloatValue十进制19, 3) 精度是默认值

someFloatValue最大值: 12345678901234567890, 规模: 5

会屈服

someFloatValue十进制25, 5) 最大刻度的精度数字

someFloatValue最大值: 100, : -100000

会屈服

someFloatValue十进制8, 2) 最小默认刻度中的精度数字

GORM和测试

在早期版本的GORM中,设置单元测试来测试您的GORM逻辑要困难得多。

但是,自从GORM以来,这种情况已经改变,设置GORM进行测试相对来说并不容易。

使用Spock进行单元测试

斯波克是推荐使用GORM编写单元测试的工具,并且设置简单

带有Hibernate和Spock基础知识的GORM

以下是Spock单元测试的示例

 斯波克长
 grails gorm注释实体
 组织grails orm休眠HibernateDatastore

 ExampleSpec 延伸规格(1)

    共享 自动清理HibernateDatastore hibernateDatastore(2)

    虚空setupSpec hibernateDatastoreHibernateDatastore人员(3)
    }

    虚空 "测试一些"() { (4)
       你的逻辑在这里
    }
}

实体 (5)
  {
    ...
}
1 测试应扩展spock lang规格
2 共享注释用于向Spock指示HibernateDatastore在所有测试中共享自动清理注释可确保HibernateDatastore当所有测试完成执行时关闭
3 setupSpec新方法HibernateDatastore用类构造,以用作构造函数的参数
4 然后,您可以在每种方法中编写测试逻辑
5 如果使用注释,可以在单元测试中内联域类实体

Spock和交易

请注意,通常,您必须将测试执行逻辑包装在会话或事务中。最简单的方法是grails gorm Transactions事务性:

进口grails gorm交易 org springframework交易PlatformTransactionManager

 ExampleSpec 延伸规格共享 自动清理HibernateDatastore hibernateDatastore共享PlatformTransactionManager transactionManager(1)

    虚空setupSpec hibernateDatastoreHibernateDatastore人员transactionManager hibernateDatastore getTransactionManager(2)
    }

    交易性 (3)
    定义 设定() {
        名字:"弗雷德"保存回滚 (4)
    虚空 "在单元测试中独立执行GORM测试"() {
        你的逻辑在这里
    }
}
1 PlatformTransactionManager被定义为共享领域
2 您可以获取PlatformTransactionManager来自HibernateDatastore
3 交易性批注用于设置测试数据
4 回滚批注用于回滚每个测试中所做的任何更改

在上面的示例中,每种测试方法都包含在一个事务中,该事务使用grails gorm事务回滚注解

如果您想在setupSpec所有测试之间共享的方法,则可以使用withTransaction:
无效setupSpec hibernateDatastoreHibernateDatastore人员具有事务处理的人员名字:"弗雷德"保存

在Spock中配置GORM

如果您需要在Spock单元测试中配置GORM,则可以将映射传递给HibernateDatastore例如设置多租户

无效的setupSpec组态'grails gorm多租户模型':'判别器',
        'grails gorm multiTenancy tenantResolverClass'SystemPropertyTenantResolver hibernateDatastoreHibernateDatastore配置人员

使用JUnit进行单元测试

进行单元测试JUnit的它与Spock大致相似,只是遵循不同的习惯用法

所以代替setupSpec采用课前:

 组织机构
 grails gorm交易
 组织grails orm休眠HibernateDatastore
 org springframework交易PlatformTransactionManager

 示例测试  {

    静态的HibernateDatastore hibernateDatastore PlatformTransactionManager transactionManager课前
    虚空setupGorm hibernateDatastoreHibernateDatastore人员下课以后
    虚空shutdownGorm hibernateDatastore关闭之前
    虚空设置transactionManager hibernateDatastore getTransactionManager回滚
    测试
    虚空testSomething你的逻辑在这里
    }
}
JUnit没有像Spock这样的东西自动清理所以你必须打电话HibernateDatastore手动地