使用 Grails / Hibernate 加入不相关的实体
Left joining unrelated entities with Grails / Hibernate
我正在使用 Grails 访问共享数据库,该数据库是由另一个应用程序的 ORM 创建并填充的,我想在不修改基础 table 结构的情况下对其执行 LEFT JOIN 和聚合函数。理想情况下,这应该发生在高层基础上,例如使用 HQL、Criteria 或类似方式。
这些table无关,它们没有外键定义的关系。 table 使用可用于执行连接的字符串标识符系统。
出于这个原因,到目前为止,我能找到的在 Grails 中加入这些不相关的 table 的唯一方法是使用本机 SQL 查询。 HQL 中的左连接需要 table 中定义的关系(例如 left join checkoutJournal.checkoutDate
需要 RentalCar
中的 checkoutJournal
字段,据我所知,这会向数据库添加额外的列table)
我的域 类 看起来像这样 - 他们目前唯一做的就是表示数据库的 table 结构。
class RentalCar {
String identifier
static mapping = {
table 'rental_car'
}
...
}
class CheckoutJournal {
String identifier
String renterName
Date checkoutDate
static mapping = {
table 'checkout_journal'
}
...
}
class RepairJournal {
String identifier
String repairDescription
Date repairDate
static mapping = {
table 'repair_journal'
}
...
}
我要构建的结果数据集应具有以下结构(在 JSON 表示法中):
[
{
identifier: "xyz",
// = max checkout date from CheckoutJournal, identifier String NOT unique
lastCheckoutDate: "2015-10-31 20:06:17.738831",
// all repairDate entries from RepairJournal that match identifier,
// realized with aggregate function in SQL
repairHistory: [
"2015-10-31 20:06:17.738831",
"2015-10-30 20:06:17.738831",
"2015-10-29 20:06:17.738831"
]
}, {
// Another rental car with the same structure
...
}]
我对所需结果的本机 SQL 查询如下所示:
SELECT
rc.identifier,
coj.renterName,
array_agg(rej.repairDate)
FROM rental_car rc
LEFT JOIN checkout_journal AS coj
ON coj.identifier = rc.identifier
AND coj.checkout_date =
( SELECT max(checkout_date)
FROM checkout_journal c
WHERE rc.identifier = c.identifier )
LEFT JOIN repair_journal AS rej
ON rc.identifier = rej.identifier
GROUP BY rc.identifier, coj.renter_name
- 在不更改数据库的情况下实现查询的最佳方法是什么
计划?
我正在考虑将 RentalCar 子类化(将组合实体与基本实体分开)与 CheckoutJournal 和 RepairJournal 的附加关系添加类似于
static mapping = {
checkoutJournal indexColumn: [name: "identifier", type: String],
joinTable: [column: "identifier"],
repairJournal indexColumn: [name: "identifier", type: String],
joinTable: [column: "identifier"]
加上条件
...
projections {
max "checkoutDate"
}
...
- 这是正确的方法并且不会改变 table 结构吗?
- 我如何使用条件查询我的数据,表达
lastCheckoutDate
的最大聚合和我想要为 repairHistory
返回的数组?
非常感谢,非常感谢任何帮助。
亲切的问候
我将专注于主要问题,即如何加入不相关的 entities/domain classes.
问题
不幸的是,正如您所发现的,GORM/Hibernate 联接需要关联属性,这会导致将列添加到域 class 表中。没有关联就无法进行连接。这适用于 HQL、条件查询和 where 查询。我有一个关于这个主题的 article,你可能会觉得有用。
我从来没有尝试过使用 subclassing 来解决这样的问题,但我认为它不会起作用,原因之一是 GORM/Hibernate 不能表示复杂的左外连接在 checkout_journal.checkout_date
.
上使用
可能的解决方案
您可以使用数据库视图来模拟创建 GORM 关联所必需的映射 表。另一位开发人员对此有 some success。通过适当的关联,您将能够使用 HQL 或条件查询。
注意:查询仅支持内连接。
RentalCar 到 CheckoutJournal 的映射
从以下视图开始(将它们视为伪SQL):
-- This view transforms the checkout_journal table into something GORM compliant by creating a unique key for each journal entry
CREATE VIEW checkout_journal_gorm AS
SELECT CONCAT(identifier, to_char(checkout_date, '-YYYY-MM-DD-HH-MI-SS-US')) as checkout_identifier, renter_name, checkout_date
FROM checkout_journal
-- This view makes it possible to create a one-to-many association from RentalCar to CheckoutJournal.
CREATE VIEW rental_car_checkous AS
SELECT rc.identifier AS rental_car_identifier, CONCAT(coj.identifier, to_char(coj.checkout_date, '-YYYY-MM-DD-HH-MI-SS-US')) as checkout_identifier
FROM rental_car AS rc INNER JOIN checkout_journal as coj ON rc.indentifier = coj.identifier
有了这些视图,您可以在 RentalCar
和 CheckoutJournal
域 classes.
中使用它们
class RentalCar {
String identifier
static hasMany = [
checkouts: CheckoutJournal,
repairs: RepairJournal
]
static mapping = {
table 'rental_car'
checkouts joinTable: [
name: 'rental_car_checkous',
key: 'rental_car_identifier',
column: 'checkout_identifier'
]
}
...
}
class CheckoutJournal {
String identifier
String renterName
Date checkoutDate
static mapping = {
table 'checkout_journal_gorm'
id column: 'checkout_identifier', name: 'identifier'
}
...
}
然后您可以为 RepairJournal
.
执行等效操作
GORM 查询
设置了符合 GORM 的域 classes(顺便说一句,它现在是只读的),您可以使用 HQL 或条件查询而不是 SQL。这是一个例子:
RentalCar.withCriteria {
resultTransformer = new org.hibernate.transform.AliasToEntityMapResultTransformer()
createAlias 'checkouts', 'c', org.hibernate.sql.JoinType.LEFT_OUTER_JOIN
createAlias 'repairs', 'r', org.hibernate.sql.JoinType.LEFT_OUTER_JOIN
projections {
groupProperty 'identifier', 'identifier'
max 'c.checkoutDate', 'lastCheckoutDate'
groupProperty 'r.repairDate', 'repairDate'
}
}
输出将如下所示:
[
[identifier: 'xyz', lastCheckoutDate: '2015-10-31 20:06:17.738831', repairDate: '2015-10-31 20:06:17.738831'],
[identifier: 'xyz', lastCheckoutDate: '2015-10-31 20:06:17.738831', repairDate: '2015-10-30 20:06:17.738831'],
[identifier: 'xyz', lastCheckoutDate: '2015-10-31 20:06:17.738831', repairDate: '2015-10-29 20:06:17.738831']
]
这样的输出可以这样转换:
rows.groupBy { it.identifier }.collect { identifier, maps ->
[
identifier: identifier,
lastCheckoutDate: maps[0].lastCheckoutDate,
repairHistory: maps*.repairDate
]
}
生成您正在寻找的结构:
[
[
identifier:xyz,
lastCheckoutDate:2015-10-31 20:06:17.738831,
repairHistory:[
2015-10-31 20:06:17.738831,
2015-10-30 20:06:17.738831,
2015-10-29 20:06:17.738831
]
]
]
我正在使用 Grails 访问共享数据库,该数据库是由另一个应用程序的 ORM 创建并填充的,我想在不修改基础 table 结构的情况下对其执行 LEFT JOIN 和聚合函数。理想情况下,这应该发生在高层基础上,例如使用 HQL、Criteria 或类似方式。
这些table无关,它们没有外键定义的关系。 table 使用可用于执行连接的字符串标识符系统。
出于这个原因,到目前为止,我能找到的在 Grails 中加入这些不相关的 table 的唯一方法是使用本机 SQL 查询。 HQL 中的左连接需要 table 中定义的关系(例如 left join checkoutJournal.checkoutDate
需要 RentalCar
中的 checkoutJournal
字段,据我所知,这会向数据库添加额外的列table)
我的域 类 看起来像这样 - 他们目前唯一做的就是表示数据库的 table 结构。
class RentalCar {
String identifier
static mapping = {
table 'rental_car'
}
...
}
class CheckoutJournal {
String identifier
String renterName
Date checkoutDate
static mapping = {
table 'checkout_journal'
}
...
}
class RepairJournal {
String identifier
String repairDescription
Date repairDate
static mapping = {
table 'repair_journal'
}
...
}
我要构建的结果数据集应具有以下结构(在 JSON 表示法中):
[
{
identifier: "xyz",
// = max checkout date from CheckoutJournal, identifier String NOT unique
lastCheckoutDate: "2015-10-31 20:06:17.738831",
// all repairDate entries from RepairJournal that match identifier,
// realized with aggregate function in SQL
repairHistory: [
"2015-10-31 20:06:17.738831",
"2015-10-30 20:06:17.738831",
"2015-10-29 20:06:17.738831"
]
}, {
// Another rental car with the same structure
...
}]
我对所需结果的本机 SQL 查询如下所示:
SELECT
rc.identifier,
coj.renterName,
array_agg(rej.repairDate)
FROM rental_car rc
LEFT JOIN checkout_journal AS coj
ON coj.identifier = rc.identifier
AND coj.checkout_date =
( SELECT max(checkout_date)
FROM checkout_journal c
WHERE rc.identifier = c.identifier )
LEFT JOIN repair_journal AS rej
ON rc.identifier = rej.identifier
GROUP BY rc.identifier, coj.renter_name
- 在不更改数据库的情况下实现查询的最佳方法是什么 计划?
我正在考虑将 RentalCar 子类化(将组合实体与基本实体分开)与 CheckoutJournal 和 RepairJournal 的附加关系添加类似于
static mapping = {
checkoutJournal indexColumn: [name: "identifier", type: String],
joinTable: [column: "identifier"],
repairJournal indexColumn: [name: "identifier", type: String],
joinTable: [column: "identifier"]
加上条件
...
projections {
max "checkoutDate"
}
...
- 这是正确的方法并且不会改变 table 结构吗?
- 我如何使用条件查询我的数据,表达
lastCheckoutDate
的最大聚合和我想要为repairHistory
返回的数组?
非常感谢,非常感谢任何帮助。
亲切的问候
我将专注于主要问题,即如何加入不相关的 entities/domain classes.
问题
不幸的是,正如您所发现的,GORM/Hibernate 联接需要关联属性,这会导致将列添加到域 class 表中。没有关联就无法进行连接。这适用于 HQL、条件查询和 where 查询。我有一个关于这个主题的 article,你可能会觉得有用。
我从来没有尝试过使用 subclassing 来解决这样的问题,但我认为它不会起作用,原因之一是 GORM/Hibernate 不能表示复杂的左外连接在 checkout_journal.checkout_date
.
可能的解决方案
您可以使用数据库视图来模拟创建 GORM 关联所必需的映射 表。另一位开发人员对此有 some success。通过适当的关联,您将能够使用 HQL 或条件查询。
注意:查询仅支持内连接。
RentalCar 到 CheckoutJournal 的映射
从以下视图开始(将它们视为伪SQL):
-- This view transforms the checkout_journal table into something GORM compliant by creating a unique key for each journal entry
CREATE VIEW checkout_journal_gorm AS
SELECT CONCAT(identifier, to_char(checkout_date, '-YYYY-MM-DD-HH-MI-SS-US')) as checkout_identifier, renter_name, checkout_date
FROM checkout_journal
-- This view makes it possible to create a one-to-many association from RentalCar to CheckoutJournal.
CREATE VIEW rental_car_checkous AS
SELECT rc.identifier AS rental_car_identifier, CONCAT(coj.identifier, to_char(coj.checkout_date, '-YYYY-MM-DD-HH-MI-SS-US')) as checkout_identifier
FROM rental_car AS rc INNER JOIN checkout_journal as coj ON rc.indentifier = coj.identifier
有了这些视图,您可以在 RentalCar
和 CheckoutJournal
域 classes.
class RentalCar {
String identifier
static hasMany = [
checkouts: CheckoutJournal,
repairs: RepairJournal
]
static mapping = {
table 'rental_car'
checkouts joinTable: [
name: 'rental_car_checkous',
key: 'rental_car_identifier',
column: 'checkout_identifier'
]
}
...
}
class CheckoutJournal {
String identifier
String renterName
Date checkoutDate
static mapping = {
table 'checkout_journal_gorm'
id column: 'checkout_identifier', name: 'identifier'
}
...
}
然后您可以为 RepairJournal
.
GORM 查询
设置了符合 GORM 的域 classes(顺便说一句,它现在是只读的),您可以使用 HQL 或条件查询而不是 SQL。这是一个例子:
RentalCar.withCriteria {
resultTransformer = new org.hibernate.transform.AliasToEntityMapResultTransformer()
createAlias 'checkouts', 'c', org.hibernate.sql.JoinType.LEFT_OUTER_JOIN
createAlias 'repairs', 'r', org.hibernate.sql.JoinType.LEFT_OUTER_JOIN
projections {
groupProperty 'identifier', 'identifier'
max 'c.checkoutDate', 'lastCheckoutDate'
groupProperty 'r.repairDate', 'repairDate'
}
}
输出将如下所示:
[
[identifier: 'xyz', lastCheckoutDate: '2015-10-31 20:06:17.738831', repairDate: '2015-10-31 20:06:17.738831'],
[identifier: 'xyz', lastCheckoutDate: '2015-10-31 20:06:17.738831', repairDate: '2015-10-30 20:06:17.738831'],
[identifier: 'xyz', lastCheckoutDate: '2015-10-31 20:06:17.738831', repairDate: '2015-10-29 20:06:17.738831']
]
这样的输出可以这样转换:
rows.groupBy { it.identifier }.collect { identifier, maps ->
[
identifier: identifier,
lastCheckoutDate: maps[0].lastCheckoutDate,
repairHistory: maps*.repairDate
]
}
生成您正在寻找的结构:
[
[
identifier:xyz,
lastCheckoutDate:2015-10-31 20:06:17.738831,
repairHistory:[
2015-10-31 20:06:17.738831,
2015-10-30 20:06:17.738831,
2015-10-29 20:06:17.738831
]
]
]