我想知道是否有人知道抛出Exception的原因?我下面有以下实体。是不是因为返回的一些 Employees 不是 DriverEmployees,所以没有路由?
@Entity
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Integer id;
private String name;
}
@Entity
public class DriverEmployee extends Employee {
@OneToMany(fetch = FetchType.LAZY)
private List<Routes> routes;
}
@Entity
public class Routes {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Integer id;
private String name;
private String description;
}
CriteriaQuery<Tuple> criteria = criteriaBuilder.createQuery(Tuple.class);
Root<Employee> employee = criteria.from(Employee.class);
Root<DriverEmployee> driverEmployee = criteriaBuilder.treat(employee, DriverEmployee.class);
ListJoin<DriverEmployee, Routes> routes = driverEmployee.joinList("routes");
// Want routes to be returned as a list in the multiselect.
criteria.multiselect(employee.get("name").alias("name"), routes.alias("routes"));
TypedQuery<Tuple> query = em.createQuery(criteria);
query.getResultList().forEach((t) -> {
process(t.get("name", String.class));
processList(t.get("routes", List.class).size());
});
我收到的错误如下。有谁知道如何才能让下面的内容成功运行?
Caused by: java.lang.IllegalStateException: No data type for node: org.hibernate.hql.internal.ast.tree.IdentNode
+-[IDENT] IdentNode: 'routes' {originalText=routes}
在这个例子中,你有一个元组,它包含一个DriverEmployee对象和一个Routes对象(我建议你把这个实体改名为Route,并设置@Table(name = "routes")。根据JavaDoc的 multiselect()
:
如果标准查询的类型是CriteriaQuery(即通过createTupleQuery方法或向createQuery方法传递Tuple类参数创建的标准查询对象),则会按照指定的顺序,为查询执行后的每一条记录实例化并返回一个与多选方法的参数相对应的Tuple对象。
因此,这意味着你不能做一个类似于 Tuple<DriverEmployee, List<Routes>>
如何能达到你当前查询的这种行为的方法就是自己去做。例如
你的方法与标准:
CriteriaQuery<Tuple> criteria = criteriaBuilder.createQuery(Tuple.class);
Root<Employee> employee = criteria.from(Employee.class);
Root<DriverEmployee> driverEmployee = criteriaBuilder.treat(employee, DriverEmployee.class);
ListJoin<DriverEmployee, Routes> routes = driverEmployee.joinList("routes");
criteria.multiselect(employee.get("name").alias("name"), routes.alias("routes"));
TypedQuery<Tuple> query = em.createQuery(criteria);
List<Tuple> resultList = query.getResultList();
Map<String, List<Routes>> resultMap = getMapFromResultList(resultList);
resultMap.entrySet().forEach((name, routesList) -> {
process(name);
processList(routesList);
});
和获取地图的方法
private Map<String, List<Routes>> getMapFromResultList(List<Tuple> tuples) {
Map<String, List<Routes>> map = new HashMap<>();
tuples.forEach(tuple -> {
String name = tuple.get("name", String.class);
Routes routes = tuple.get("routes", Routes.class);
map.computeIfPresent(name, (key, value) -> {
value.add(routes);
return value;
});
map.computeIfAbsent(name, (key) -> {
List<Routes> routesList = new ArrayList<>();
routesList.add(routes);
return routesList;
});
});
return map;
}
我想Hibernate的JPA Criteria实现并不支持。如果你真的想用JPA Criteria API来做这件事,你可能就没戏了。在JPQLHQL中,你可以像这样建模 SELECT e.name, r FROM DriverEmployee e LEFT JOIN e.routes r
. 除此之外,你还必须像Andrew Kolesnyk提到的那样提取值。
然而,这是一个完美的使用案例。Blaze-持久性实体视图.
Blaze-Persitence是一个JPA之上的查询构建器,它支持JPA模型之上的许多高级DBMS功能。我在它上面创建了实体视图,以允许JPA模型和自定义接口定义的模型之间的轻松映射,就像Spring Data Projections一样。这个想法是,你以你喜欢的方式定义你的目标结构,并通过JPQL表达式将属性(getters)映射到实体模型。由于属性名被用作默认映射,你大多不需要显式映射,因为80%的用例是拥有实体模型子集的DTO。
你的模型的映射可以像下面这样简单。
@EntityView(DriverEmployee.class)
interface EmployeeDto {
@IdMapping
Integer getId();
String getName();
List<RoutesDto> getRoutes();
}
@EntityView(Routes.class)
interface RoutesDto {
@IdMapping
Integer getId();
String getName();
}
查询就是应用实体视图进行查询,最简单的只是按id进行查询。
EmployeeDto dto = entityViewManager.find(entityManager, EmployeeDto.class, id);
通过Spring Data集成,你几乎可以像Spring Data Projections一样使用它。https:/persistence.blazebit.comdocumentationentity-viewmanualen_USindex.html#spring-data-features。 而且还可以保存回来。这里是一个示例仓库
@Repository
interface EmployeeRepository {
EmployeeDto findOne(Long id);
}
它只会获取你告诉它要获取的映射。
这里你可以看到一个示例项目。https:/github.comBlazebitblaz-persistencetreemasterexamplesspring-data-webmvc。