我正在使用创建的Grails访问共享数据库,并由另一个应用程序的ORM填充,并希望在不修改基础表结构的情况下执行LEFT JOIN以及聚合函数。理想情况下,这应该在高级基础上进行,例如使用HQL,Criteria或类似方法。
这些表无关,它们没有外键定义的关系。这些表使用可用于执行连接的字符串标识符系统。
由于这个原因,我在Grails中找到加入这些不相关的表的唯一方法是使用本机SQL查询。 HQL中的左连接需要在表中定义的关系(例如left join checkoutJournal.checkoutDate
,这需要checkoutJournal
中的RentalCar
字段,据我所知,这将为DB表添加额外的列)
我的域类看起来像这样 - 他们目前唯一做的就是表示数据库的表结构。
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
返回的数组?非常感谢,非常感谢任何帮助。
亲切的问候
答案 0 :(得分:0)
我将专注于主要问题,即如何加入不相关的实体/域类。
不幸的是,正如您所发现的,GORM / Hibernate连接需要关联属性,这会导致将列添加到域类表中。没有关联,就无法建立联接。这适用于HQL,条件查询以及查询的位置。我对此主题有一个article,您可能会觉得有帮助。
我从未尝试过使用子类来解决这样的问题,但是我认为它不会起作用,因为GORM / Hibernate不能代表你在checkout_journal.checkout_date
上使用的复杂左外连接
您可以使用数据库视图来模拟创建GORM关联所需的映射表。另一位开发人员有some success。通过适当的关联,您将能够使用HQL或条件查询。
注意:查询仅支持内部联接。
从以下视图开始(将它们视为伪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
域类中使用它们。
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的域类(现在只能是只读的),您可以使用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
]
]
]