JPA Nullable JoinColumn - PullRequest
       9

JPA Nullable JoinColumn

0 голосов
/ 27 сентября 2011

У меня есть сущность:

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 имеет родителя, кроме первого экземпляра Foo в БД, который имеет NULL ParentID. Когда я пытаюсь создать запрос через критерии API и пытаюсь найти первый объект Foo (нулевой родительский идентификатор), используя любое из его свойств (идентификатор, имя пользователя и т. Д.), Я получаю:

javax.persistence.NoResultException: No entity found for query

Как должен быть реализован JoinColumn в этом случае? Как мне получить первый объект Foo в БД с нулевым родительским идентификатором?

Спасибо

Обновлено 28 сентября 2011 года

Чего я пытаюсь добиться, так это искать все 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 всегда будет особым случаем, так как я должен добавить критерий, указывающий, что родительский элемент является нулевым? Или, возможно, я пропустил опцию в joinColumn, чтобы сделать это.

Спасибо

ОБНОВЛЕНО Чт 29 сентября 13:03:41 PHT 2011 @mikku Я обновил часть критериев поста выше (property1), потому что я думаю, что именно эта часть отвечает за то, что foo1 не будет включена в Result Set.

Вот частичное представление сгенерированного запроса из журналов:

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, и создаст критерийQuery, задающий буквальное значение для имени пользователя и использующий запрос по умолчанию в противном случае.

Предложения / альтернативы очень ценятся.

1 Ответ

1 голос
/ 27 сентября 2011

EDIT:

В вашем отредактированном вопросе 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; }

}
...