【问题标题】:ListJoin and multiselect with criteriaquery with PolymorphismListJoin 和 multiselect with criteriaquery with Polymorphism
【发布时间】:2020-05-20 01:50:31
【问题描述】:

我想知道是否有人知道引发异常的原因?我在下面有以下实体。是不是因为返回的一些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}

【问题讨论】:

  • 我认为不可能在多选中选择一个集合。在这种情况下,路由是一对多的 List.

标签: java hibernate jpa-2.0 criteria-api


【解决方案1】:

在当前示例中,您的元组包含一个 DriverEmployee 对象和一个 Routes 对象(我建议您将此实体重命名为 Route 并设置 @Table(name = "routes")。根据 multiselect() 的 JavaDoc :

如果条件查询的类型是 CriteriaQuery(即,由 createTupleQuery 方法或通过将 Tuple 类参数传递给 createQuery 方法创建的条件查询对象),则对应于多选方法的参数的 Tuple 对象,按照指定的顺序,将为查询执行产生的每一行实例化并返回。

所以这意味着你不能像 Tuple&lt;DriverEmployee, List&lt;Routes&gt;&gt; 这样创建一个元组 如何达到当前查询的这种行为的方法是自己做。例如:

您的方法与标准:

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

【讨论】:

    【解决方案2】:

    我猜 Hibernate JPA Criteria 实现不支持这一点。如果您真的想使用 JPA Criteria API 来执行此操作,那么您可能不走运。在 JPQL/HQL 中,您可以像 SELECT e.name, r FROM DriverEmployee e LEFT JOIN e.routes r 一样建模。最重要的是,您必须提取像 Andrew Kolesnyk 提到的值。

    不过,这是 Blaze-Persistence Entity Views 的完美用例。

    Blaze-Persitence 是基于 JPA 的查询构建器,它支持 JPA 模型之上的许多高级 DBMS 功能。我在它之上创建了实体视图,以允许在 JPA 模型和自定义接口定义模型之间轻松映射,例如 Spring Data Projections on steroids。这个想法是您以您喜欢的方式定义您的目标结构,并通过 JPQL 表达式将属性(getter)映射到实体模型。由于属性名称用作默认映射,因此您通常不需要显式映射,因为 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.com/documentation/entity-view/manual/en_US/index.html#spring-data-features,它也可以保存回来。这里是一个示例存储库

    @Repository
    interface EmployeeRepository {
      EmployeeDto findOne(Long id);
    }
    

    它只会获取您告诉它获取的映射。

    这里你可以看到一个示例项目:https://github.com/Blazebit/blaze-persistence/tree/master/examples/spring-data-webmvc

    【讨论】:

      猜你喜欢
      • 2016-12-22
      • 2020-08-24
      • 1970-01-01
      • 1970-01-01
      • 2021-05-10
      • 1970-01-01
      • 1970-01-01
      • 2012-10-19
      • 1970-01-01
      相关资源
      最近更新 更多