简体   繁体   中英

JPA Criteria Join OneToMany table where clause does not work

I have two tables.

CREATE TABLE public.question
(
    id       SERIAL PRIMARY KEY
  , day      VARCHAR(2)        NOT NULL
  , month    VARCHAR(2)        NOT NULL
  , year     VARCHAR(4)         NOT NULL
);

CREATE TABLE public.question_translation
(
    id SERIAL PRIMARY KEY
  , question_id   INT REFERENCES public.question(id) NOT NULL
  , question_text TEXT                               NOT NULL
  , language      VARCHAR(2)                         NOT NULL
);

Now i want to create criteria to retrieve question. SQL like this:

SELECT * FROM question q LEFT JOIN question_translation qt ON q.id = qt.question_id WHERE qt.language = 'en'

In java using JPA Criterias it looks like this:

@Override
public Collection<Question> findByMonthYear(String month, String year, String locale) {
    EntityManager em = sessionFactory.createEntityManager();
    CriteriaBuilder builder = em.getCriteriaBuilder();
    CriteriaQuery<Question> criteriaQuery = builder.createQuery(Question.class);

    List<Predicate> predicates = new ArrayList<>();

    Root<Question> questionRoot = criteriaQuery.from(Question.class);
    ListJoin<Question, QuestionTranslation> questionTranslationJoinRoot = questionRoot.join(Question_.questionTranslation, JoinType.LEFT);

    predicates.add(builder.equal(questionRoot.get(Question_.month), month));
    predicates.add(builder.equal(questionRoot.get(Question_.year), year));
    predicates.add(builder.equal(questionTranslationJoinRoot.get(QuestionTranslation_.language), locale));

    criteriaQuery.select(questionRoot).where(predicates.toArray(new Predicate[]{}));

    TypedQuery<Question> query = em.createQuery(criteriaQuery);

    String queryString = query.unwrap(Query.class).getQueryString();

    return query.getResultList();
}

I use ListJoin because in metamodel Question_.class i got this line:

public static volatile ListAttribute<Question, QuestionTranslation> questionTranslation;

But this one return me Question class with List QuestionTranslation with two entries where language field equals en and de values. But i specify where clause to return me only one entry where language equals en value. What's wrong with my code?

UPDATE #1:

I have second case.

One more table:

CREATE TABLE public.user_answer
(
    uuid        VARCHAR(36) PRIMARY KEY
  , user_uuid   VARCHAR(36) REFERENCES public.users(uuid)  NOT NULL
  , question_id INT         REFERENCES public.question(id) NOT NULL
  , answer      TEXT                                       NOT NULL
);

And i want to make SQL like this:

SELECT * FROM user_answer ua LEFT JOIN question q on ua.question_id = q.id LEFT JOIN question_translation qt ON q.id = qt.question_id WHERE qt.language = 'en' AND ua.user_uuid = '00000000-user-0000-0000-000000000001' AND q.month = '01' AND q.day = '01' AND q.year = '2016';

In java using JPA Criterias it looks like this:

@Override
public UserAnswer findByDayMonthYear(String day, String month, String year, User user, String locale) {
    EntityManager em = sessionFactory.createEntityManager();
    CriteriaBuilder builder = em.getCriteriaBuilder();
    CriteriaQuery<UserAnswer> criteriaQuery = builder.createQuery(UserAnswer.class);

    List<Predicate> predicates = new ArrayList<>();

    Root<UserAnswer> userAnswerRoot = criteriaQuery.from(UserAnswer.class);
    Join<UserAnswer, Question> questionJoin = userAnswerRoot.join(UserAnswer_.question);
    ListJoin<Question, QuestionTranslation> questionTranslatJoin = questionJoin.join(Question_.questionTranslation);

    predicates.add(builder.equal(builder.treat(questionJoin, Question.class).get(Question_.day), day));
    predicates.add(builder.equal(builder.treat(questionJoin, Question.class).get(Question_.month), month));
    predicates.add(builder.equal(builder.treat(questionJoin, Question.class).get(Question_.year), year));
    predicates.add(builder.equal(builder.treat(questionTranslatJoin, QuestionTranslation.class).get(QuestionTranslation_.language), locale));
    predicates.add(builder.equal(userAnswerRoot.get(UserAnswer_.user), user));

    criteriaQuery.select(userAnswerRoot).where(predicates.toArray(new Predicate[]{}));

    TypedQuery<UserAnswer> query = em.createQuery(criteriaQuery);

    String queryString = query.unwrap(Query.class).getQueryString();

    return query.getSingleResult();
}

In this case Question has List with two items of QuestionTranlsation with languages en and de , but i need only one QuestionTranlsation entry where language equals en .

What i have to do in this case?

This is possible with JPA 2.1 feature JOIN ON

see How to do JOIN ON query using Criteria API

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM