使用Grails / Hibernate

时间:2015-11-03 21:58:00

标签: hibernate postgresql grails join hql

我正在使用创建的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返回的数组?

非常感谢,非常感谢任何帮助。

亲切的问候

1 个答案:

答案 0 :(得分:0)

我将专注于主要问题,即如何加入不相关的实体/域类。

问题

不幸的是,正如您所发现的,GORM / Hibernate连接需要关联属性,这会导致将列添加到域类表中。没有关联,就无法建立联接。这适用于HQL,条件查询以及查询的位置。我对此主题有一个article,您可能会觉得有帮助。

我从未尝试过使用子类来解决这样的问题,但是我认为它不会起作用,因为GORM / Hibernate不能代表你在checkout_journal.checkout_date上使用的复杂左外连接

可能的解决方案

您可以使用数据库视图来模拟创建GORM关联所需的映射。另一位开发人员有some success。通过适当的关联,您将能够使用HQL或条件查询。

注意:查询仅支持内部联接。

RentalCar to 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

有了这些观点,您可以在RentalCarCheckoutJournal域类中使用它们。

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
        ]
    ]
]