2015-05-25 1 views
12

Derzeit habe ich folgende Frühling JPA Repository Basis benutzerdefinierte Abfrage wurde mit und es funktioniert gut,Frühling JPA Repository dynamische Abfrage

@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 brauche diese Abfrage zu ändern, wenn Filtertext ein Komma getrennt Wert sein würde. Aber wie folgt wird es eine dynamische Abfrage und wie kann ich es ausführen.

dynamische Abfrage, die ich aufbauen müssen,

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 + "))"; 
} 
+1

verwenden können schauen Sie in 'JpaSpecificationExecutor' –

+1

Stellen Sie eine Implementierung für Ihr DAO bereit und führen Sie diese dynamische Abfrage von der Implementierung aus. http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.single-repository-behaviour. Die DAO sollte nicht aufgeteilt werden. Es sollte eine Liste oder ein Set als Argument nehmen. Der Anrufer sollte sich mit dem Aufteilen befassen. –

Antwort

8

Per JB Nizet und die spring-data documentation, sollten Sie eine benutzerdefinierte Schnittstelle + Repository-Implementierung verwenden.

Eine Schnittstelle mit der Methode:

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

eine Implementierung erstellen:

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

Schließlich fordern die Methode woanders:

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

Abfrage Implementierung

Ihr Verfahren zur Herstellung der sql variable Herstellung, nämlich durch einige Strings verketten in die Abfrage schlecht ist. Tun Sie das nicht.

Die word, die Ihnen eine valid JPQL identifier sein werden verketten müssen, nämlich ein : von einem java identifier start gefolgt gegebenenfalls von einigen java identifier part gefolgt. Wenn Ihre CSV-Datei foo bar,baz enthält, werden Sie versuchen, foo bar als Kennung zu verwenden, und Sie erhalten eine Ausnahme.

Sie können stattdessen CriteriaBuilder verwenden, um die Abfrage in einer sicheren Weise zu konstruieren:

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(); 
} 
+1

Vielen Dank für Ihre Wert volle Antwort. Aufgrund Compiler Ausfall ich folgende Korrekturen anwenden müssen, cb.lower (Wort) -> cb.lower (wordLiteral) q.select (doc) -> q.select (Benutzer) Aber mit diesen Korrekturen Wenn ich diese Lösung implementiere, erhalte ich folgenden Fehler bei der Anwendungsbereitstellung: - verursacht durch: org.springframework.data.mapping.PropertyReferenceException: Es wurde kein Eigenschaftsfilter für den Typ com.ord.model.User – Channa

+0

@Channa Fixed gefunden. Es klingt, als ob spring-data versucht, den Funktionsnamen 'findByFilterText' zu interpretieren, was er nicht tun sollte. Haben Sie Ihr Repository die benutzerdefinierte Repository-Klasse erweitern lassen? – beerbajay

+0

Ja, ich habe die Dinge getan, wie du es erwähnst. Das bedeutet, dass ich die "MyEntityRepositoryCustom" -Schnittstelle erstellt habe und sie später auf die vorhandene Repository-Schnittstelle als "öffentliche Schnittstelle erweitern MyEntityRepository erweitert JpaRepository , MyEntityRepositoryCustom" ausdehnt. – Channa

2

Ich habe für die Lösung suchen mich: Die Namensgebung des „Custom“ Repository-Schnittstelle und implentation ist sehr streng (wie gesagt, es How to add custom method to Spring Data JPA)

Also, klar zu sein, der gesamte Code: (Aber @beerbajay richtig ist)

die benutzerdefinierte Methode Schnittstelle

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

Die benutzerdefinierte Methode Implementierung

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 "Original" Repository

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

Sie jetzt das "Original" Repository mit den neuen benutzerdefinierten Methoden

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

    public void doStuff() { 
     List<Object> list = r.findSpecial(); 
    } 
} 
Verwandte Themen