Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring 'NOT IN' in method naming does not work as it expected

Question

Is it bug, or it just my fail? Can you explain me what is wrong?

Code

I have created simple JPARepository

@Repository
interface UserRepository extends JpaRepository<User, Long> {
    User findByName(String name);


    Collection<User> findByIdNotIn(Collection<Long> users);
}

It looks correct. And it works correct if users is not empty. But otherwise it works incorrect:

result = userRepository.findByIdNotIn([]);

It returns empty result, but it should be equals to result of findAll method call.

userRepository.findByIdNotIn([]).equals(userRepository.findAll());

Also

To check result I have added @Query annotation to method

@Repository
interface UserRepository extends JpaRepository<User, Long> {
    User findByName(String name);

    @Query('SELECT u FROM User u WHERE u.id NOT IN ?1')
    Collection<User> findByIdNotIn(Collection<Long> users);
}

And in this case expected result was correct. Also I have tried write query using native Hibernate CriteriaBuilder

CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<User> query = builder.createQuery(User.class);
Root<User> root = query.from(User.class);

query.where(builder.not(root.get("id").in([])));
result = entityManager.createQuery(query.select(root)).getResultList();

And in this case expected result was correct too.

Additional Info

Result Hibernate queries: Correct result (using @Query annotation):

Hibernate: select user0_.id as id1_7_, user0_.name as name2_7_ from User user0_ where user0_.id not in  ()

Incorrect result(using method naming):

Hibernate: select user0_.id as id1_7_, user0_.name as name2_7_ from User user0_ where user0_.id not in  (?)

My conclusion

It looks like a Spring JPA bug

New Additional Info

I spent a day in debuging spring-data-jpa source code, and I found that the problems occurs in org.springframework.data.jpa.provider.PersistenceProvider in method potentiallyConvertEmptyCollection for HIBERNATE

@Override
        public <T> Collection<T> potentiallyConvertEmptyCollection(Collection<T> collection) {
            return collection == null || collection.isEmpty() ? null : collection;
        }

When collection is empty this function return null value. But I have found, if this value replaced (at runtime) on empty collection again then the final result would be correct!!!

HAVE YOU ANY IDEA ABOUT THIS?!

like image 317
Oleh Dokuka Avatar asked Oct 21 '15 23:10

Oleh Dokuka


1 Answers

From JPA Specification 4.6.9 In Expressions :

There must be at least one element in the comma separated list that defines the set of values for the IN expression. If the value of a state_field_path_expression or in_item in an IN or NOT IN expression is NULL or unknown, the value of the expression is unknown.

So Spring JPA is just following JPA specification, even if, as you said, one could expect that there would be no restriction.

Best to just do the check in your business before calling the right repository method.

like image 126
TheBakker Avatar answered Sep 22 '22 04:09

TheBakker