选择嵌套的Hibernate不可变对象会抛出org.hibernate.hql.internal.ast.QuerySyntaxException:意外令牌:new

时间:2016-06-30 11:52:12

标签: java spring hibernate jpa jpa-2.0

使用:

  • Hibernate 4.1.6
  • Spring 4.1
  • Java 8

我有两个不可变的数据模型:

家长模式

@Entity
@Immutable
@Table(name="PARENT_TABLE")
public class Parent {
    @Column(name="NAME")
    private final String name;

    @OneToMany(cascade=CascadeType.ALL, mappedBy="parent")
    @MapKey(name="key")
    Map<String, Child> children = new HashMap<>(1);

    public Parent(String name) {
        this.name = name;
    }

    public putChild(Child c) {
        Child childWithRef = new Child(this, c.getKey());
        children.put(c.getKey(), childWithRef);
    }
}

儿童模型

@Entity
@Immutable
@Table(name="CHILD_TABLE")
public class Child {
    @ManyToOne
    @JoinColumn(name="PARENT_ID") //say Parent has a generated ID
    private final Parent parent;

    @Column(name="KEY")
    private final String key;

    public Child(Parent p, String key) {
        this.parent = p;
        this.key = key;
    }
}

我想和他们的父母一起抓住所有的孩子,所以我尝试跑步(entityManager来自Spring):

entityManager.createQuery("SELECT new Child(new Parent('sample name'), c.key) FROM Child c").getResultList()

但我得到一个例外(问题表明是第二个 new):

org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: new near line 1, column ...

有谁知道如何获取嵌套的不可变对象?

我知道Hibernate真的反对不变性。由于我只执行简单的数据库任务,所以我还是想尝试一下。

1 个答案:

答案 0 :(得分:0)

您的查询对于JPQL是非法的。 JPQL BNF(JPA 2.1规范)非常明确地说明

select_expression ::= single_valued_path_expression | scalar_expression | aggregate_expression | 
    identification_variable | OBJECT(identification_variable) | constructor_expression
constructor_expression ::= NEW constructor_name ( constructor_item {, constructor_item}* )
constructor_item ::= single_valued_path_expression | scalar_expression | aggregate_expression |
    identification_variable

您无法嵌套构造函数表达式。

将其解压缩到单个对象中,然后在代码中拆分为您的结构。例如

SELECT c.key FROM Child c

获取子项的“键”,然后在代码中自己创建对象。

编辑完整代码:

以下是完整的解决方案,以防有人需要它(可能有很多方法可以改进和增加性能,因此请以此为基础进行理解):

//fetch all fields without instantiating any models from Hibernate;
//all aliases are for understanding only;
//notice `INNER JOIN c.parent` for fetching the parent data
List<Object[]> results = entityManager.createQuery("SELECT p.id as parentId, p.name as parentName, p.key as parentKey, c.key as childKey FROM Child c INNER JOIN c.parent").getResultList()

//use map for parents identification
Map<Long, Parent> parentsMap = new HashMap<>(results.size())

//now use the data to instantiate the immutable models
results.forEach(resultRecord -> {
    Long parentId = (Long) resultRecord[0];
    if (parentsMap.get(parentId) == null) { //first time meeting this parent
        Parent p = new Parent(resultRecord[0], resultRecord[1], resultRecord[2]);
        parentsMap.put(parentId, p);
    }

    Child c = new Child(parentsMap.get(parentId), resultRecord[3]);
    parentsMap.get(parentId).putChild(c);
}