1

I have query which looks like this and does not work as expected. I have tried quite a few different variations, but I am missing something with the query.

Repository Code:

@Query("SELECT c FROM /customer c, c.emailAddresses email WHERE email.emailAddress = $1")
List<CustomerEntity> findByEmailAddress(String emailAddress);       

Model:

public class CustomerEntity {
   @Id
   protected String id;
   private List<CustomerEmailAddressEntity> emailAddresses;      
}

public class CustomerEmailAddressEntity {
   private Long id;
   private String emailAddress;
 }

The stack that I get looks like this:

Caused by: java.lang.ClassCastException: com.gemstone.gemfire.cache.query.internal.Undefined cannot be cast to com.gemstone.gemfire.cache.query.SelectResults
    at com.gemstone.gemfire.internal.cache.PRQueryProcessor.executeSequentially(PRQueryProcessor.java:330)
    at com.gemstone.gemfire.internal.cache.PRQueryProcessor.executeQuery(PRQueryProcessor.java:127)
    at com.gemstone.gemfire.internal.cache.PartitionedRegionQueryEvaluator.executeQueryOnLocalNode(PartitionedRegionQueryEvaluator.java:1370)
    at com.gemstone.gemfire.internal.cache.PartitionedRegionQueryEvaluator.executeQueryOnRemoteAndLocalNodes(PartitionedRegionQueryEvaluator.java:339)
    at com.gemstone.gemfire.internal.cache.PartitionedRegionQueryEvaluator.queryBuckets(PartitionedRegionQueryEvaluator.java:442)
    at com.gemstone.gemfire.internal.cache.PartitionedRegion.doExecuteQuery(PartitionedRegion.java:1909)
    at com.gemstone.gemfire.internal.cache.PartitionedRegion.executeQuery(PartitionedRegion.java:1829)
    at com.gemstone.gemfire.cache.query.internal.DefaultQuery.execute(DefaultQuery.java:234)
    at com.gemstone.gemfire.cache.query.internal.DefaultQuery.execute(DefaultQuery.java:195)
    at com.gemstone.gemfire.internal.cache.tier.sockets.BaseCommand.processQueryUsingParams(BaseCommand.java:1402)
    at com.gemstone.gemfire.internal.cache.tier.sockets.BaseCommand.processQuery(BaseCommand.java:1347)
    at com.gemstone.gemfire.internal.cache.tier.sockets.command.Query.cmdExecute(Query.java:88)
    at com.gemstone.gemfire.internal.cache.tier.sockets.BaseCommand.execute(BaseCommand.java:174)
    at com.gemstone.gemfire.internal.cache.tier.sockets.ServerConnection.doNormalMsg(ServerConnection.java:809)
    at com.gemstone.gemfire.internal.cache.tier.sockets.ServerConnection.doOneMessage(ServerConnection.java:940)
    at com.gemstone.gemfire.internal.cache.tier.sockets.ServerConnection.run(ServerConnection.java:1189)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at com.gemstone.gemfire.internal.cache.tier.sockets.AcceptorImpl$1$1.run(AcceptorImpl.java:532)
    at java.lang.Thread.run(Thread.java:724)

I am not sure what I am doing wrong. Any help is appreciated.

jww
  • 97,681
  • 90
  • 411
  • 885

2 Answers2

1

Good catch!

I would add that, generally, array and Collection-based (application domain object) properties should never be null. Empty ok, but never null. This can be problematic for iteration (especially with foreach loops leading to rather obscure NPEs, similar to null primitive type Wrapper objects in auto-boxing/unboxing) and as you have seen, GemFire OQL.

Though, I would agree it would have been more informative and helpful if GemFire identified the cause of the Query problem or have been able to handle the null reference.

Interestingly enough, this problem maybe GemFire 7.0.2 specific. I just tested this scenario using both GemFire 7.0.2 and (the latest) GemFire 8.1.0.

With 7.0.2, the following Exception occurred...

org.springframework.dao.InvalidDataAccessApiUsageException: Result object returned from GemfireCallback isn't a SelectResult: [UNDEFINED]
...

But, running with GemFire 8.1.0, the OQL statement (Query) worked as expected, even when the nested Collection was uninitialized on my application domain object...

class Customer ... {
  Set<Address> addresses;
  ...
}

interface CustomerRepository implements GemfireRepository<Customer, Long> {
  @Query("<trace> SELECT DISTINCT c FROM /Customers c, c.addresses a WHERE a.city = $1")
  List<User> findCustomersInCity(String city);

}

Then...

customerRepo.findCustomersInCity("Portland");

I (re-)coded my Customer so that the 'addresses' Collection was only initialized if an address was added and I used a combination of Customers with and without addresses in my test case.

So, even though GemFire 8.x may appear to handle this, I still think it is wise to initialize an object properly.

John Blum
  • 7,381
  • 1
  • 20
  • 30
0

One of the modest architects in our group found the reason for this. It was not a bad query, but a problem with data. Some of the customers had null email addresses and Gemfire OQL was not able to handle the null collection element. It would have been better if GemfireOQL gave me a more useful error message.

So lessons learnt:

Pre-intialize your collections before storing them to gemfire 7.