我有一个实体:
public class Foo {
@Id
@GeneratedValue
private Long id;
private String username;
@ManyToOne(cascade = { CascadeType.MERGE }, fetch = FetchType.LAZY, optional = true)
@JoinColumn(name = "ParentID", nullable = true)
private Foo parent;
// other fields
}
使用这种关系,每个Foo对象都有一个父对象,除了DB中具有NULL ParentID的第一个Foo实例。当我尝试通过条件api创建一个查询并尝试使用它的任何属性(ID,用户名等)搜索第一个Foo对象(null父ID)时,我得到一个:
javax.persistence.NoResultException: No entity found for query
在这种情况下应如何实施JoinColumn?我应该如何获得DB中具有空父ID的第一个Foo对象?
由于
2011年9月28日更新
我想要实现的是查看所有Foos,其用户名以“foo”开头,并且希望将其作为单独的对象使用,然后返回:
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder;
CriteriaQuery<FooDto> criteriaQuery = criteriaBuilder.createQuery(FooDto.class);
Root<Foo> foo = criteriaQuery.from(Foo.class);
criteriaQuery.multiselect(foo.get("id"), foo.get("username"), foo.get("parent").get("username"), foo.get("property1")
//other selected properties);
Predicate predicate = criteriaBuilder.conjunction();
predicate = criteriaBuilder.and(predicate, criteriaBuilder.like(foo.get("username").as(String.class), criteriaBuilder.parameter(String.class, "username")));
// other criteria
criteriaQuery.where(predicate);
TypedQuery<FooDto> typedQuery = entityManager.createQuery(criteriaQuery);
typedQuery.setParameter("username", "foo%");
// other parameters
// return result
假设用户名是foo1,foo2和foo3,其中foo1具有空父级,结果集将仅返回foo2和foo3,即使foo1具有以指定谓词开头的用户名。 而且单独搜索foo1会抛出一个
javax.persistence.NoResultException: No entity found for query
有没有办法在其余的结果中包含foo1?或者foo1将始终是一个特例,因为我必须添加一个指定父项为空的条件?或者也许ai错过了joinColumn中的一个选项。
由于
更新时间:9月29日13:03:41 PHT 2011 @mikku 我已更新上面帖子的标准部分(property1),因为我认为这是负责foo1不包含在结果集中的部分。
以下是日志生成的查询的部分视图:
select
foo.id as id,
foo.username as username,
foo.password as password,
foo.ParentID as parentId,
foo_.username as parentUsername,
foo.SiteID as siteId,
from FOO_table foo, FOO_table foo_ cross join Sites site2_
where foo.ParentID=foo_.id and foo.SiteID=site2_.id and 1=1
and foo.username=? and site2_.remoteKey=? limit ?
并且这不会返回Foo1,显然是因为用户名值来自foo_,这就是为什么我的原始问题是'我应该如何到达Foo1'。
在我评论说使用SelectCase并将其与你的第一个回复混淆的部分,我所做的就是在多选中添加这个部分:
criteriaBuilder
.selectCase()
.when(criteriaBuilder.isNotNull(agent.get("parent")), agent.get("parent").get("username"))
.otherwise(criteriaBuilder.literal("")),
替换
foo.get("parent").get("username")
然而,这也不能得到Foo1。我的最后一种方法虽然效率低,但可能会检查参数是否为Foo1并创建一个criteriaQuery指定用户名的文字值,否则使用默认查询。
非常感谢建议/备选方案。
答案 0 :(得分:1)
编辑:
在你编辑的问题中,nullable joincolumn不是问题 - 缺少FooDto,multiselect等的构造。您可以通过以下方式实现目标:
查询:
/**
* Example data:
* FOO
* |id|username|property1|parentid|
* | 1| foo | someval| null|<= in result
* | 2| fooBoo | someval2| 1|<= in result
* | 3|somename| someval3| null|
*/
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<FooDto> c = cb.createQuery(FooDto.class);
Root<Foo> fooRoot = c.from(Foo.class);
Predicate predicate = cb.like(fooRoot.get("username").as(String.class),
cb.parameter(String.class, "usernameParam"));
c.select(
cb.construct(FooDto.class,
fooRoot.get("id"),
fooRoot.get("username"),
fooRoot.get("property1")))
.where(predicate);
TypedQuery<FooDto> fooQuery = em.createQuery(c);
fooQuery.setParameter("usernameParam", "foo%");
List<FooDto> results = fooQuery.getResultList();
保存数据的对象:
public class FooDto {
private final long id;
private final String userName;
private final String property1;
//you need constructor that matches to type and order of arguments in cb.construct
public FooDto(long id, String userName, String property1) {
this.id = id;
this.userName = userName;
this.property1 = property1;
}
public long getId() { return id; }
public String getUserName() { return userName; }
public String getProperty1() { return property1; }
}