How to build dynamic queries with Spring Data Redi

2019-04-12 10:50发布

问题:

I'm testing Redis with spring-data-redis using repositories like this:

public interface CreditCardRepository extends CrudRepository<CreditCard, String>{
    List<CreditCard> findByIssuer(String issuer);
    List<CreditCard> findByCreditNetwork(String creditNetwork);
    List<CreditCard> findByCreditNetworkAndIssuer(String creditNetwork, String issuer);
}

Above methods will query over Redis structures like:

creditcard:creditNetwork:mastercard
creditcard:creditNetwork:visa
creditcard:issuer:company1
creditcard:issuer:company2

Right now my CreditCard object contains two attributes (issuer, network and the id), so it's easy to search objects like this:

private List<CreditCard> searchCardFromCache(CreditCardGetReq req) {
    if (req.getIssuer() != null && req.getNetwork() != null) { 
        return ccRepository.findByIssuerAndCreditNetwork(req.getIssuer(), req.getNetwork().name()); 
    }
    if (req.getIssuer() != null) { 
        return ccRepository.findByIssuer(req.getIssuer()); 
    }
    if (req.getNetwork() != null) { 
        return ccRepository.findByCreditNetwork(req.getNetwork().name()); 
    }
    return null;
}

However, I don't like this code since I will have to create a combination of all the properties and will be very messy. In the future, I plan to have 15 properties so the 'if' chain is not possible.

I would like to ask you how can I create dynamic queries using spring-data-redis, so Redis can return the intersection based on the object properties in a better way than checking each property?

Have tried using MethodHandle by hardcoding (I previously deleted from the repository findByIssuerAndCreditNetwork) a method name that would be dynamic generated like this:

MethodType methodType = MethodType.methodType(cardList.getClass(), String.class, String.class);
// Dynamic create 'findByIssuerAndCreditNetwork'
MethodHandle methodHandle = MethodHandles.lookup().findVirtual(CreditCardRepository.class, "findByIssuerAndCreditNetwork", methodType);

But seems MethodHandle does not work since I got below error:

java.lang.NoSuchMethodException: no such method: com.creditcard.dao.CreditCardRepository.findByIssuerAndCreditNetwork(String,String)ArrayList/invokeInterface

回答1:

Right now, there's no support to create dynamic queries. It sounds a bit as if Query by Example could be the thing you're looking for. Spring Data MongoDB and Spring Data JPA already implement Query by Example.

A query is created by the data store module to match an example domain object:

Person person = new Person();                         
person.setFirstname("Dave");                          

Example<Person> example = Example.of(person); 

MongoRepository repo = …
List<Person> result = repo.findAll(example); // returns all objects that with Dave in firstname

Query by Example is not supported by Spring Data Redis right now but it should be possible to provide basic support.

I created a ticket DATAREDIS-605 to track the progress of this feature.