hibernate - 使用 Grails/Hibernate 加入不相关的实体

标签 hibernate postgresql grails join hql

我正在使用 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/

    相关文章:

    java - com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException : Unknown column 'email' in 'where clause'

    postgresql - 从查询更新 Redshift 表

    hibernate - Grails org.h2.jdbc.JdbcSQLException 列的值太长

    validation - Groovy/Grails : How are constraints implemented?

    mysql - 创建 Hibernate JPA View

    hibernate - 我在 OneToMany 关系中遇到错误?

    hibernate - 尝试保存数据时无法找到实体..

    ruby-on-rails - Rails 唯一索引和 validates_uniqueness_of 有什么区别

    postgresql - 设置postgres环境变量运行镜像

    grails - 在Grails中编写集成测试以测试数据库持久性