【问题标题】:Grails, left outer joinGrails,左外连接
【发布时间】:2011-06-28 06:55:00
【问题描述】:

我有两个域类用户和项目,如下所示

Users{
    String firstName
    String lastName
    String emailAddress
    static hasMany = [projects:Projects]
}



class Projects {
    String projectName
    String description
    Users projectLead
    Date completionDate
    static belongsTo = Users
}

这里的completionDate == null 表示该项目尚未完成。

现在我想向每个用户发送关于他们未完成项目的电子邮件提醒,如何编写查询来检索每个用户的未完成项目?

我正在考虑以下几行,但仍然无法继续。为了发送电子邮件,我需要用户 emailid、所有不完整的项目和他们的姓名

def users = Users.list()
       for(user in users){
           user.projects.find{it.completionDate==null}
       }

这种情况下可以使用createCriteria吗?

【问题讨论】:

    标签: grails groovy left-join


    【解决方案1】:

    我认为这应该可行:

    def usersAndIncompleteProjects = Users.withCriteria {
        projects {
            isNull( completionDate )
        }
    }
    

    这应该只返回带有不完整项目的用户,并且每个 User 的 projects 属性将只包含不完整的项目。如果您希望用户加载所有项目,我believe you need to use an alias


    测试中...

    给定用户类:

    package criteriatest
    
    class User {
        String name
    
        static hasMany = [ projects: Project ]
    }
    

    还有项目类:

    package criteriatest
    
    class Project {
        String name
        Date completionDate
    
        static belongsTo = User
    
        static constraints = {
            completionDate( nullable:true )
        }
    }
    

    这个集成测试通过了(希望断言能解释它)

    package criteriatest
    
    import grails.test.*
    
    class UserTests extends GroovyTestCase {
        protected void setUp() {
            super.setUp()
            User.withSession { session ->
                def tim = new User( name:'tim' )
                def dave = new User( name:'dave' )
    
                [ tim, dave ]*.save()
    
                def project1 = new Project( name:'project 1', completionDate:null )
                def project2 = new Project( name:'project 2', completionDate:new Date() )
    
                tim.addToProjects project1
                tim.addToProjects project2
    
                [ project1, project2 ]*.save()
    
                session.flush()
                session.clear()
            }
        }
    
        protected void tearDown() {
            super.tearDown()
        }
    
        void testQuery() {
            def usersAndIncompleteProjects = User.withCriteria {
                projects {
                    isNull 'completionDate'
                }
                order 'name', 'asc'
            }
    
            // We get two users back (users with no projects get returned as well)
            assert usersAndIncompleteProjects.size() == 2
    
            // First user (dave) has no projects
            assert usersAndIncompleteProjects[0].projects.size() == 0
    
            // Second user (tim) has one project (with a null completionDate)
            assert usersAndIncompleteProjects[1].projects.size() == 1
    
            // Check it's the right project
            assert usersAndIncompleteProjects[1].projects*.name == [ 'project 1' ]
        }
    }
    

    (这是条件查询在此实例中执行的 sql):

    select
        this_.id as id1_1_,
        this_.version as version1_1_,
        this_.name as name1_1_,
        projects3_.user_projects_id as user1_3_,
        projects_a1_.id as project2_3_,
        projects_a1_.id as id0_0_,
        projects_a1_.version as version0_0_,
        projects_a1_.completion_date as completion3_0_0_,
        projects_a1_.name as name0_0_ 
    from
        user this_ 
    left outer join
        user_project projects3_ 
            on this_.id=projects3_.user_projects_id 
    left outer join
        project projects_a1_ 
            on projects3_.project_id=projects_a1_.id 
    where
        (
            projects_a1_.completion_date is null
        ) 
    order by
        this_.name asc
    

    【讨论】:

    • 只是为了确认,在运行此查询后,如果我要执行 usersInstance.projects.each(){......},它只会遍历完成日期为空的项目吗?
    • 我认为该链接很好地说明了为什么如果您关心您的联接是内部联接还是外部联接,为什么 HQL 是您的最佳选择:)
    • 我认为这将选择至少有一个未完成项目的所有用户。
    • 嘿蒂姆,非常感谢这个链接解释了很多。
    • 我添加了一个快速测试,以确保它在 Grails 1.3.7 中的行为仍然相同,而且似乎是......用代码更新我的答案......
    【解决方案2】:

    我不确定这个问题是否需要左连接,除非您想包含具有空用户的项目。为什么不直接选择所有完成日期为空的项目并针对用户加入?

    在 HQL 中,它看起来像这样:

    Projects.executeQuery('from Projects p join p.projectLead u where p.completionDate is null')
    

    您可以在条件查询中执行类似操作:

    Projects.withCriteria {
        isNull('completionDate')
        join('projectLead')
    }
    

    【讨论】:

    • 在这种情况下,我不会遍历每个用户的项目!我的意思是我想获取所有用户,然后为每个用户检索不完整的项目。这样我就可以为每个用户发送一封合并的电子邮件。
    • 如果您调用 HQL 查询,它将返回 [project, user] 对的列表。然后,您可以调用 results.groupBy{ it[1] } 将其转换为用户到 [project, user] 对的映射,并对其进行迭代。
    • 我提供的查询在一个查询中得到了所有信息。使用列表遍历所有用户然后选择不完整项目列表的直接方法可能更明显。不幸的是,它是一种 SQL 反模式 (n+1 selects),也无法扩展。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-10-01
    • 1970-01-01
    • 2016-10-02
    • 1970-01-01
    • 1970-01-01
    • 2011-09-10
    • 2014-02-24
    相关资源
    最近更新 更多