问题描述
型号:
@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 根据 User
实现的属性查询 Project
实体。例如,查询具有“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: Invalid path: 'generatedalias2.departmentName' [selectgeneratedalias0 from io.github.perplexhub.rsql.model.User asgeneratedalias0 left join generatealias0.projects asgeneratedalias1 wheretreat(generatedalias2 as io.github.perplexhub.rsql.model.AdminProject).departmentName=:param0];嵌套异常是 java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: Invalid path: 'generatedalias2.departmentName' [selectgeneratedalias0 from io.github.perplexhub.rsql.model.User asgeneratedalias0 left join generatedalias0。项目作为generatedalias1 wheretreat(generatedalias2 as io.github.perplexhub.rsql.model.AdminProject).departmentName=:param0]
我错过了什么?是否与加入有关,或者之后向下转换是如何发生的?
编辑
在@K.Nicholas 的回答之后,我设法使查询在孤立的场景中起作用,但在我的应用程序中不起作用。
但是,我注意到 entityManager.createquery(query)
调用在第一次调用时抛出了上面的异常,如果我再次调用它而不更改查询对象,它会起作用。这是第二次调用时生成的查询(此查询从数据库中找到我想要的对象):
selectgeneratedalias0 from User asgeneratedalias0 left joingeneratedalias0.projects asgeneratedalias2 wheretreat(generatedalias2 as io.github.perplexhub.rsql.model.AdminProject).departmentName=:param0
解决方法
如您所见,我会对 Entitys
做一些不同的处理。主要问题是您使用 User
作为根并连接到 Projects
列表。这是一个问题,因为您应该在 Project
类上拥有外键并将 projects
字段用作仅查询字段。这就是我所做的。这样效果更好。这也是一个问题,因为您必须执行 join fetch
而不是 join
,以便 projects
与 users
一起获取。
所以,首先,实体是这样的:
@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
{1}}。
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();
顺便说一下,生成的查询略有不同,但我并没有仔细研究它们。你应该。