convert from SQL to HQL gives error Grails

2019-06-14 17:34发布

问题:

I am trying to convert this sql to hql to fetch the data from artifact table. I have this sql query which works fine on the database

select a.*
from classification c 
     join (select id from taxonomy_node start with id = 5067 connect by nocycle prior id = parent_id) h
     on (c.node_id = h.id)
     join artifact a on (a.id = c.artifact_id)
     Where a.DOCUMENT_ID = 10462 AND c.active=1

I am trying to convert it to HQL

artifacts = Artifact.executeQuery("FROM classification c "+
                                        "JOIN (SELECT id FROM taxonomy_node START WITH id = :nodeId "+
                                        "CONNECT BY NOCYCLE PRIOR id = parent_id) h "+
                                        "ON (c.node_id = h.id) "+
                                        "JOIN artifact a ON (a.id = c.artifactId) "+
                                        "WHERE a.DOCUMENT_ID = :docid AND c.active=1",
                                        [nodeId: NodeId ,docid: document.id],
                                        [max:limit, offset:startIndex])

The domain classes look like Classification

class Classification {

    public static final String USER_DEFAULT = "USER"
    public static final String USER_SYSTEM = "SYSTEM"

    TaxonomyNode node
    String artifactId //TODO can we drive this with an annotated object or something?
    Boolean active
    String createdBy
    String updatedBy
    Date dateCreated
    Date lastUpdated
    // or should we create an Artifact Toolkit plugin?

    static constraints = {
        node nullable:false, blank:false
        artifactId nullable:false, blank:false, unique: ['node']
        active nullable: false, blank: false
        createdBy nullable:false, blank:false
        updatedBy nullable:false, blank:false
    }

    static mapping = {
        id generator:'sequence', params:[sequence:'classification_seq']
        artifactId index: 'classify_by_artifact_node'
        node index: 'classify_by_artifact_node'
        active defaultValue: "1"
    }
}

Artifact looks like

class Artifact {

    public enum ArtifactType {
        CLAUSE("Clause"),
        TITLE("Title"),
        DEFINED_TERMS("Defined Terms")

        private final String value

        ArtifactType(String value) { this.value = value }
        String toString() { value }
        String getKey() { name() }
    }

    public enum ArtifactStatus {
        ALL("All"),
        REVIEWED("Reviewed"),
        FOR_REVIEW("For Review"),
        UNCLASSIFIED("Unclassified")

        private final String value

        ArtifactStatus(String value) { this.value = value }
        String toString() { value }
        String getKey() { name() }
    }

    Document document
    String artifactType
    String text
    String status
    String createdBy
    String updatedBy
    Date dateCreated
    Date lastUpdated

    static belongsTo = Document
    static hasMany = [classification: Classification]

    static constraints = {
        artifactType maxSize: 50, nullable: false, blank: false
        text nullable: false, blank: false, maxSize: 4000
        status nullable:true, blank:true
        createdBy nullable:false, blank:false
        updatedBy nullable:false, blank:false
    }

    static mapping = {
        id generator:'sequence', params:[sequence:'artifact_seq']
        document index: 'artifact_by_doc'
        artifactType index: 'artifact_by_doc,artifact_by_type'
        text index: 'artifact_by_doc,artifact_by_text'
    }
}

the taxonomy Node looks like

class TaxonomyNode {

    public static final String USER_DEFAULT = "USER"
    public static final String USER_SYSTEM = "SYSTEM"

    String hierarchyId
    String nodeId
    String label
    String inputFormat
    Boolean active
    String createdBy
    String updatedBy
    Date dateCreated
    Date lastUpdated

    static hasMany = [metadata: TaxonomyNodeMetadata]

    TaxonomyNode parent

    static constraints = {
        inputFormat nullable:true, blank:true //TODO for now we want to allow null/blank for user-entered nodes
        parent nullable:true //TODO we should enforce that only one node per hierarchyId can have a null parent
        hierarchyId nullable:false, blank:false //TODO we should enforce the parent's hierarchyId is the same
        active nullable: false, blank: false
        nodeId nullable:false, blank:false, unique: ['hierarchyId']
        label nullable:true, blank:true
        createdBy nullable:false, blank:false
        updatedBy nullable:false, blank:false
    }

    static mapping = {
        id generator:'sequence', params:[sequence:'taxonomy_node_seq']
        hierarchyId index: 'taxonomy_by_hier'
        label index: 'taxonomy_by_hier,taxonomy_by_label'
        parent index: 'taxonomy_by_hier,taxonomy_by_parent'
        active defaultValue: "1"
    }
}

I am getting this error

Error |
2014-10-22 09:22:38,633 [http-bio-8080-exec-4] ERROR ast.ErrorCounter  - line 1:28: unexpected token: (
Error |
2014-10-22 09:22:38,634 [http-bio-8080-exec-4] ERROR ast.ErrorCounter  - line 1:28: unexpected token: (
Message: unexpected token: (
    Line | Method
->>  281 | $tt__index in com.ald.aeandsdx.ArtifactController
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
|    198 | doFilter   in grails.plugin.cache.web.filter.PageFragmentCachingFilter
|     63 | doFilter . in grails.plugin.cache.web.filter.AbstractFilter
|   1145 | runWorker  in java.util.concurrent.ThreadPoolExecutor
|    615 | run . . .  in java.util.concurrent.ThreadPoolExecutor$Worker
^    745 | run        in java.lang.Thread
Error |
org.springframework.orm.hibernate4.HibernateQueryException: unexpected token: ( near line 1, column 28 [FROM classification c JOIN (SELECT id FROM taxonomy_node START WITH id = :nodeId CONNECT BY NOCYCLE PRIOR id = parent_id) hON (c.node_id = h.id) JOIN artifact a ON (a.id = c.artifactId) WHERE a.DOCUMENT_ID = :docid AND c.active=1]; nested exception is org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: ( near line 1, column 28 [FROM classification c JOIN (SELECT id FROM taxonomy_node START WITH id = :nodeId CONNECT BY NOCYCLE PRIOR id = parent_id) hON (c.node_id = h.id) JOIN artifact a ON (a.id = c.artifactId) WHERE a.DOCUMENT_ID = :docid AND c.active=1]

回答1:

HQL supports subqueries only in SELECT and WHERE clauses, see: http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/queryhql.html#queryhql-subqueries