问题
I have a java object as below that i store in a redis store.
@RedisHash("UserProfile")
public class UserProfile implements Serializable {
@Id String id;
@Reference PersonalInfo personalInfo = new PersonalInfo();
@Reference BusinessInfo businessInfo = new BusinessInfo();
...
}
Now, the PersonalInfo
object is structured as:
public class PersonalInfo {
private String firstName;
private String lastName;
@Indexed private String userId;
private ContactInfo contactInfo = new ContactInfo();
}
Note that userId in the PersonalInfo
object is indexed.
My goal is to query the UserProfile
objects in redis by their userIds, using spring data.
The CrudRepository
interface will not be able to directly do a findByUserId()
, unless i have userId in the UserProfile object.
I dont want to have the userId directly within the UserProfile object, if there is some better way to write a query to find users by userid.
Any suggestion on how i can go about doing this?
Update: I added the method as below to the repository:
public interface UserProfileRepository extends CrudRepository<UserProfile, String> {
List<UserProfile> findByPersonalInfo_UserId(String userId);
}
But on calling this method in code, i get the below error. Not sure if there is an issue with the redis/spring libraries i am using. I am using
- springboot-starter-parent version: 1.5.2.RELEASE
- spring-data-redis version: 1.8.6.RELEASE
Exception:
java.lang.NoSuchMethodError: org.springframework.data.keyvalue.core.query.KeyValueQuery.getCriteria()Ljava/lang/Object;
at org.springframework.data.redis.repository.query.RedisQueryCreator.complete(RedisQueryCreator.java:101) ~[spring-data-redis-1.8.6.RELEASE.jar:na]
at org.springframework.data.redis.repository.query.RedisQueryCreator.complete(RedisQueryCreator.java:41) ~[spring-data-redis-1.8.6.RELEASE.jar:na]
at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:88) ~[spring-data-commons-1.13.1.RELEASE.jar:na]
at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:73) ~[spring-data-commons-1.13.1.RELEASE.jar:na]
at org.springframework.data.keyvalue.repository.query.KeyValuePartTreeQuery.createQuery(KeyValuePartTreeQuery.java:184) ~[spring-data-keyvalue-1.2.1.RELEASE.jar:na]
at org.springframework.data.keyvalue.repository.query.KeyValuePartTreeQuery.prepareQuery(KeyValuePartTreeQuery.java:128) ~[spring-data-keyvalue-1.2.1.RELEASE.jar:na]
at org.springframework.data.keyvalue.repository.query.KeyValuePartTreeQuery.execute(KeyValuePartTreeQuery.java:87) ~[spring-data-keyvalue-1.2.1.RELEASE.jar:na]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:483) ~[spring-data-commons-1.13.1.RELEASE.jar:na]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:461) ~[spring-data-commons-1.13.1.RELEASE.jar:na]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.7.RELEASE.jar:4.3.7.RELEASE]
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:61) ~[spring-data-commons-1.13.1.RELEASE.jar:na]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.7.RELEASE.jar:4.3.7.RELEASE]
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) ~[spring-aop-4.3.7.RELEASE.jar:4.3.7.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.7.RELEASE.jar:4.3.7.RELEASE]
at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:57) ~[spring-data-commons-1.13.1.RELEASE.jar:na]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.7.RELEASE.jar:4.3.7.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) ~[spring-aop-4.3.7.RELEASE.jar:4.3.7.RELEASE]
at com.sun.proxy.$Proxy66.findByPersonalInfo_UserId(Unknown Source) ~[na:na]
Update 2: I was able to fix the above exception by changing the springboot version to 1.5.6.RELEASE.
However, now the issue i am facing is, when i query by userid, it doesnt give me the UserProfiles, even though there is a match.
For example, redis-cli shows:
KEYS *
UserProfile:d0876c1f-5684-4ee4-acbb-7a92b7fa25ae
UserProfile:3591e476-29d7-4c3c-a7e5-6272231f96e0
UserProfile:3591e476-29d7-4c3c-a7e5-6272231f96e0:idx
UserProfile:d0876c1f-5684-4ee4-acbb-7a92b7fa25ae:idx
UserProfile:51814a77-bf40-4912-b700-cfa50d1c4b25
UserProfile:personalInfo.userId:adrian.tremblay
UserProfile:66ba8276-1bb0-47a0-a54d-4c9d99b8bf80
UserProfile:66ba8276-1bb0-47a0-a54d-4c9d99b8bf80:idx
UserProfile:personalInfo.userId:anthony.turner
UserProfile:personalInfo.userId:ashleigh.hayes
UserProfile:a81356b0-27ef-4a34-92a3-629be5114f0e
UserProfile
When i do a findAll, findById, it gives me results back. When I do a findByPersonalInfo_UserId(), and pass values like: anthony.turner or ashleigh.hayes, nothing shows up. Am i giving the userId in some incorrect format? I tried a few different things, but didnt work.
Update 3: I also tried removing the @Reference in the UserProfile object (so that the redis objects get flattened out, instead of pointing to references). But this didn't help either.
回答1:
Try to name your method without any underscore like:
List<UserProfile> findByPersonalInfoUserId(String userId);
Had similar case and it worked for me. Also don't need @Reference
annotation.
Using spring-boot-starter-parent 2.0.3.RELEASE
回答2:
I faced the same situation,
Basically if you have a reference then Redis internally stores the Id for the reference, which cannot be directly queried as it need to fetch the whole Object to match, yep its the drawback for Redis(or any Hashbased/Non-relational database).
You may ask why not possible, technically it can be done but it will be processor and memory intensive operation which will ultimately hit performance, and Redis is known for its performance.
@Reference
should only be used only and only when you wish to share a single object (here PersonalInfo) among multiple Objects (here UserInfo).
Possible solutions if you want to use @Reference
(assuming UserProfile's 'id' not equal to PersonalInfo's userId ):
My personal solution at this answer's end :
(Not Recommended - slow performance) - first
findAllUserProfile()
then go through eachPersonalInfo
present inUserProfile
and match desired field (hereuserId
)(Recommended) - Restructuring your schema, ask me why, because if you wish to keep references for more than just reading (i mean for querying too) then non-relational database is not one for you in this situation. Restructure to make it fit in non-relational structure if possible.
Note: solution by @Aurimas above is good and suits your situation best, if you won't put reference then it will create a new object which will be stored in UserProfile
object but any changes in PersonalInfo
will be specific to this UserProfile
and won't reflect in other UserProfile
objects. But it's advantage is it will be query-able :-)
So as per you situation I will recommend to ditch the @Reference
for PersonalInfo
as logically it should not be shared (i.e Personal ;-) ), and go ahead with @Aurimas solution.
来源:https://stackoverflow.com/questions/45419196/query-nested-objects-in-redis-using-spring-data