Skip to content

SimpleJpaRepository.delete(Specification<T> spec) throws NullPointerException #2796

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
xsg22 opened this issue Feb 13, 2023 · 9 comments
Closed
Assignees
Labels
type: bug A general bug

Comments

@xsg22
Copy link

xsg22 commented Feb 13, 2023

Hi,
org.springframework.data.jpa.repository.support.SimpleJpaRepository#delete(Specification) throw s an NullPointerException when I execute in the kotlin language environment. More error messages like this

java.lang.NullPointerException: Parameter specified as non-null is null: method cn.sail.patient.controller.mutaion.ExerciseMutation.deleteExerciseRecord$lambda$0, parameter <anonymous parameter 1>
@Override
public long delete(Specification<T> spec) {

    CriteriaBuilder builder = this.em.getCriteriaBuilder();
    CriteriaDelete<T> delete = builder.createCriteriaDelete(getDomainClass());

    Predicate predicate = spec.toPredicate(delete.from(getDomainClass()), null, builder);
    
    if (predicate != null) {
	    delete.where(predicate);
    }
    
    return this.em.createQuery(delete).executeUpdate();
}

when executing "spec.toPredicate(delete.from(getDomainClass()), null, builder)", the second parameter passed in is null. However, the comment on the "Specification.toPredicate(Root root, CriteriaQuery query, CriteriaBuilder criteriaBuilder)" method clearly states that the ’query‘ parameter must not be null. If 'query' is null, the above exception will appear under kotlin because “CriteriaQuery query” is not a nullable type.

/**
* Creates a WHERE clause for a query of the referenced entity in form of a {@link Predicate} for the given
* {@link Root} and {@link CriteriaQuery}.
*
* @param root must not be {@literal null}.
* @param query must not be {@literal null}.
* @param criteriaBuilder must not be {@literal null}.
* @return a {@link Predicate}, may be {@literal null}.
*/
@Nullable
Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder);

Thanks for any ideas for smooth workarounds or other input,

Leon

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Feb 13, 2023
@mp911de mp911de self-assigned this Feb 20, 2023
@mp911de
Copy link
Member

mp911de commented Feb 20, 2023

We have a problem in that area indeed because the query parameter is declared as non-null and yet we call the specification with null.

@mp911de mp911de changed the title SimpleJpaRepository.delete(Specification<T> spec) throws NullPointerException SimpleJpaRepository.delete(Specification<T> spec) throws NullPointerException Mar 21, 2023
@mp911de mp911de added type: bug A general bug and removed status: waiting-for-triage An issue we've not yet triaged labels Mar 21, 2023
@mp911de mp911de added this to the 3.0.5 (2022.0.5) milestone Mar 21, 2023
@mp911de
Copy link
Member

mp911de commented Mar 21, 2023

In its actual sense, this is not a bug because the documentation says that the specification is expected to be non-null and the argument is not defined as @Nullable. However, several specifications can be null, so we should leniently accept null specifications.

mp911de added a commit that referenced this issue Mar 21, 2023
Add since tag.

See #2796
mp911de added a commit that referenced this issue Mar 21, 2023
Add since tag.

See #2796
klajdipaja pushed a commit to klajdipaja/spring-data-jpa that referenced this issue Mar 24, 2023
klajdipaja pushed a commit to klajdipaja/spring-data-jpa that referenced this issue Mar 24, 2023
@zteaterml
Copy link

zteaterml commented Mar 24, 2023

@mp911de thank you for patching this issue. When do you anticipate a new version will be cut with this fix?

@mp911de
Copy link
Member

mp911de commented Mar 24, 2023

Check out the release calendar for our schedule at https://calendar.spring.io/. The next release will be 3.0.5 with the 2022.0.5 release train.

@felixatmaltego
Copy link

felixatmaltego commented Apr 18, 2023

@mp911de thanks for this fix! I believe that the problem mentioned by @xsg22 still exists.

The problem is that the Specification::toPredicate is being called with null for the query in org/springframework/data/jpa/repository/support/SimpleJpaRepository.java

	@Override
	public long delete(Specification<T> spec) {

		CriteriaBuilder builder = this.em.getCriteriaBuilder();
		CriteriaDelete<T> delete = builder.createCriteriaDelete(getDomainClass());

		Predicate predicate = spec.toPredicate(delete.from(getDomainClass()), null, builder);

		if (predicate != null) {
			delete.where(predicate);
		}

		return this.em.createQuery(delete).executeUpdate();
	}

This happens with 3.0.5 and Kotlin 1.7.22.

Example:

repository.delete(
    Specification.allOf(
        MySpecifications.hasId(id),
        ...
    )
)

I wonder if this is a Kotlin problem as I see no null check. I am happy for any info on this problem!

@mp911de
Copy link
Member

mp911de commented Apr 19, 2023

Specification accepts a CriteriaQuery whereas for deletes, CriteriaBuilder returns a CriteriaDelete. These types aren't assignable so we cannot fix the nullability issue.

@felixatmaltego
Copy link

Thank you for clarifying!

@ghostg00
Copy link

I've had this problem too, so what's the latest solution? thanks

@vasanex
Copy link

vasanex commented Apr 26, 2024

I've just ran into the same issue. The root problem seems to be that the nullability contract of Specification::toPredicate is broken when called from SimpleJpaRepository::delete. Declaring a delete Specification like this:

myRepository.delete { root, _, builder ->
    /* build a predicate */
}

... will throw a NPE in Kotlin because of eager nullability checks that don't exist in pure Java.

However, it's possible to implement a band-aid fix by explicitly providing a nullable type hint for the unused query parameter:

myRepository.delete { root, _: Any?, builder ->
    /* build a predicate */
}

I guess manually providing type hints overrides the nullability contract of Specification::toPredicate, which means the lambda can then be called with a null query parameter.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug A general bug
Projects
None yet
Development

No branches or pull requests

7 participants