2017-02-13 83 views
0

有沒有辦法將自定義對象作爲另一個自定義對象的內部對象? 例如,我有兩個的entites:JPA/HIBERNATE:Query如何返回非實體對象或具有內部非實體對象的對象列表?

@Entity 
class Foo { 
    @Id 
    private int id; 

    @Column 
    private String a; 

    @Column 
    private String b; 

    @OneToMany(...) 
    private Set<Bar> bars; 

    ... 
} 

@Entity 
class Bar { 
    @Id 
    private int id; 

    @Column 
    private String x; 

    @Column 
    private String y; 

    @Column 
    private String z; 

    ... 
} 

我想寫它可以選擇像FooQueryResult對象的查詢:

class FooQueryResult { 
    private String a; 
    private Set<BarQueryResult> bars; 

    ... 
} 

class BarQueryResult { 
    private String id; 
    private String x; 

    ... 
} 

我想喜歡的東西下面

String query = 
     "SELECT " + 
       "s.a, " + 
       "new package.BarQueryResult(f.bars.id, f.bars.x) " + 
     "FROM Foo as f " + 
     "WHERE f.id = ?1"; 

FooQueryResult site = factory.createEntityManager() 
     .createQuery(query, FooQueryResult.class) 
     .setParameter(1, fooId) 
     .getSingleResult(); 

但是,這以錯誤結束:

QuerySyntaxException: unexpected token: , near line 1, column 14 [SELECT s.a, new package.BarQueryResult(f.bars.id, f.bars.x) FROM package.Foo as f WHERE f.id = ?1] 

是否有可能做到這一點,以及如何?

+0

您是否試過只選擇'new package.BarQueryResult(..)'而沒有額外的's.a'? – Jack

+0

它引起另一個異常:QueryException:非法嘗試取消引用集合[foo0_.id.bars]與元素屬性引用[編號] –

回答

0

這是不可能的開箱。您應該使用選擇新的構造函數表達式,但有一個限制 - 嵌套構造函數調用不受支持,您不能編寫select new FooQueryResult(..., new BarQueryResult)
或者您可以使用您的自定義ResultSetTransformer。但我建議創建Transformer類並手動完成。

+0

順便說一句,你可以使用http://mapstruct.org變壓器,它有助於減少樣板代碼的數量 – idmitriev

0

嘗試在刪除逗號後new package.BarQueryResult(f.bars.id, f.bars.x)

+0

這不是它,我正在修復一個問題,並刪除逗號 –

0

請注意JPA/Hibernate API結構存在用於操作數據庫對象(實體)。如果你想操縱非實體,那麼你最好在JPA之外創建自己的自定義集合。

也就是說,你的Foo是你的Bar對象('多'一邊)的父對象('one'side),即孩子。你可以做四件事來得到你的FooQueryResult(即Foo'a'和Bars):

1)在Bar對象(foo_id)中添加一個父字段。基本上,孩子使用父母的主鍵(@Id)與父母聯繫。

2)創建Bar對象上與母體富一個多對一的關係,即

@ManyToOne 
    @JoinColumn(name="foo_id", 
    referencedColumnName = "foo_id") 
    private Foo foo; 

3)創建FooQueryResult作爲對象,返回查詢的結果,例如

public class FooQueryResult { 
    private String a; 
    private String b; 
    private String x; 
    private String y; 
    private String z; ... 

4)創建查詢(這裏使用JPA的命名查詢方法直接在子對象):

@Entity 
@Table(name="Bar") 
    @NamedQuery(name="Bar.FooQueryResult", 
    query="SELECT NEW FooQueryResult(" 
    + "e.foo.a, e.foo.b, e.x, e.y, e.z)" 
    + " FROM Bar e" 
    + " WHERE e.foo.id = :foo_id") ... 

最後,在你的會話bean,您可以創建返回結果列表的方法結合您的Foo和Bar對象:

public List<FooQueryResult> myResults(int foo_id) { 
    return em.createNamedQuery("Bar.FooQueryResult") 
     .setParameter("foo_id", foo_id) 
     .getResultList(); 
    } 

這裏是另一種方式,你可以實現你的5個步驟要什麼

1)創建您的Foo和聚合Bar對象的類。其中包含兩個構造函數:a)一個用於構造Foo對象的構造函數; b)另一種是用於構造所有的對象,包括酒吧:

public class FooQueryResult { 
private a; 
private b; 
private List<BarResult> bars; 

public FooQueryResult(a, b) ... 
public FooQueryResult(a, b, List<Bar> bars) ... 
... 

2)創建只返回酒吧另一個結果對象:

public class BarResult { 
    private x 
    private y 
    private z 
    ... 

3)在富實體,創建查詢該返回美孚對象所需,利用建立的Foo構造函數只:

@Entity 
@Table(name="Foo") 
    @NamedQuery(name="Bar.FooQueryResult", 
    query="SELECT NEW FooQueryResult(" 
    + "e.a, e.b)" 
    + " FROM Foo e" 
    ... 

4)在酒吧實體,創建一個返回所有的酒吧爲一個Foo查詢:

@Entity 
@Table(name="Bar") 
    @NamedQuery(name="Bar.BarResult", 
    query="SELECT NEW BarResult(" 
    + "e.x, e.y, e.z)" 
    + " FROM Bar e" 
    + " WHERE e.foo.id = :foo_id") ... 

5)最後,創建一個返回聚合的方法:

public List<FooQueryResult> myResults() { 
    List<FooQueryResult> aggregate; 

     for (FooQueryResult foo : em.createNamedQuery("Foo.FooQueryResult") 
      .getResultList()) { 

      // create a new result 
      FooQueryResult query = new FooQueryResult(); 
// add Foos 
      query.setA(foo.getA()); 
      query.setB(foo.getB()); 

      // .. then add Bars 
      query.setBars(em.createNamedQuery("Bar.BarResult") 
      .setParameter("foo_id", foo.id) 
      .getResultList()); 

      // add query to aggregate 
      aggregate.add(query); 
      } 
    return aggregate; 
} 
+0

我知道我可以做到這一點那樣。但是,我想避免將自定義的轉換從一個結構寫入其他結構。這就是爲什麼我想寫查詢返回聚合嵌套對象,而不是平面列表的原因。你認爲這是不可能的休眠? –

+0

通常,JPA不支持嵌套的集合關係。看到這篇文章:https://en.wikibooks.org/wiki/Java_Persistence/Relationships#Nested_Collections.2C_Maps_and_Matrices。但是,請參閱我編輯的答案,該答案基於使用兩個查詢提供結果的解決方法。 – nkmuturi