开发者

JPA Nullable JoinColumn

开发者 https://www.devze.com 2023-04-08 09:55 出处:网络
I have an entity: public class Foo { @Id @GeneratedValue private Long id; private String username; @ManyToOne(cascade = { CascadeType.MERGE }, fetch = FetchType.LAZY, optional = true)

I have an entity:

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
}

With this kind of relationship, each Foo object has a parent, except the first Foo instance in the DB which has a NULL ParentID. When i try to create a query via criteria api and try to search for the first Foo object (null parent id) using any of it's properties (ID, username etc..), i get a:

javax.persistence.NoResultException: No entity found for query

How should the JoinColumn be implemented in this case? How should i get the first Foo object in the DB which has a null parent ID?

Thanks

Updated September 28 2011

What i am trying to achieve is to look lookfor all Foos with a username starting in "foo" and would like to use as separate object being returned then:

CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder;
开发者_如何学PythonCriteriaQuery<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

assuming that the usernames are foo1, foo2 and foo3 wherein foo1 has a null parent, the result set will only return foo2 and foo3 even if the foo1 has a username that starts with the specified predicate. and also searching for foo1 alone will throw a

javax.persistence.NoResultException: No entity found for query 

Is there a way to still include foo1 with the rest of the results? or foo1 will always be a special case since i have to add a criteria specifying that the parent is null? Or perhaps ai have missed an option in the joinColumn to do so.

Thanks

UPDATED Thu Sep 29 13:03:41 PHT 2011 @mikku I have updated the criteria part of the post above (property1), because i think this is the part that is responsible for foo1 not to be included in the Result Set.

Here's a partial view of the generated query from the logs:

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 ?

and this wont return Foo1, obviously because username value is from foo_ which is why my original question is 'how should i get to Foo1'.

On the part that i have commented that ill use SelectCase and mix it up with your first reply, what i did is to add this part on the multiselect:

criteriaBuilder
   .selectCase()
      .when(criteriaBuilder.isNotNull(agent.get("parent")), agent.get("parent").get("username"))
      .otherwise(criteriaBuilder.literal("")),

replacing the

foo.get("parent").get("username")

However this wont be able to get Foo1 as well. My Last resort though inefficient would probably check if the parameters are of Foo1 and create a criteriaQuery specifying a literal value for username, and using the default query otherwise.

Suggestions/alternatives are very much appreciated.


EDIT:

In your edited question nullable joincolumn is not problem - missing construction of FooDto, multiselect etc are. You can achieve your goal with following:

Querying:

    /**
     * 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();

Object to hold data:

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

}
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号