选择嵌套的 Hibernate 不可变 objects 抛出 org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: new
Selecting nested Hibernate immutable objects throws org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: new
使用:
- 休眠 4.1.6
- Spring 4.1
- Java 8
我有两个不可变数据模型:
Parent 型号
@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);
}
}
Child 型号
@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;
}
}
我想获取所有 children,以及它们的 parents,所以我尝试 运行(entityManager
来自 Spring):
entityManager.createQuery("SELECT new Child(new Parent('sample name'), c.key) FROM Child c").getResultList()
但我得到一个例外(指示的问题是 second new
):
org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: new near line 1, column ...
有谁知道如何获取嵌套的不可变 objects?
我知道 Hibernate 确实反对不变性。由于我只是执行简单的数据库任务,所以我还是想尝试一下。
您的查询对于 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
您不能嵌套构造函数表达式。
将其提取到单个 object 中,然后在您的代码中拆分为您的结构。例如
SELECT c.key FROM Child c
获取 child 的 "key",然后在您的代码中自己创建 object。
编辑完整代码:
这里是完整的解决方案,以防有人需要它(可能有很多方法可以提高和增加性能,所以以此作为理解的基础):
//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);
}
使用:
- 休眠 4.1.6
- Spring 4.1
- Java 8
我有两个不可变数据模型:
Parent 型号
@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);
}
}
Child 型号
@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;
}
}
我想获取所有 children,以及它们的 parents,所以我尝试 运行(entityManager
来自 Spring):
entityManager.createQuery("SELECT new Child(new Parent('sample name'), c.key) FROM Child c").getResultList()
但我得到一个例外(指示的问题是 second new
):
org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: new near line 1, column ...
有谁知道如何获取嵌套的不可变 objects?
我知道 Hibernate 确实反对不变性。由于我只是执行简单的数据库任务,所以我还是想尝试一下。
您的查询对于 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
您不能嵌套构造函数表达式。
将其提取到单个 object 中,然后在您的代码中拆分为您的结构。例如
SELECT c.key FROM Child c
获取 child 的 "key",然后在您的代码中自己创建 object。
编辑完整代码:
这里是完整的解决方案,以防有人需要它(可能有很多方法可以提高和增加性能,所以以此作为理解的基础):
//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);
}