JPA Nullable JoinColumn

时间:2011-09-27 04:13:30

标签: jpa join nullable

我有一个实体:

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指定用户名的文字值,否则使用默认查询。

非常感谢建议/备选方案。

1 个答案:

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

}