我正在使用Grails访问共享数据库,该共享数据库是由另一个应用程序的ORM创建并填充的,并且希望在不修改基础表结构的情况下执行LEFT JOIN及其上的聚合函数。理想情况下,这应该在较高级别的基础上进行,例如使用HQL,条件或类似方法。
这些表是不相关的,它们没有由外键定义的关系。这些表使用字符串标识符系统,该系统可用于执行联接。
因此,到目前为止,我只能在本机SQL查询中找到在Grails中联接这些不相关表的唯一方法。 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
...
}]
我对所需结果的本机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"
}
...
lastCheckoutDate
的最大合计以及要为repairHistory
返回的数组? 非常感谢,非常感谢您的帮助。
亲切的问候
最佳答案
我将专注于主要问题,即如何加入不相关的实体/域类。
问题
不幸的是,正如您所发现的,GORM / Hibernate联接需要关联属性,这会导致将列添加到域类表中。没有关联,就无法建立连接。这适用于HQL,条件查询和where查询。我对此主题有一个article,您可能会觉得有帮助。
我从未尝试使用子类来解决此类问题,但我认为这行不通的原因之一是GORM / Hibernate无法表示您在checkout_journal.checkout_date
上使用的复杂的左外部联接。
可能的解决方案
您也许可以使用数据库 View 来模拟创建GORM关联所必需的映射表。另一个开发人员对此具有some success。有了适当的关联,您就可以使用HQL或条件查询。
注意:查询仅支持内部联接。
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
]
]
]