grails:将 SQL 转换为域类

Posted

技术标签:

【中文标题】grails:将 SQL 转换为域类【英文标题】:grails: converting SQL into domain classes 【发布时间】:2012-09-20 17:57:38 【问题描述】:

我正在开发一个 GRAILS 应用程序(我是 GRAILS 的新手,并从以前的开发人员那里继承了该项目)。我正在慢慢了解 GRAILS 的运行方式以及 DOMAIN 类、休眠等的使用。mysql db 托管在 Amazon 上,我们正在使用 ElasticCache。

有没有知识渊博的人知道我可以如何将以下 SQL 语句转换为域类和查询条件。

if(params?.searchterm) 

    def searchTerms = params.searchterm.trim().split( ',' )
    def resultLimit = params.resultlimit?: 1000
    def addDomain = ''

    if (params?.domainname)
        addDomain = " and url like '%$params.domainname%' "
    

    def theSearchTermsSQL = ""

    /*
     *  create c.name rlike condition for each search term
     *
     */
    searchTerms.each
        aSearchTerm -> 
            if( theSearchTermsSQL != '' )
                theSearchTermsSQL += ' or '
            
            theSearchTermsSQL += "cname rlike '[[:<:]]" + aSearchTerm.trim() +  "[[:>:]]'"
    

    /*
     *  build query
     *
     */
    def getUrlsQuery = "select
        u.url as url,
        c.name as cname,
        t.weight as tweight
    from
       (category c, target t, url_meta_data u )
    where
       (" + theSearchTermsSQL + ")
    and 
        t.category_id = c.id
        and t.url_meta_data_id = u.id
        and u.ugc_flag != 1 " + addDomain + "
    order by tweight desc
    limit " + resultLimit.toLong()


    /* 
     *  run query
     *
     */
    Sql sqlInstance = new Sql( dataSource )

    def resultsList = sqlInstance.rows( getUrlsQuery )


表格如下(虚拟数据):

[Category]
id | name
-----------
 1 | small car
 2 | bike
 3 | truck
 4 | train
 5 | plane
 6 | large car
 7 | caravan

[Target]
id | cid | weight | url_meta_data_id
----------------------------------------
 1 |  1  |  56    |        1
 2 |  1  |  76    |        2
 3 |  3  |  34    |        3
 4 |  2  |  98    |        4
 5 |  1  |  11    |        5
 6 |  3  |  31    |        7
 7 |  5  |  12    |        8
 8 |  4  |  82    |        6

[url_meta_data]
id | url                          | ugc_flag
---------------------------------------------
 1 | http://www.example.com/foo/1 |    0
 2 | http://www.example.com/foo/2 |    0
 3 | http://www.example.com/foo/3 |    1
 4 | http://www.example.com/foo/4 |    0
 5 | http://www.example.com/foo/5 |    1
 6 | http://www.example.com/foo/6 |    1
 7 | http://www.example.com/foo/7 |    1
 8 | http://www.example.com/foo/8 |    0

域类

class Category 

    static hasMany = [targets: Target]

    static mapping = 
        cache  true
        cache usage: 'read-only'
        targetConditions cache : true
    

    String name

    String source


class Target 

    static belongsTo = [urlMetaData: UrlMetaData, category: Category]
    static mapping = 
        cache true
        cache usage: 'read-only'
    

    int weight



class UrlMetaData 

    String url

    String ugcFlag

    static hasMany = [targets: Target ]

    static mapping =  
        cache true
        cache usage: 'read-only'
    

    static transients = ['domainName']

    String getDomainName() 

        return HostnameHelper.getBaseDomain(url)
    

基本上,来自 url_meta_data 的 url 可以关联到许多类别。所以本质上我想要实现的应该是一个相对基本的操作......返回搜索词“汽车”的所有网址、它们的权重(即重要性)以及 ugc_flag 的位置不是 1(即 url 不是用户生成的内容)。数据库中有 100K + 记录,这些记录是从第三方提供商导入的。请注意,所有 URL 都属于我的客户 - 这里没有做任何狡猾的事情。

请注意我在查询中使用的 rlike - 我最初使用的是 ilike %searchterm% 但它会找到 searchterm 是较大单词一部分的类别,例如“caravan”) - 不幸的是,尽管 rlike 不会如果用户请求“汽车”,则返回任何内容。

我编辑了代码 - 正如 Igor 指出最初包含“域名”的奇怪内容。这是传递的可选参数,允许用户仅过滤特定域的 url(例如 'example.com')

【问题讨论】:

【参考方案1】:

我会创建一个给定域对象的空列表, 遍历 resultsList,从每一行构造一个域对象并将其添加到这些对象的列表中。然后将该列表从控制器返回以查看。这就是你要找的吗?

【讨论】:

感谢矢量。正如您所提到的,我很高兴对 resultsList 进行循环,现在我将其发送到 gsp 视图。实际上,我从问题中的代码中删除了它,只是为了将我的问题集中在如何将 sql 转换为休眠状态。【参考方案2】:

1) 如果它是从头开始开发的 Grails 应用程序(而不是基于遗留数据库结构),那么您可能应该已经拥有域类 CategoryTargetUrlMetaData(否则您将不得不手动或使用 db-reverse-engineer 插件创建它们)

2) 我假设Target 有一个字段Category categoryCategory 有一个字段UrlMetaData urlMetaData

3) 要走的路可能是http://grails.org/doc/2.1.0/ref/Domain%20Classes/createCriteria.html,我会尝试为您的特定案例概述基础知识

4) 不确定 theDomain 的含义 - 可能是代码异味,以及接受来自客户端的 rlike 参数

5) 下面的代码根本没有经过测试——特别是我不确定嵌套条件内部的析取是如何工作的。但这可能是一个合适的起点;记录 sql 查询应该有助于使其正常工作 (How to log SQL statements in Grails)

def c = Target.createCriteria() //create criteria on Target
def resultsList = c.list(max: resultLimit.toLong())  //list all matched entities up to resultLimit results
    category  //nested criteria for category
        //the following 'if' statement and its body is plain Groovy code rather than part of DSL that translates to Hibernate Criteria
        if (searchTerms)  //do the following only if searchTerms list is not empty
            or  // one of several conditions
                for (st in searchTerms)  // not a part of DSL - plain Groovy loop
                    rlike('name', st.trim())) //add a disjunction element
                
            
        
        urlMetaData  //nested criteria for metadata
            ne('ugcFlag', 1) //ugcFlag not equal 1
        
        
    order('weight', 'desc') //order by weight

or 限制在显式编写时可能效果更好

if (searchTerms)  
    def r = Restrictions.disjunction() 
    for (st in searchTerms) 
        r.add(new LikeExpression('name', st.trim()))
    
    instance.add(r) //'instance' is an injected property

干杯, 伊戈尔·西内夫

【讨论】:

谢谢伊戈尔!我编辑了原始帖子以包含数据库表和一些虚拟数据。在代码中修复了 domainName 的使用,并进一步解释了我最终要实现的目标。 我仍然不确定您是否已经拥有像 CategoryTarget 这样的域类,或者您现在需要创建它们。 如果你有域类,那么你能告诉我建议的代码是否有帮助吗? (我认为根据您的数据从头开始构建测试应用程序对我来说是不切实际的 - 无论如何它都是不准确的)

以上是关于grails:将 SQL 转换为域类的主要内容,如果未能解决你的问题,请参考以下文章

如何将 Grails 域类映射到 DTO?

Grails 中的 SQL/数据库视图

Grails:将枚举类型的mysql字段映射到域类

我应该将瞬态域类放在 grails 应用程序中的啥位置?

使用 TEXT 字段将 Grails 域类映射到遗留数据库的问题

向 Grails 域类添加字段?