JPA / HIBERNATE:Query如何返回非实体对象或具有内部非实体对象的对象列表?

时间:2017-02-13 15:40:17

标签: java hibernate jpa

有没有办法将自定义对象包含为另一个自定义对象的内部对象? 例如,我有两个Entites:

@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]

可以这样做吗?

3 个答案:

答案 0 :(得分:0)

尝试在new package.BarQueryResult(f.bars.id, f.bars.x)之后删除逗号?

答案 1 :(得分:0)

请注意,JPA / Hibernate API结构用于操作数据库对象(实体)。如果您想操纵非实体,那么最好在JPA之外创建自己的自定义集合。

那就是说,你的Foo是你的Bar对象('many'侧)的父对象('one'侧),即孩子。你可以做四件事来获得你的FooQueryResult(即Foo'a'和Bars):

1)在Bar对象(foo_id)中添加父字段。基本上,child使用父级的主键(@Id)与父级连接。

2)在Bar对象上创建与父Foo的多对一关系,即

@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") ...

最后,在Session 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)用于构造所有对象的另一个对象,包括Bars:

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

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

2)创建另一个仅返回Bars的结果对象:

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

3)在Foo实体中,使用仅构建Foos的构造函数创建返回所需Foo对象的查询:

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

4)在Bar实体中,创建一个返回Foo的所有Bars的查询:

@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;
}

答案 2 :(得分:0)

开箱即用是不可能的。你应该使用select new constructor expression,但是有一个限制 - 嵌套构造函数调用不受支持,你不能写select new FooQueryResult(..., new BarQueryResult)
或者您可以使用自定义ResultSetTransformer。但我建议创建Transformer类并手动完成。