2012-01-16 62 views
3

我在獲取以下方案時遇到問題。學生可以參加考試。一個學生隨着時間的推移進行了一些測試,並獲得了每個測試的分數。每個學生實體都有一個已完成測試的列表,映射爲@OneToMany。JPA:根據多個子實體上的多個標準選擇實體

現在我想選擇所有已經完成了一系列分組標準測試的學生。我想例如,以搜索具有所有學生:

組1:完成「測試1」,得到了「75和100之間的」得分

和/或

集團2 :完成「測試2」並得到了「50到80之間的分數」

這是我迄今爲止的但它不會做我所需要的(不能通過多個參數進行搜索,這意味着我必須執行查詢多次):

SELECT s FROM Student s JOIN s.tests t WHERE t.score BETWEEN :minScore AND :maxScore AND t.testName = :testName 

有沒有辦法使用單個NamedQuery來實現我想要的?要檢索所有已完成符合上述至少一個參數組的測試的學生?我一直在嘗試加入,但一直在牆上跑。

我在下面做了一個示例代碼框架來說明我正在嘗試做什麼。

@Entity 
@NamedQueries({ 
    @NamedQuery(name="Student.findStudentByParams", query="????????") // What should this query look like to satisfy the criteria? (see below for more detail) 
}) 
public class Student { 
    // .. Some other variables that are not relevant for this example 

    @Id 
    private String name; 

    @OneToMany(fetch=FetchType.EAGER, mappedBy = "student") 
    private List<Test> tests; 

    // Setters and getters 
} 

@Entity 
public class Test { 
    private double score; 
    private String testName; 
    // .. Some other variables that are not relevant for this example 

    @ManyToOne(cascade=CascadeType.ALL) 
    private Student student; 

    // Setters and getters 
} 

public class SearchParameters { 
    private double minScore; 
    private double maxScore; 
    private String testName; 

    public SearchParameters(String minScore, String maxScore, String testName) { 
     this.minScore = minScore; 
     this.maxScore = maxScore; 
     this.testName = testName; 
    } 

    // Setters and getters 
} 

public class MainClass { 
    public static List<Student> getStudents(List<SearchParameters> searchParams) { 

     // Database initialization stuff 

     // What should the query look like to find all students that match any of the combined requirements in the searchParams list? 
     // Is it possible to do in a single query or should i make multiple ones? 
     // What parameters should i set? Is it possible to put in the entire array and do some sort of join? 

     // Retrieve all students which matches any of these search parameters: 
     // Have either: 
     //  Completed "Test 1" and got a score between 75 and 100 
     // and/or: 
     //  Completed "Test 2" and got a score between 50 and 80 
     Query namedQuery = em.createNamedQuery("Student.findStudentByParams"); 
     namedQuery.setParameter(??); 

     return (List<Student>)namedQuery.getResultList(); 

    } 
    public static void main() { 
     List<SearchParams> searchParams = new ArrayList<SearchParams(); 
     searchParams.add(new SearchParameters(75,100, "Test 1")); 
     searchParams.add(new SearchParameters(50,80, "Test 2")); 

     // Retrieve all students which matches any of these search parameters: 
     // Have either: 
     //  Completed "Test 1" and got a score between 75 and 100 
     // and/or: 
     //  Completed "Test 2" and got a score between 50 and 80 
     ArrayList<Student> students = getStudents(searchParams); 
     for(Student s: students) // Print all user that match the criteria 
     { 
      System.out.println("Name: " + s.getName()); 
     } 
    } 
} 
+0

看一看標準查詢生成器。您可以構建一個謂詞數組:http://www.ibm.com/developerworks/java/library/j-typesafejpa/ – perissf 2012-01-16 17:03:52

回答

2

您需要使用Criteria Builder(以及最終的規範Metamodel)。

嘗試這樣的事情(未測試的代碼):

EntityManager em; // put here your EntityManager instance 
CriteriaBuilder cb = em.getCriteriaBuilder(); 
CriteriaQuery<Student> cq = cb.createQuery(Student.class); 
Root<Student> student = cq.from(Student.class); 
Predicate predicate = cb.disjunction(); 
for (SearchParams param : searchParams) { 
    ListJoin<Student, Test> tests = student.join(Student_.tests); 
    Predicate tempPredicate1 = cb.equal(tests.get(Test_.testName), param.getTestName()); 
    Predicate tempPredicate2 = cb.ge(tests.get(Test_.score), param.getMinScore()); 
    Predicate tempPredicate3 = cb.le(tests.get(Test_.score), param.getMaxScore()); 
    Predicate tempPredicate = cb.and(tempPredicate1, tempPredicate2, tempPredicate3); 
    predicate = cb.or(predicate, tempPredicate); 
} 
cq.where(predicate); 
TypedQuery<Student> tq = em.createQuery(cq); 
return tq.getResultList(); 
+1

感謝您的代碼示例,它似乎正在做我正在尋找的東西。 – MrKiane 2012-01-17 08:35:08

1

我不明白如果不動態組合查詢將會如何。考慮使用Criteria API來創建它。

我會設計這樣的查詢:

select s from Student s where 
    exists (select t.id from Test t where t.student.id = s.id and ...) 
or 
    exists (select t.id from Test t where t.student.id = s.id and ...) 
or 
    exists (...) 

正如你看到的,有一個重複的模式,而所有這些子查詢是相似的合併成一個脫節。