2011-06-28 77 views
2

我有两个领域类用户和项目,如以下Grails的,左外连接

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?

回答

4

我认为这应该工作:

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

这应该只是回到你不完整的项目,用户,和projects属性每个User将仅包含不完整的项目。如果您希望用户拥有所有项目的加载,我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 
+0

只是为了确认,在运行此查询后,如果我做usersInstance .projects.each(){......}它只会迭代通过null completiondate的项目吗? – Sap

+0

我认为这个链接很好地说明了为什么如果你关心你的连接是内部的还是外部的话,为什么HQL是最好的选择:) – ataylor

+0

这会选择所有至少有一个不完整项目的用户,我想。 – ataylor

3

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

在HQL,它会是这个样子:

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

你可以在条件查询类似:

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

在这种情况下,我不会迭代每个用户的项目!我的意思是我想获得所有用户,然后为每个用户检索不完整的项目。这样我就可以为每个用户发送一个合并电子邮件。 – Sap

+1

如果您调用HQL查询,它将返回一个[project,user]对的列表。然后,您可以调用'results.groupBy {it [1]}'将其转换为用户对[项目,用户]对的映射,并遍历它。 – ataylor

+0

我提供的查询在一个查询中全部获取。用列表遍历所有用户,然后选择不完整项目列表的直接方式可能更为明显。不幸的是,这是一个SQL反模式([n + 1选择](http://stackoverflow.com/questions/97197/what-is-the-n1-selects-problem)),它不会扩展。 – ataylor