it-swarm.com.de

Dynamische Abfrage des Spring JPA Repository

Derzeit habe ich die folgende benutzerdefinierte Basisabfrage für Spring JPA Repository verwendet.

 @Query("SELECT usr FROM User usr  WHERE usr.configurable = TRUE "
              + "AND (" +
                        "lower(usr.name) like lower(:filterText) OR lower(usr.userType.classType.displayName) like lower(:filterText) OR lower(usr.userType.model) like lower(:filterText)"
              +      ")"
              + "")
  public List<User> findByFilterText(@Param("filterText") String filterText, Sort sort);

Ich muss diese Abfrage ändern, wenn Filtertext ein durch Kommas getrennter Wert wird. Aber wie folgt, wird es eine dynamische Abfrage und wie kann ich sie ausführen? 

Dynamische Abfrage, die ich bauen muss, 

String sql = "SELECT usr FROM User usr WHERE usr.configurable = TRUE";

for(String Word : filterText.split(",")) {
                sql += " AND (lower(usr.name) like lower(:" + Word + ") OR lower(usr.userType.classType.displayName) like lower(:" + Word + ") OR lower(usr.userType.model) like lower(:" + Word + "))";
}
15
Channa

Pro JB Nizet und der Spring-Data-Dokumentation sollten Sie eine benutzerdefinierte Schnittstelle + Repository-Implementierung verwenden.

Erstellen Sie eine Schnittstelle mit der Methode:

public interface MyEntityRepositoryCustom {
    List<User> findByFilterText(Set<String> words);
}

Erstellen Sie eine Implementierung:

@Repository
public class MyEntityRepositoryImpl implements MyEntityRepositoryCustom {
    @PersistenceContext
    private EntityManager entityManager;

    public List<User> findByFilterText(Set<String> words) {
        // implementation below
    }
}

Erweitern Sie die neue Schnittstelle in Ihrer vorhandenen Repository-Schnittstelle:

public interface MyEntityRepository extends JpaRepository<MyEntity, Long>, MyEntityRepositoryCustom {
    // other query methods
}

Rufen Sie schließlich die Methode woanders auf:

dao.findByFilterText(new HashSet<String>(Arrays.asList(filterText.split(","))));

Abfrageimplementierung

Ihre Methode zum Erzeugen der Variablen sql, dh durch Verketten einiger Zeichenfolgen in der Abfrage, ist fehlerhaft. Mach das nicht. 

Die Word, die Sie verketten, muss ein gültiger JPQL-Bezeichner sein, d. H. Ein :, gefolgt von einem Java-Bezeichner start , optional gefolgt von einem Java-Bezeichner . Wenn Ihre CSV foo bar,baz enthält, versuchen Sie, foo bar als Bezeichner zu verwenden, und Sie erhalten eine Ausnahme.

Sie können stattdessen CriteriaBuilder verwenden, um die Abfrage auf sichere Weise zu erstellen:

public List<User> findByFilterText(Set<String> words) {
    CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    CriteriaQuery<User> q = cb.createQuery(User.class);
    Root<User> user = q.from(User.class);

    Path<String> namePath = user.get("name");
    Path<String> userTypeClassTypeDisplayName = 
                     user.get("userType").get("classType").get("displayName");
    Path<String> userTypeModel = user.get("userType").get("model");
    List<Predicate> predicates = new ArrayList<>();
    for(String Word : words) {
        Expression<String> wordLiteral = cb.literal(Word);
        predicates.add(
                cb.or(
                    cb.like(cb.lower(namePath), cb.lower(wordLiteral)),
                    cb.like(cb.lower(userTypeClassTypeDisplayName),
                            cb.lower(wordLiteral)),
                    cb.like(cb.lower(userTypeModel), cb.lower(wordLiteral))
                )
        );
    }
    q.select(doc).where(
            cb.and(predicates.toArray(new Predicate[predicates.size()]))
    );

    return entityManager.createQuery(q).getResultList();
}
10
beerbajay

Ich habe selbst nach der Lösung gesucht: Die Benennung der "Benutzerdefinierten" Repository-Schnittstelle und deren Implementierung ist sehr streng (wie dort gesagt Wie füge ich eine benutzerdefinierte Methode zu Spring Data JPA hinzu )

Um klar zu sein, der gesamte Code: (Aber @beerbajay hatte recht)

Die benutzerdefinierte Methodenschnittstelle

public interface MyEntityRepositoryCustom {
    List<MyEntity> findSpecial();
}

Die Implementierung der benutzerdefinierten Methode

public class MyEntityRepositoryImpl implements MyEntityRepositoryCustom {
    @PersistenceContext
    private EntityManager em;

    //custom method implementation
    public List<Object> findSpecial() {
        List<Object> list = em.createNativeQuery("select name, value from T_MY_ENTITY").getResultList();
        return list;
    }
}

Das "ursprüngliche" Repository

@Repository
public interface MyEntityRepository extends JpaRepository<MyEntity,Long>, MyEntityRepositoryCustom {
    //original methods here... do not redefine findSpecial()...
}

Sie können das "ursprüngliche" Repository jetzt mit den neuen benutzerdefinierten Methoden verwenden

@Service
public class MyService {
    @Autowired
    private DataRepository r;

    public void doStuff() {
        List<Object> list = r.findSpecial();
    }
}
3
Obscur Moirage

Spring Data JPA bietet eine Möglichkeit, benutzerdefinierte und dynamische Abfragen mit "Spezifikationen" zu erstellen: Spring Data - Spezifikationen

Erstens sollte Ihre Schnittstelle, die JPARepository oder CRUDRepository erweitert, auch JpaSpecificationExecutor<...> implementieren, und das ist alles, was Sie brauchen. Ihr Repository hat jetzt eine neue Methode findAll, die ein Specification<...>-Objekt akzeptiert, und Sie können die Methode Beerbajay verwenden, die zum Überschreiben von Kriterienabfragen verwendet wird die Methode toPredicate(...) und dort können Sie (fast) jede gewünschte Abfrage erstellen:

Specification<...> spec = new Specification<...>() {
   	@Override
   	public Predicate toPredicate(Root<...> entity, CriteriaQuery<?> query, CriteriaBuilder cb) {			    
   	    List<Predicate> conditions = buildManyPredicates(cb, entity);
   	    return cb.and(conditions.toArray(new Predicate[conditions.size()]));
   	}
 };

repository.findAll(spec, PageRequest.of(0, 10));

Dies löst das Problem, dass Spring Data versucht, die Methoden zu analysieren, die Sie in der benutzerdefinierten Schnittstelle hinzugefügt haben (da keine benutzerdefinierte Schnittstelle vorhanden ist).

2
Tom Elias