英文:
Spring Data JPA Specification search for property in nested collection
问题
以下是您要翻译的内容:
Say I have at least two entities.
@Entity
public class Process {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true)
private String name;
@ManyToAny(
metaColumn = @Column(name = "node_type"),
fetch = FetchType.LAZY
)
@AnyMetaDef(
idType = "long", metaType = "string",
metaValues = {
@MetaValue(targetEntity = Milestone.class, value = MILESTONE_DISC),
@MetaValue(targetEntity = Phase.class, value = PHASE_DISC)
}
)
@Cascade({org.hibernate.annotations.CascadeType.ALL})
@JoinTable(
name = "process_nodes",
joinColumns = @JoinColumn(name = "process_id", nullable = false),
inverseJoinColumns = @JoinColumn(name = "node_id", nullable = false)
)
private Collection<ProcessNode> nodes = new ArrayList<>();
...
}
-------
@Entity
@ToString
@DiscriminatorValue(MILESTONE_DISC)
public class Milestone implements ProcessNode {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Collection<ResultDefinition> results;
@ToString.Exclude
@ManyToOne(fetch = FetchType.LAZY)
@Transient
private Process process;
...
}
Now I want to use spring data jpa specification to find (all) processes which have a milestone with name "S5".
Note that Milestone is a ProcessNode and there is another Entity called Phase which is also a ProcessNode. These can be contained in the "nodes" collection of my Process Entity.
I tried to write something like this:
public static Specification<Process> hasMilestoneWithName(final String milestoneName) {
return (Specification<Process>) (root, query, criteriaBuilder) -> {
Path<?> namePath = root.join("nodes").get("name");
return criteriaBuilder.equal(namePath, milestoneName);
};
}
This does not work, but throws:
> Caused by: java.lang.IllegalArgumentException: Unable to locate Attribute with the the given name [nodes] on this ManagedType [com.smatrics.dffs.processservice.model.entities.Process]
I don't really know how to use the API. Examples often refer to a meta-model that would be generated by the IDE or maven, but I really do not want to have any static generated resources. Please help me resolve this with Specification of spring-data-jpa without a generated meta-model.
Also if you could help me write the hql it would be awesome.
Thanks!
英文:
Say I have at least two entities.
@Entity
public class Process {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true)
private String name;
@ManyToAny(
metaColumn = @Column(name = "node_type"),
fetch = FetchType.LAZY
)
@AnyMetaDef(
idType = "long", metaType = "string",
metaValues = {
@MetaValue(targetEntity = Milestone.class, value = MILESTONE_DISC),
@MetaValue(targetEntity = Phase.class, value = PHASE_DISC)
}
)
@Cascade({org.hibernate.annotations.CascadeType.ALL})
@JoinTable(
name = "process_nodes",
joinColumns = @JoinColumn(name = "process_id", nullable = false),
inverseJoinColumns = @JoinColumn(name = "node_id", nullable = false)
)
private Collection<ProcessNode> nodes = new ArrayList<>();
...
}
@Entity
@ToString
@DiscriminatorValue(MILESTONE_DISC)
public class Milestone implements ProcessNode {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Collection<ResultDefinition> results;
@ToString.Exclude
@ManyToOne(fetch = FetchType.LAZY)
@Transient
private Process process;
...
}
Now I want to use spring data jpa specification to find (all) processes which have a milestone with name "S5".
Note that Milestone is a ProcessNode and there is another Entity called Phase which is also a ProcessNode. These can be contained in the "nodes" collection of my Process Entity.
I tried to write something like this:
public static Specification<Process> hasMilestoneWithName(final String milestoneName) {
return (Specification<Process>) (root, query, criteriaBuilder) -> {
Path<?> namePath = root.join("nodes").get("name");
return criteriaBuilder.equal(namePath, milestoneName);
};
}
This does not work, but throws:
> Caused by: java.lang.IllegalArgumentException: Unable to locate Attribute with the the given name [nodes] on this ManagedType [com.smatrics.dffs.processservice.model.entities.Process]
I don't really know how to use the API. Examples often refer to a meta-model that would be generated by the IDE or maven, but I really do not want to have any static generated resources. Please help me resolve this with Specification of spring-data-jpa without a generated meta-model.
Also if you could help me write the hql it would be awesome.
Thanks!
答案1
得分: 1
-
建议一个更简单的替代方案,从底部开始:
- 使用
name=S5
加载Milestone
实体:findByName("S5")
- 返回每个
Milestone
的Process
- 过滤掉重复项
- 使用
或者,您甚至可以通过返回不是Milestone
实体而是每个Milestone
的Process
的ID来节省一些SQL查询,然后通过ID列表加载Process
节点:
本机SQL等效代码为
select *
from process
where id in (
select process_id
from milestone
where name = 'S5'
)
不管我的解决方案如何,您的join
看起来对我来说并不完全正确,但我无法指出哪里出了问题 - 也许JPA元模型上有其他返回CollectionJoin
的方法?不确定。可能是因为@ManyToAny
不是JPA标准,所以JPA criteria API不会将nodes
识别为有效的“可连接”字段。
英文:
I would suggest a simpler alternative, coming from bottom-up:
- Load
Milestone
entities withname=S5
:findByName("S5")
- Return the
Process
for eachMilestone
- Filter out the duplicates
Or you could even save a few SQL queries by returning not the Milestone
entity but only the ID of the Process
for each Milestone
and then load the Process
nodes by a list of IDs:
The (native) SQL equivalent would be
select *
from process
where id in (
select process_id
from milestone
where name = 'S5'
)
Regardless of my solution your join
does not look completely correct to me but I can't point out what's wrong - maybe there are other methods on the JPA metamodel that return a CollectionJoin
? Not sure. Probably it is because @ManyToAny
is not JPA standard so the JPA criteria API does not recognize nodes
as a valid "joinable" field.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论