我正在使用 Grails 访问一个共享数据库,该数据库由另一个应用程序的 ORM 创建并填充,并且希望在不修改底层表结构的情况下对其执行 LEFT JOIN 和聚合函数。理想情况下,这应该发生在高级别的基础上,例如使用 HQL、Criteria 或类似的方式。
这些表是不相关的,它们没有由外键定义的关系。这些表使用可用于执行连接的字符串标识符系统。
出于这个原因,到目前为止,我能找到在 Grails 中加入这些不相关表的唯一方法是使用原生 SQL 查询。 HQL 中的左连接需要表中定义的关系(例如 left join checkoutJournal.checkoutDate
需要 checkoutJournal
中的 RentalCar
字段,据我了解,这会在数据库表中添加额外的列)
我的域类看起来像这样——他们目前唯一做的就是表示数据库的表结构。
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
...
}]
我对所需结果的 native 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
方案?
我正在考虑使用与 CheckoutJournal 和 RepairJournal 的附加关系将 RentalCar 子类化(以将组合实体与基本实体分开)添加类似于
static mapping = {
checkoutJournal indexColumn: [name: "identifier", type: String],
joinTable: [column: "identifier"],
repairJournal indexColumn: [name: "identifier", type: String],
joinTable: [column: "identifier"]
加上一个标准
...
projections {
max "checkoutDate"
}
...
lastCheckoutDate
的最大聚合以及我想要为 repairHistory
返回的数组? 非常感谢,非常感谢任何帮助。
亲切的问候
最佳答案
我将专注于主要问题,即如何加入不相关的实体/域类。
问题
不幸的是,正如您所发现的,GORM/Hibernate 连接需要关联属性,这会导致将列添加到域类表中。如果没有关联,则无法进行连接。这适用于 HQL、条件查询和 where 查询。我有一个 article关于这个你可能会觉得有帮助的话题。
我从未尝试过使用子类化来解决这样的问题,但我认为它不会起作用的一个原因是 GORM/Hibernate 不能表示您在 checkout_journal.checkout_date
上使用的复杂左外连接。 .
可能的解决方案
您可以使用数据库 View 来模拟创建 GORM 关联所需的映射表。另一个开发者有 some success有了这个。通过适当的关联,您将能够使用 HQL 或条件查询。
注意:where 查询仅支持内部连接。
RentalCar 到 CheckoutJournal 的映射
从以下 View 开始(将它们视为伪 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
有了 View ,您可以在
RentalCar
中使用它们。和 CheckoutJournal
域类。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 的域类(顺便说一句,现在是只读的),您可以使用 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
]
]
]
关于hibernate - 使用 Grails/Hibernate 加入不相关的实体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33509859/