【问题标题】:JPA Criteria: QuerySyntaxException for left join on treat entityJPA 标准:处理实体上的左连接的 QuerySyntaxException
【发布时间】:2021-03-29 10:04:46
【问题描述】:

型号:

@Entity
public class User {

    @Id
    private Integer id;

    @JoinColumn(name = "user_id")
    @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Project> projects;
}
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "Type")
public abstract class Project {

    @Id
    private Integer id;

    private String name;
}
@Entity
@DiscriminatorValue("Administrative")
public class AdminProject extends Project {

    private String departmentName;
}
@Entity
@DiscriminatorValue("Design")
public class DesignProject extends Project {

    private String companyName;
}

我正在尝试使用 JPA 的标准 api 根据 Project 的实现的属性来查询 User 实体。例如,查询具有“SOME_NAME”部门的项目的所有用户(DesignProject 上不存在该字段)。

我看到有一种方法可以通过向下转换查询的Project 实体来实现。我正在尝试类似的东西:

CriteriaBuilder cb...
Root<User> userRoot...
root = ((From) root).join("projects", JoinType.LEFT);
root = cb.treat(root, AdminProject.class);
root = root.get("departmentName");

例外:

org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.hql.internal.ast.QuerySyntaxException: 无效路径: 'generatedAlias2.departmentName' [从 io.github.perplexhub.rsql.model.User 中选择 generatedAlias0 作为 generatedAlias0 左连接generatedAlias0.projects as generatedAlias1 where Treat(generatedAlias2 as io.github.perplexhub.rsql.model.AdminProject).departmentName=:param0];嵌套异常是 java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: 无效路径: 'generatedAlias2.departmentName' [从 io.github.perplexhub.rsql.model.User 中选择 generatedAlias0 作为 generatedAlias0 左连接 generatedAlias0。项目作为 generateAlias1 where Treat(generatedAlias2 as io.github.perplexhub.rsql.model.AdminProject).departmentName=:param0]

我错过了什么?是否与加入有关,或者之后如何向下转换?

编辑

在@K.Nicholas 的回答之后,我设法使查询在一个孤立的场景中工作,但不是在我的应用程序上。 但是,我注意到entityManager.createQuery(query) 调用在第一次调用时会引发上述异常,如果我再次调用它而不更改查询对象,它就会起作用。这是第二次调用时生成的查询(此查询从数据库中找到我想要的对象):

select generatedAlias0 from User as generatedAlias0 left join generatedAlias0.projects as generatedAlias2 wheretreat(generatedAlias2 as io.github.perplexhub.rsql.model.AdminProject).departmentName=:param0

为什么实体管理器在连续调用两次时会创建两个不同的查询?

【问题讨论】:

    标签: java hibernate jpa criteria-api


    【解决方案1】:

    我会做 Entitys 有点不同,你会看到。主要问题是您使用User 作为您的根并加入Projects 列表。这是一个问题,因为您应该在 Project 类上有外键,并使用 projects 字段作为仅查询字段。这就是我所做的。这样效果更好。这也是一个问题,因为您必须使用join fetch 而不是join,以便projectsusers 一起获取。

    所以,首先,实体是这样的:

    @Entity
    public class User { 
        @Id
        private Integer id; 
        @OneToMany(mappedBy="user")
        private List<Project> projects;
    }
    
    @Entity
    @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
    @DiscriminatorColumn(name = "Type")
    public abstract class Project {
        @Id
        private Integer id;
        private String name;    
        @ManyToOne
        private User user;
    }
    
    @Entity
    @DiscriminatorValue("Administrative")
    public class AdminProject extends Project {
        private String departmentName;
    }
    
    @Entity
    @DiscriminatorValue("Design")
    public class DesignProject extends Project {    
        private String companyName;
    }
    

    经过一番挖掘,我发现了一个 JPQL 查询可以解决问题。这是一个起点:

    List<User> users = entityManager.createQuery("select distinct(u) from User u join fetch u.projects p where TYPE(p) = 'Administrative' and p.departmentName = 'dept1'", User.class).getResultList();
    

    经过一番挖掘后,我发现treat 工作得很好,如果你做得正确,并且在 JPA 2.1 中你应该使用 EntityGraph 做得到 join 来做 fetch

    CriteriaBuilder builder = entityManager.getCriteriaBuilder();
    CriteriaQuery<User> query = builder.createQuery(User.class);
    Root<User> root = query.from(User.class);
    Join<User, Project> join = root.join("projects");
    query.select(root).where(builder.equal(builder.treat(join, AdminProject.class).get("departmentName"), "dept1"));
    EntityGraph<User> fetchGraph = entityManager.createEntityGraph(User.class);
    fetchGraph.addSubgraph("projects");
    users = entityManager.createQuery(query.distinct(true)).setHint("javax.persistence.loadgraph", fetchGraph).getResultList();
    

    作为旁注,生成的查询略有不同,但我没有仔细研究它们。你应该。

    【讨论】:

    • 感谢您的回复。我测试了双向关系,获取图并不是真正需要的。即使没有它,您的解决方案也能正常工作。我有我的问题的例外情况发生在我第一次致电entityManager.createQuery(query) 时,但不是第二次。 我将根据您的回答所取得的进步来编辑我的问题
    猜你喜欢
    • 2018-11-13
    • 2021-02-15
    • 2020-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-19
    相关资源
    最近更新 更多