2015-05-25 27 views
12

Obecnie używam następujący Wiosna JPA Repository baza niestandardowe zapytania i działa dobrze,Wiosna JPA Repository dynamiczny zapytań

@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); 

muszę zmodyfikować ten tekst kwerendy gdy filtr będzie wartość oddzielonych przecinkami. Ale w następujący sposób będzie to zapytanie dynamiczne i jak go wykonać.

Dynamiczne zapytania muszę budować,

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

spójrz na 'JpaSpecificationExecutor' –

+1

Udostępnienie implementacji dla DAO i wykonanie tego kwerendy dynamicznej z implementacji. http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.single-repository-behavour. DAO nie powinien się dzielić. Powinien mieć jako argument List lub Set . Dzwoniący powinien poradzić sobie z dzieleniem. –

Odpowiedz

8

Per JB Nizet i spring-data documentation, należy użyć niestandardowego interfejsu + repozytorium realizację.

Załóż interfejs z metodą:

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

Utwórz realizacji:

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

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

rozszerzyć nowy interfejs w istniejącym interfejsu repozytorium

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

Wreszcie nazywamy metoda gdzie indziej:

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

realizacja zapytań

Twój sposób wytwarzania zmiennej sql, mianowicie poprzez złączenie sznurki do zapytania jest zła. Nie rób tego.

The word którym jest łączenie musi być valid JPQL identifier, mianowicie : następnie java identifier start, po czym ewentualnie jakiegoś java identifier part. Oznacza to, że jeśli Twój plik CSV zawiera foo bar,baz, spróbujesz użyć foo bar jako identyfikatora, a otrzymasz wyjątek.

Można zamiast tego użyć CriteriaBuilder skonstruować kwerendę w sposób bezpieczny:

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

Wielkie dzięki za twoją wartość pełną odpowiedź. Z powodu awarii kompilatora muszę zastosować następujące poprawki, cb.lower (WORD) -> cb.lower (wordLiteral) q.select (doc) -> q.select (użytkownik) Ale z tych poprawek gdy zaimplementuję to rozwiązanie, otrzymam następujący błąd w czasie wdrażania aplikacji: - Powodowany przez: org.springframework.data.mapping.PropertyReferenceException: nie znaleziono filtru właściwości dla typu com.ord.model.User – Channa

+0

@Channa Naprawiono. Wygląda na to, że dane źródłowe próbują interpretować nazwę funkcji 'findByFilterText', której nie powinno robić. Czy Twoje repozytorium rozszerzyło klasę niestandardowego repozytorium? – beerbajay

+0

Tak, zrobiłem to, o czym wspomniałeś. Oznacza to, że utworzyłem interfejs "MyEntityRepositoryCustom", a następnie rozszerzyłem go do istniejącego interfejsu repozytorium, ponieważ "interfejs publiczny MyEntityRepository rozszerza JpaRepository , MyEntityRepositoryCustom" – Channa

2

Szukałem dla roztworu sobie: Nazywanie interfejsu repozytorium „Custom” i implentation jest bardzo surowe (jak powiedział, How to add custom method to Spring Data JPA)

tak więc, aby było jasne, cały kod: (Ale @beerbajay rację)

interfejs sposób niestandardowy

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

Realizacja sposób niestandardowy

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

"oryginał" repozytorium

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

Można teraz korzystać z "oryginalną" repozytorium z nowymi metodami niestandardowymi

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

    public void doStuff() { 
     List<Object> list = r.findSpecial(); 
    } 
} 
Powiązane problemy