Динамический поиск по критериям

Я использую QueryDSL с Spring Data Jpa и хочу выполнить динамический поиск.

Я следую этому ответу, и с BooleanBuilder все в порядке, но в моем случае мне нужно объединяться.

Итак, как я могу это сделать, если у меня есть 3 соединения на player, player_team, team и у меня есть необязательные параметры для имени игрока и названия его команды?

 ________       ___________________       _______
| player |    | player_team         |    | team  |
|------  |    |----------------     |    |-------|
| id     |    | player_team_id (pk) |    | id    |
| name   |    | player_id (fk)      |    | name  |
  ------      | team_id (fk)        |     -------
                -----------      

player.java

@Entity
@Table(...)
public class Player implements java.io.Serializable {

    private Integer idPlayer ;
    private String namePlayer;
    private Set<PlayerTeam> player_teams = new HashSet<PlayerTeam>(0);  
    ...
}

команда.java

@Entity
@Table(...)
public class Team implements java.io.Serializable {

    private Integer idTeam ;
    private String nameTeam;
    private Set<PlayerTeam> player_teams = new HashSet<PlayerTeam>(0);  
    ...
}  

player_team.java

@Entity
@Table(...)
public class PlayerTeam implements java.io.Serializable {

    private Integer idPlayerTeam ;
    private Team team;
    private Player paleyr;
    ...
} 

и для каждого домена у меня есть такой репозиторий:

public interface PlayerRespository extends JpaRepository<Player, Integer>, QueryDslPredicateExecutor<Player> {

}

person Youssef    schedule 16.07.2014    source источник
comment
Как выглядит модель предметной области?   -  person Timo Westkämper    schedule 18.07.2014
comment
я обновил свой вопрос   -  person Youssef    schedule 19.07.2014
comment
Укажите код для модели домена. Какие поиски? Что присоединяется?   -  person philipxy    schedule 20.07.2014


Ответы (2)


Если вы не добавляете дополнительные свойства в PlayerTeam, его не следует моделировать как сущность. Что касается условий, то это будет

player.namePlayer.eq(...)

а также

new JPASubQuery().from(playerTeam)
    .where(playerTeam.player.eq(player), palyerTeam.team.name.eq(...))
    .exists()
person Timo Westkämper    schedule 19.07.2014
comment
но я хочу выбрать только игроков, и у меня есть дополнительное поле в моей PlayerTeam. и почему вы используете JPASubQuery(), а не PlayerRespository и его метод findall()? - person Youssef; 19.07.2014
comment
Данные два выражения являются аргументами для метода findAll, вы можете комбинировать их через first.and(second) - person Timo Westkämper; 20.07.2014

Вы пробовали использовать Specification? В репозиториях Spring JPA есть этот метод для поиска результатов с использованием спецификаций:

List<T> findAll(Specification<T> spec);

Существуют разные подходы к построению спецификации, мой подход адаптирован для приема запроса от моей службы REST, поэтому я по существу создаю пустой объект заданного типа (в данном случае Foo) и устанавливаю любые критерии поиска, предоставленные в запросе (имя например), затем я создаю предикаты из каждого поля (если указано поле имени, добавьте предикат «имя равно «боб»»).

Вот пример построителя спецификаций:

import static org.springframework.data.jpa.domain.Specifications.where;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.commons.lang3.StringUtils;
import org.springframework.data.jpa.domain.Specification;

import com.acme.model.security.Principal;

public class FooSpecification { 

    private final Foo criteria;
    private String query;

    public FooSpecification(String query, Foo criteria) {
        this.query = query;
        this.criteria = criteria;
    }

    public Specification<Foo> trainerEquals() {
        if (criteria.getTrainer() == null) {
            return null;
        }

        return new Specification<Foo>() {

            @Override
            public Predicate toPredicate(Root<Foo> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                return cb.equal(root.<Principal>get("trainer").<Long>get("id"), criteria.getTrainer().getId());
            }
        };
    }

    public <T> Specification<Foo> valueEquals(final T value, final String field) {
        if (value == null) {
            return null;
        }

        return new Specification<Foo>() {

            @Override
            public Predicate toPredicate(Root<Foo> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                return cb.equal(root.<T> get(field), value);
            }
        };
    }

    /**
     * Convert input string to lower case, appends '%' around it and does SQL LIKE comparison with the field value, also lower cased.
     * If value is null, no comparison is done.  Example:
     * 
     * value = "John";
     * field = "firstName";
     * 
     * resulting specification = "name like '%john%'"
     * 
     * @param value string or null
     * @param field field name
     * @return SQL LIKE specification for the given value or null if value is null
     */
    public Specification<Foo> stringLike(final String value, final String field) {
        if (StringUtils.isBlank(value)) {
            return null;
        }

        return new Specification<Foo>() {

            @Override
            public Predicate toPredicate(Root<Foo> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                return cb.like(cb.lower(root.<String> get(field)), getLikePattern(value));
            }
        };
    }

    private String getLikePattern(String searchTerm) {
        return new StringBuilder("%")
                .append(searchTerm.toLowerCase().replaceAll("\\*", "%"))
                .append("%")
                .toString();
    }

    public Specification<Foo> fullSearch() {
        return where(trainerEquals())
                .and(valueEquals(criteria.getName(), "name"))
                .and(valueEquals(criteria.getInstructions(), "description"))
                .and(valueEquals(criteria.isAwesome(), "isAwesome"))
                .and(
                    where(
                            stringLike(query, "name"))
                        .or(stringLike(query, "instructions")
                    )
                );
    }
}
person SergeyB    schedule 18.07.2014
comment
вопрос касается QueryDSL, а не Specifications - person Rafik BELDI; 19.07.2014